diff options
Diffstat (limited to 'src/qml')
318 files changed, 17365 insertions, 31240 deletions
diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri new file mode 100644 index 0000000000..bcc3ea0fa0 --- /dev/null +++ b/src/qml/common/common.pri @@ -0,0 +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/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/qqmlapiversion_p.h b/src/qml/common/qqmlapiversion_p.h new file mode 100644 index 0000000000..eca05558d8 --- /dev/null +++ b/src/qml/common/qqmlapiversion_p.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** 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 QQMLAPIVERSION_P_H +#define QQMLAPIVERSION_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. +// + +#define Q_QML_PRIVATE_API_VERSION 5 + +#endif // QQMLAPIVERSION_P_H diff --git a/src/qml/common/qqmljsdiagnosticmessage_p.h b/src/qml/common/qqmljsdiagnosticmessage_p.h new file mode 100644 index 0000000000..763332ba76 --- /dev/null +++ b/src/qml/common/qqmljsdiagnosticmessage_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 QQMLJSDIAGNOSTICMESSAGE_P_H +#define QQMLJSDIAGNOSTICMESSAGE_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/qlogging.h> +#include <QtCore/qstring.h> + +// Include the API version here, to avoid complications when querying it for the +// QQmlSourceLocation -> line/column change. +#include <private/qqmlapiversion_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +struct DiagnosticMessage +{ + QString message; + QtMsgType type = QtCriticalMsg; + quint32 line = 0; + quint32 column = 0; + + bool isError() const + { + return type == QtCriticalMsg; + } + + bool isWarning() const + { + return type == QtWarningMsg; + } + + bool isValid() const + { + return !message.isEmpty(); + } +}; +} // namespace QQmlJS + +Q_DECLARE_TYPEINFO(QQmlJS::DiagnosticMessage, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QQMLJSDIAGNOSTICMESSAGE_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/jsruntime/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h index 65c3e4d65a..65c3e4d65a 100644 --- a/src/qml/jsruntime/qv4alloca_p.h +++ b/src/qml/common/qv4alloca_p.h diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/common/qv4calldata_p.h index d9abfc071e..5a5280cb86 100644 --- a/src/qml/jit/qv4jithelpers_p.h +++ b/src/qml/common/qv4calldata_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef TEMPLATE_H -#define TEMPLATE_H +#ifndef QV4CALLDATA_P_H +#define QV4CALLDATA_P_H // // W A R N I N G @@ -51,42 +51,73 @@ // We mean it. // -#include <private/qv4global_p.h> - -//QT_REQUIRE_CONFIG(qml_jit); +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -#ifdef V4_ENABLE_JIT +struct CallData +{ + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + NewTarget = 4, + Argc = 5, + + LastOffset = Argc, + OffsetCount = LastOffset + 1 + }; + + StaticValue function; + StaticValue context; + StaticValue accumulator; + StaticValue thisObject; + StaticValue newTarget; + StaticValue _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + + inline ReturnedValue argument(int i) const { + return i < argc() ? args[i].asReturnedValue() + : StaticValue::undefinedValue().asReturnedValue(); + } + + StaticValue args[1]; -namespace JIT { -namespace Helpers { + static Q_DECL_CONSTEXPR int HeaderSize() + { + return offsetof(CallData, args) / sizeof(QV4::StaticValue); + } -void convertThisToObject(ExecutionEngine *engine, Value *t); -ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index); -ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index); -ReturnedValue toObject(ExecutionEngine *engine, const Value &obj); -ReturnedValue exp(const Value &base, const Value &exp); -ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index); -void setLookupStrict(Function *f, int index, Value &base, const Value &value); -void setLookupSloppy(Function *f, int index, Value &base, const Value &value); -void pushBlockContext(Value *stack, int index); -void cloneBlockContext(Value *contextSlot); -void pushScriptContext(Value *stack, ExecutionEngine *engine, int index); -void popScriptContext(Value *stack, ExecutionEngine *engine); -ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index); -ReturnedValue deleteName(Function *function, int name); -void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v); + template<typename Value> + Value *argValues(); -} // Helpers namespace -} // JIT namespace + template<typename Value> + const Value *argValues() const; +}; -#endif // V4_ENABLE_JIT +Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); +Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(StaticValue)); -} // QV4 namespace +} // namespace QV4 QT_END_NAMESPACE -#endif // TEMPLATE_H +#endif // QV4CALLDATA_P_H diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 9050cfba94..c3ddce5884 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -50,36 +50,35 @@ // We mean it. // +#include <functional> + #include <QtCore/qstring.h> -#include <QVector> -#include <QStringList> -#include <QHash> -#include <QUrl> - -#include <private/qv4value_p.h> -#include <private/qv4executableallocator_p.h> -#include <private/qqmlrefcount_p.h> -#include <private/qqmlnullablevalue_p.h> -#include <private/qv4identifier_p.h> -#include <private/qflagpointer_p.h> -#include <private/qendian_p.h> -#include <private/qqmljsastfwd_p.h> -#ifndef V4_BOOTSTRAP -#include <private/qqmltypenamecache_p.h> -#include <private/qqmlpropertycache_p.h> -#include "private/qintrusivelist_p.h" +#include <QtCore/qscopeguard.h> +#include <QtCore/qvector.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qhash.h> + +#if QT_CONFIG(temporaryfile) +#include <QtCore/qsavefile.h> #endif +#include <private/qendian_p.h> +#include <private/qv4staticvalue_p.h> +#include <functional> + QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x21 +// +// IMPORTANT: +// +// 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 0x24 // Collect function parameter types class QIODevice; -class QQmlPropertyCache; -class QQmlPropertyData; class QQmlTypeNameCache; -class QQmlScriptData; class QQmlType; class QQmlEngine; @@ -88,14 +87,14 @@ struct Document; } namespace QV4 { - namespace Heap { struct Module; +struct String; +struct InternalClass; }; struct Function; class EvalISelFactory; -class CompilationUnitMapper; namespace CompiledData { @@ -128,8 +127,6 @@ struct Location Location() : _dummy(0) { } - Location &operator=(const QQmlJS::AST::SourceLocation &astLocation); - inline bool operator<(const Location &other) const { return line < other.line || (line == other.line && column < other.column); @@ -259,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 @@ -277,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; @@ -291,10 +312,6 @@ struct Function quint32_le nLabelInfos; size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - typedef quint16_le TraceInfoCount; - TraceInfoCount nTraceInfos; - static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } - // Keep all unaligned data at the end quint8 flags; quint8 padding1; @@ -302,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()); } @@ -316,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); @@ -327,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 { @@ -415,7 +432,7 @@ static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have th // Qml data structures -struct Q_QML_EXPORT TranslationData +struct TranslationData { quint32_le stringIndex; quint32_le commentIndex; @@ -424,7 +441,7 @@ struct Q_QML_EXPORT TranslationData }; static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); -struct Q_QML_PRIVATE_EXPORT Binding +struct Binding { quint32_le propertyNameIndex; @@ -521,22 +538,52 @@ struct Q_QML_PRIVATE_EXPORT Binding bool isFunctionExpression() const { return (flags & IsFunctionExpression); } - static QString escapedString(const QString &string); + //reverse of Lexer::singleEscape() + static QString escapedString(const QString &string) + { + QString tmp = QLatin1String("\""); + for (int i = 0; i < string.length(); ++i) { + const QChar &c = string.at(i); + switch (c.unicode()) { + case 0x08: + tmp += QLatin1String("\\b"); + break; + case 0x09: + tmp += QLatin1String("\\t"); + break; + case 0x0A: + tmp += QLatin1String("\\n"); + break; + case 0x0B: + tmp += QLatin1String("\\v"); + break; + case 0x0C: + tmp += QLatin1String("\\f"); + break; + case 0x0D: + tmp += QLatin1String("\\r"); + break; + case 0x22: + tmp += QLatin1String("\\\""); + break; + case 0x27: + tmp += QLatin1String("\\\'"); + break; + case 0x5C: + tmp += QLatin1String("\\\\"); + break; + default: + tmp += c; + break; + } + } + tmp += QLatin1Char('\"'); + return tmp; + } bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } -#ifndef V4_BOOTSTRAP - QString valueAsString(const CompilationUnit *unit) const; - QString valueAsScriptString(const CompilationUnit *unit) const; -#endif - double valueAsNumber(const Value *constantTable) const - { - if (type != Type_Number) - return 0.0; - return constantTable[value.constantValueIndex].doubleValue(); - } - bool valueAsBoolean() const { if (type == Type_Boolean) @@ -580,15 +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"); -struct Parameter -{ - quint32_le nameIndex; - quint32_le type; - quint32_le customTypeNameIndex; - Location location; -}; -static_assert(sizeof(Parameter) == 16, "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; @@ -616,24 +654,33 @@ static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected struct Property { - enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color, - Font, Time, Date, DateTime, Rect, Point, Size, - Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, - Custom, CustomList }; - - enum Flags : unsigned int { - IsReadOnly = 0x1 - }; - quint32_le nameIndex; union { - quint32_le_bitfield<0, 31> type; - quint32_le_bitfield<31, 1> flags; // readonly + quint32_le_bitfield<0, 29> builtinTypeOrTypeNameIndex; + quint32_le_bitfield<29, 1> isBuiltinType; + quint32_le_bitfield<30, 1> isList; + quint32_le_bitfield<31, 1> isReadOnly; }; - quint32_le customTypeNameIndex; // If type >= Custom + Location location; + + void setBuiltinType(BuiltinType t) + { + builtinTypeOrTypeNameIndex = static_cast<quint32>(t); + isBuiltinType = true; + } + BuiltinType builtinType() const { + if (isBuiltinType) + return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex)); + return BuiltinType::InvalidBuiltin; + } + void setCustomType(int nameIndex) + { + builtinTypeOrTypeNameIndex = nameIndex; + isBuiltinType = false; + } }; -static_assert(sizeof(Property) == 16, "Property 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(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Alias { enum Flags : unsigned int { @@ -830,7 +877,6 @@ static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expect enum { QmlCompileHashSpace = 48 }; static const char magic_str[] = "qv4cdata"; -extern const char qml_compile_hash[QmlCompileHashSpace + 1]; struct Unit { @@ -845,8 +891,6 @@ struct Unit char libraryVersionHash[QmlCompileHashSpace]; char md5Checksum[16]; // checksum of all bytes following this field. - void generateChecksum(); - char dependencyMD5Checksum[16]; enum : unsigned int { @@ -894,8 +938,6 @@ struct Unit quint32_le offsetToQmlUnit; - bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const; - /* QML specific fields */ const QmlUnit *qmlUnit() const { @@ -1029,8 +1071,8 @@ struct TypeReferenceMap : QHash<int, TypeReference> auto prop = obj->propertiesBegin(); auto propEnd = obj->propertiesEnd(); for ( ; prop != propEnd; ++prop) { - if (prop->type >= QV4::CompiledData::Property::Custom) { - TypeReference &r = this->add(prop->customTypeNameIndex, prop->location); + if (!prop->isBuiltinType) { + TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location); r.errorWhenNotFound = true; } } @@ -1051,142 +1093,144 @@ struct TypeReferenceMap : QHash<int, TypeReference> } }; -#ifndef V4_BOOTSTRAP -struct ResolvedTypeReference; -// map from name index -// 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. -struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> +using DependentTypesHasher = std::function<QByteArray()>; + +// This is how this hooks into the existing structures: + +struct CompilationUnitBase { - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; -}; + Q_DISABLE_COPY(CompilationUnitBase) -using DependentTypesHasher = std::function<bool(QCryptographicHash *)>; -#else -struct DependentTypesHasher {}; -#endif + CompilationUnitBase() = default; + ~CompilationUnitBase() = default; -// index is per-object binding index -typedef QVector<QQmlPropertyData*> BindingPropertyData; + CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); } -// This is how this hooks into the existing structures: + CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept + { + if (this != &other) { + runtimeStrings = other.runtimeStrings; + other.runtimeStrings = nullptr; + constants = other.constants; + other.constants = nullptr; + runtimeRegularExpressions = other.runtimeRegularExpressions; + other.runtimeRegularExpressions = nullptr; + runtimeClasses = other.runtimeClasses; + other.runtimeClasses = nullptr; + imports = other.imports; + other.imports = nullptr; + } + return *this; + } -struct Q_QML_PRIVATE_EXPORT CompilationUnitBase -{ // pointers either to data->constants() or little-endian memory copy. - QV4::Heap::String **runtimeStrings = nullptr; // Array - const Value* constants = nullptr; - QV4::Value *runtimeRegularExpressions = nullptr; - QV4::Heap::InternalClass **runtimeClasses = nullptr; - const Value** imports = nullptr; + Heap::String **runtimeStrings = nullptr; // Array + const StaticValue* constants = nullptr; + QV4::StaticValue *runtimeRegularExpressions = nullptr; + Heap::InternalClass **runtimeClasses = nullptr; + const StaticValue** imports = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *)); -Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *)); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *)); -struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase +struct CompilationUnit : public CompilationUnitBase { + Q_DISABLE_COPY(CompilationUnit) + const Unit *data = nullptr; const QmlUnit *qmlData = nullptr; + QStringList dynamicStrings; public: - CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString()); - ~CompilationUnit(); + using CompiledObject = CompiledData::Object; - void addref() + CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), + const QString &finalUrlString = QString()) { - Q_ASSERT(refCount.load() > 0); - refCount.ref(); + setUnitData(unitData, nullptr, fileName, finalUrlString); } - void release() - { - Q_ASSERT(refCount.load() > 0); - if (!refCount.deref()) - destroy(); - } - int count() const + ~CompilationUnit() { - return refCount.load(); - } - - const Unit *unitData() const { return data; } - void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr, - const QString &fileName = QString(), const QString &finalUrlString = QString()); + if (data) { + if (data->qmlUnit() != qmlData) + free(const_cast<QmlUnit *>(qmlData)); + qmlData = nullptr; -#ifndef V4_BOOTSTRAP - QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine = nullptr; - QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. + if (!(data->flags & QV4::CompiledData::Unit::StaticData)) + free(const_cast<Unit *>(data)); + } + data = nullptr; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + delete [] constants; + constants = nullptr; +#endif - // url() and fileName() shall be used to load the actual QML/JS code or to show errors or - // warnings about that code. They include any potential URL interceptions and thus represent the - // "physical" location of the code. - // - // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code - // They are _not_ intercepted and thus represent the "logical" name for the code. + delete [] imports; + imports = nullptr; + } - QString fileName() const { return m_fileName; } - QString finalUrlString() const { return m_finalUrlString; } - QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QUrl finalUrl() const + CompilationUnit(CompilationUnit &&other) noexcept { - if (m_finalUrl.isNull) - m_finalUrl = QUrl(finalUrlString()); - return m_finalUrl; + *this = std::move(other); } - QV4::Lookup *runtimeLookups = nullptr; - QVector<QV4::Function *> runtimeFunctions; - QVector<QV4::Heap::InternalClass *> runtimeBlocks; - mutable QVector<QV4::Heap::Object *> templateObjects; - mutable QQmlNullableValue<QUrl> m_url; - mutable QQmlNullableValue<QUrl> m_finalUrl; - - // QML specific fields - QQmlPropertyCacheVector propertyCaches; - QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - - // index is object index. This allows fast access to the - // property data when initializing bindings, avoiding expensive - // lookups by string (property name). - 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 - QHash<int, IdentifierHash> namedObjectsPerComponentCache; - inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated - - QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; - ResolvedTypeReferenceMap resolvedTypes; - ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } + CompilationUnit &operator=(CompilationUnit &&other) noexcept + { + if (this != &other) { + data = other.data; + other.data = nullptr; + qmlData = other.qmlData; + other.qmlData = nullptr; + dynamicStrings = std::move(other.dynamicStrings); + other.dynamicStrings.clear(); + m_fileName = std::move(other.m_fileName); + other.m_fileName.clear(); + m_finalUrlString = std::move(other.m_finalUrlString); + other.m_finalUrlString.clear(); + m_module = other.m_module; + other.m_module = nullptr; + CompilationUnitBase::operator=(std::move(other)); + } + return *this; + } - bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; + const Unit *unitData() const { return data; } - int metaTypeId = -1; - int listMetaTypeId = -1; - bool isRegisteredWithEngine = false; + void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr, + const QString &fileName = QString(), const QString &finalUrlString = QString()) + { + data = unitData; + qmlData = nullptr; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + delete [] constants; +#endif + constants = nullptr; + m_fileName.clear(); + m_finalUrlString.clear(); + if (!data) + return; + + qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize]; + const quint64_le *littleEndianConstants = data->constants(); + for (uint i = 0; i < data->constantTableSize; ++i) + bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]); + constants = bigEndianConstants; +#else + constants = reinterpret_cast<const StaticValue*>(data->constants()); +#endif - QScopedPointer<CompilationUnitMapper> backingFile; - QStringList dynamicStrings; + m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); + m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); + } - // --- interface for QQmlPropertyCacheCreator - typedef Object CompiledObject; - int objectCount() const { return qmlData->nObjects; } - const Object *objectAt(int index) const { return qmlData->objectAt(index); } - int importCount() const { return qmlData->nImports; } - const Import *importAt(int index) const { return qmlData->importAt(index); } QString stringAt(int index) const { if (uint(index) >= data->stringTableSize) @@ -1194,117 +1238,84 @@ public: return data->stringAtInternal(index); } - Heap::Object *templateObjectAt(int index) const; - - struct FunctionIterator - { - FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {} - const Unit *unit; - const Object *object; - int index; - - const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); } - void operator++() { ++index; } - bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } - bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } - }; - FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); } - FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); } - // --- - - bool isESModule() const { return data->flags & Unit::IsESModule; } - bool isSharedLibrary() const { return data->flags & Unit::IsSharedLibrary; } - QStringList moduleRequests() const; - Heap::Module *instantiate(ExecutionEngine *engine); - const Value *resolveExport(QV4::String *exportName); - QStringList exportedNames() const; - void evaluate(); - void evaluateModuleRequests(); - - QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); - void unlink(); - - void markObjects(MarkStack *markStack); - - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); - - static QString localCacheFilePath(const QUrl &url); - -protected: - quint32 totalStringCount() const - { return data->stringTableSize; } + QString fileName() const { return m_fileName; } + QString finalUrlString() const { return m_finalUrlString; } -#else // V4_BOOTSTRAP - QString stringAt(int index) const { return data->stringAtInternal(index); } -#endif // V4_BOOTSTRAP + Heap::Module *module() const { return m_module; } + void setModule(Heap::Module *module) { m_module = module; } private: - void destroy(); - - struct ResolveSetEntry - { - ResolveSetEntry() {} - ResolveSetEntry(CompilationUnit *module, QV4::String *exportName) - : module(module), exportName(exportName) {} - CompilationUnit *module = nullptr; - QV4::String *exportName = nullptr; - }; - - const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet); - const ExportEntry *lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const; - void getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit *> *exportNameSet, bool includeDefaultExport = true) const; - QString m_fileName; // initialized from data->sourceFileIndex QString m_finalUrlString; // initialized from data->finalUrlIndex - QAtomicInt refCount = 1; - - Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); - Heap::Module *m_module = nullptr; +}; +class SaveableUnitPointer +{ + Q_DISABLE_COPY_MOVE(SaveableUnitPointer) public: -#if defined(V4_BOOTSTRAP) - bool saveToDisk(const QString &outputFileName, QString *errorString); + SaveableUnitPointer(const Unit *unit, quint32 temporaryFlags = Unit::StaticData) : + unit(unit), + temporaryFlags(temporaryFlags) + { + } + + ~SaveableUnitPointer() = default; + + template<typename Char> + bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const + { + auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; }); + mutableFlags() |= temporaryFlags; + return writer(data<Char>(), size()); + } + + static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size, + QString *errorString) + { +#if QT_CONFIG(temporaryfile) + QSaveFile cacheFile(outputFileName); + if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate) + || cacheFile.write(data, size) != size + || !cacheFile.commit()) { + *errorString = cacheFile.errorString(); + return false; + } + + errorString->clear(); + return true; #else - bool saveToDisk(const QUrl &unitUrl, QString *errorString); + Q_UNUSED(outputFileName) + *errorString = QStringLiteral("features.temporaryfile is disabled."); + return false; #endif -}; - -#ifndef V4_BOOTSTRAP -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} + } - QQmlType type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; +private: + const Unit *unit; + quint32 temporaryFlags; - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; + quint32_le &mutableFlags() const + { + return const_cast<Unit *>(unit)->flags; + } - QQmlRefPointer<QQmlPropertyCache> propertyCache() const; - QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + template<typename Char> + const Char *data() const + { + Q_STATIC_ASSERT(sizeof(Char) == 1); + const Char *dataPtr; + memcpy(&dataPtr, &unit, sizeof(dataPtr)); + return dataPtr; + } - void doDynamicTypeCheck(); + quint32 size() const + { + return unit->unitSize; + } }; -IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) -{ - auto it = namedObjectsPerComponentCache.find(componentObjectIndex); - if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) - return createNamedObjectsPerComponent(componentObjectIndex); - return *it; -} -#endif // V4_BOOTSTRAP } // CompiledData namespace } // QV4 namespace diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h new file mode 100644 index 0000000000..0716f7ea20 --- /dev/null +++ b/src/qml/common/qv4staticvalue_p.h @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** 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 QV4STATICVALUE_P_H +#define QV4STATICVALUE_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/qnumeric_p.h> +#include <cstring> + +#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; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +struct StaticValue +{ + StaticValue() = default; + constexpr StaticValue(quint64 val) : _val(val) {} + + StaticValue &operator=(ReturnedValue v) + { + _val = v; + return *this; + } + + template<typename Value> + StaticValue &operator=(const Value &); + + template<typename Value> + const Value &asValue() const; + + template<typename Value> + Value &asValue(); + + /* + We use 8 bytes for a value and a different variant of NaN boxing. A Double + NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a + signalling NaN it is the top 14 bits. The other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for + managed objects, and encode the other types using the free space given to use by the unused + bits for NaN values. This also works for pointers on 64 bit systems, as they all currently + only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for + pointers.) + + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between + managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave + the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is + set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are + used to encode Null/Int/Bool. + + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. + + Specific bit-sequences: + 0 = always 0 + 1 = always 1 + x = stored value + a,b,c,d = specific bit values, see notes + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - a: xor-ed signbit, always 1 for NaN + - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value + - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 + - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ + and JS + - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 + - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, + so: (val >> (64-15)) == 1 + - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where + any non double results in a NaN + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. + */ + + quint64 _val; + + 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; } + static inline int tagOffset() { return 4; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } +#endif + static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | 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()); } + + QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const + { + return int(value()); + } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) + { + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); + } + QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } + + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() + { + setTagValue(quint32(ValueTypeInternal::Empty), 0); + } + + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + + enum Type { + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 + }; + + inline Type type() const { + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); + } + + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ull; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 + }; + + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u + }; + + // Used only by 32-bit encoding + enum Masks { + SilentNaNBit = 0x00040000, + NotDouble_Mask = 0x7ffa0000, + }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u + }; + + enum { + Managed_Type_Internal = 0 + }; + + using ValueTypeInternal = ValueTypeInternal_64; + + enum { + NaN_Mask = 0x7ff80000, + }; + + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + + // used internally in property + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } + inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } + inline bool isNumber() const { return quickType() >= QT_Int; } + + inline bool isUndefined() const { return _val == 0; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isManaged() const + { +#if QT_POINTER_SIZE == 4 + return value() && tag() == Managed_Type_Internal; +#else + return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + inline bool isManagedOrUndefined() const + { +#if QT_POINTER_SIZE == 4 + return tag() == Managed_Type_Internal; +#else + return ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + + inline bool integerCompatible() const { + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; + } + + static inline bool integerCompatible(StaticValue a, StaticValue b) { + return a.integerCompatible() && b.integerCompatible(); + } + + static inline bool bothDouble(StaticValue a, StaticValue b) { + return a.isDouble() && b.isDouble(); + } + + inline bool isNaN() const + { + return (tag() & 0x7ffc0000 ) == 0x00040000; + } + + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + + QV4_NEARLY_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + StaticValue v = *this; + v._val ^= NaNEncodeMask; + memcpy(&d, &v._val, 8); + return d; + } + + QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) { + if (qt_is_nan(d)) + d = qt_qnan(); + memcpy(&_val, &d, 8); + _val ^= NaNEncodeMask; + Q_ASSERT(isDouble()); + } + + inline bool isInt32() { + if (tag() == quint32(ValueTypeInternal::Integer)) + return true; + if (isDouble()) { + double d = doubleValue(); + if (isInt32(d)) { + setInt_32(int(d)); + return true; + } + } + return false; + } + + QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } + + double asDouble() const { + if (tag() == quint32(ValueTypeInternal::Integer)) + return int_32(); + return doubleValue(); + } + + bool booleanValue() const { + return int_32(); + } + + int integerValue() const { + return int_32(); + } + + inline bool tryIntegerConversion() { + bool b = integerCompatible(); + if (b) + setTagValue(quint32(ValueTypeInternal::Integer), value()); + return b; + } + + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + if (isManagedOrUndefined()) + return false; + + // double + const double d = doubleValue(); + return d && !std::isnan(d); + } + + inline int toInt32() const + { + switch (type()) { + case Null_Type: + case Boolean_Type: + case Integer_Type: + return int_32(); + case Double_Type: + return Double::toInt32(doubleValue()); + case Empty_Type: + case Undefined_Type: + case Managed_Type: + break; + } + return Double::toInt32(std::numeric_limits<double>::quiet_NaN()); + } + + ReturnedValue *data_ptr() { return &_val; } + constexpr ReturnedValue asReturnedValue() const { return _val; } + constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; } + + inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } + static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } + static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } + inline static constexpr StaticValue undefinedValue() { return { 0 }; } + static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } + + static inline StaticValue fromDouble(double d) + { + StaticValue v; + v.setDouble(d); + return v; + } + + static inline StaticValue fromUInt32(uint i) + { + StaticValue v; + if (i < uint(std::numeric_limits<int>::max())) { + v.setTagValue(quint32(ValueTypeInternal::Integer), i); + } else { + v.setDouble(i); + } + return v; + } + + static double toInteger(double d) + { + if (std::isnan(d)) + return +0; + if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); + } + + static int toInt32(double d) + { + return Double::toInt32(d); + } + + static unsigned int toUInt32(double d) + { + return static_cast<uint>(toInt32(d)); + } +}; +Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value); + +struct Encode { + static constexpr ReturnedValue undefined() { + return StaticValue::undefinedValue().asReturnedValue(); + } + static constexpr ReturnedValue null() { + return StaticValue::nullValue().asReturnedValue(); + } + + explicit constexpr Encode(bool b) + : val(StaticValue::fromBoolean(b).asReturnedValue()) + { + } + explicit Encode(double d) { + val = StaticValue::fromDouble(d).asReturnedValue(); + } + explicit constexpr Encode(int i) + : val(StaticValue::fromInt32(i).asReturnedValue()) + { + } + explicit Encode(uint i) { + val = StaticValue::fromUInt32(i).asReturnedValue(); + } + explicit constexpr Encode(ReturnedValue v) + : val(v) + { + } + constexpr Encode(StaticValue v) + : val(v.asReturnedValue()) + { + } + + template<typename HeapBase> + explicit Encode(HeapBase *o); + + explicit Encode(StaticValue *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (StaticValue::isInt32(d)) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + + constexpr operator ReturnedValue() const { + return val; + } + quint64 val; +private: + explicit Encode(void *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4STATICVALUE_P_H diff --git a/src/qml/common/qv4stringtoarrayindex_p.h b/src/qml/common/qv4stringtoarrayindex_p.h new file mode 100644 index 0000000000..61bd988d1e --- /dev/null +++ b/src/qml/common/qv4stringtoarrayindex_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QV4STRINGTOARRAYINDEX_P_H +#define QV4STRINGTOARRAYINDEX_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/qnumeric_p.h> +#include <QtCore/qstring.h> +#include <limits> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +inline uint charToUInt(const QChar *ch) { return ch->unicode(); } +inline uint charToUInt(const char *ch) { return static_cast<unsigned char>(*ch); } + +template <typename T> +uint stringToArrayIndex(const T *ch, const T *end) +{ + uint i = charToUInt(ch) - '0'; + if (i > 9) + return std::numeric_limits<uint>::max(); + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return std::numeric_limits<uint>::max(); + + while (ch < end) { + uint x = charToUInt(ch) - '0'; + if (x > 9) + return std::numeric_limits<uint>::max(); + if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x + return std::numeric_limits<uint>::max(); + ++ch; + } + return i; +} + +inline uint stringToArrayIndex(const QString &str) +{ + return stringToArrayIndex(str.constData(), str.constData() + str.length()); +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4STRINGTOARRAYINDEX_P_H diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index da3c173545..4d6926d420 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -3,20 +3,19 @@ INCLUDEPATH += $$OUT_PWD HEADERS += \ $$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/qqmltypecompiler_p.h \ $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4bytecodehandler_p.h + $$PWD/qv4bytecodehandler_p.h \ + $$PWD/qv4util_p.h SOURCES += \ $$PWD/qv4bytecodegenerator.cpp \ - $$PWD/qv4compileddata.cpp \ $$PWD/qv4compiler.cpp \ $$PWD/qv4compilercontext.cpp \ $$PWD/qv4compilerscanfunctions.cpp \ @@ -25,25 +24,6 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4bytecodehandler.cpp -!qmldevtools_build { - -HEADERS += \ - $$PWD/qqmltypecompiler_p.h \ - $$PWD/qqmlpropertycachecreator_p.h \ - $$PWD/qqmlpropertyvalidator_p.h \ - $$PWD/qv4compilationunitmapper_p.h - - -SOURCES += \ - $$PWD/qqmltypecompiler.cpp \ - $$PWD/qqmlpropertycachecreator.cpp \ - $$PWD/qqmlpropertyvalidator.cpp \ - $$PWD/qv4compilationunitmapper.cpp - -unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp -else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -} - gcc { equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 9218c4a652..665d2633a7 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -39,7 +39,7 @@ #include "qqmlirbuilder_p.h" -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> @@ -48,12 +48,6 @@ #include <QCryptographicHash> #include <cmath> -#ifndef V4_BOOTSTRAP -#include <private/qqmlglobal_p.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlengine_p.h> -#endif - #ifdef CONST #undef CONST #endif @@ -61,12 +55,8 @@ QT_USE_NAMESPACE static const quint32 emptyStringIndex = 0; - -#if 0 //ndef V4_BOOTSTRAP -DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); -#endif // V4_BOOTSTRAP - using namespace QmlIR; +using namespace QQmlJS; #define COMPILE_EXCEPTION(location, desc) \ { \ @@ -74,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; @@ -258,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) @@ -385,13 +400,12 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen if (!parseResult || !diagnosticMessages.isEmpty()) { // Extract errors from the parser for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { - if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(url), m.line, qPrintable(m.message)); continue; } - recordError(m.loc, m.message); + errors << m; } return false; } @@ -661,11 +675,9 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) return false; } - if (node->versionToken.isValid()) { - int major, minor; - extractVersion(textRefAt(node->versionToken), &major, &minor); - import->majorVersion = major; - import->minorVersion = minor; + if (node->version) { + import->majorVersion = node->version->majorVersion; + import->minorVersion = node->version->minorVersion; } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); return false; @@ -778,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::Property::Type type; - } propTypeNameToTypes[] = { - { "int", strlen("int"), QV4::CompiledData::Property::Int }, - { "bool", strlen("bool"), QV4::CompiledData::Property::Bool }, - { "double", strlen("double"), QV4::CompiledData::Property::Real }, - { "real", strlen("real"), QV4::CompiledData::Property::Real }, - { "string", strlen("string"), QV4::CompiledData::Property::String }, - { "url", strlen("url"), QV4::CompiledData::Property::Url }, - { "color", strlen("color"), QV4::CompiledData::Property::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::Property::DateTime }, - { "rect", strlen("rect"), QV4::CompiledData::Property::Rect }, - { "point", strlen("point"), QV4::CompiledData::Property::Point }, - { "size", strlen("size"), QV4::CompiledData::Property::Size }, - { "font", strlen("font"), QV4::CompiledData::Property::Font }, - { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D }, - { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D }, - { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D }, - { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 }, - { "variant", strlen("variant"), QV4::CompiledData::Property::Variant }, - { "var", strlen("var"), QV4::CompiledData::Property::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(); @@ -821,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) { @@ -832,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->type = QV4::CompiledData::Property::Custom; - param->customTypeNameIndex = registerString(memberType); - } 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->type = type->type; - param->customTypeNameIndex = emptyStringIndex; + 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()); - param->location.line = p->identifierToken.startLine; - param->location.column = p->identifierToken.startColumn; signal->parameters->append(param); p = p->next; } @@ -886,25 +839,21 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) } else { const QStringRef &name = node->name; - bool typeFound = false; - QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var; + Property *property = New<Property>(); + property->isReadOnly = node->isReadonlyMember; - for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = 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; - if (typeModifier.isEmpty()) { - type = QV4::CompiledData::Property::Custom; - } else if (typeModifier == QLatin1String("list")) { - type = QV4::CompiledData::Property::CustomList; - } else { + property->setCustomType(registerString(memberType)); + if (typeModifier == QLatin1String("list")) { + property->isList = true; + } else if (!typeModifier.isEmpty()) { recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); return false; } @@ -919,16 +868,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) return false; } - Property *property = New<Property>(); - property->flags = 0; - if (node->isReadonlyMember) - property->flags |= QV4::CompiledData::Property::IsReadOnly; - property->type = type; - if (type >= QV4::CompiledData::Property::Custom) - property->customTypeNameIndex = registerString(memberType); - else - property->customTypeNameIndex = emptyStringIndex; - const QString propName = name.toString(); property->nameIndex = registerString(propName); @@ -969,7 +908,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) { - if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) { + if (QQmlJS::AST::FunctionExpression *funDecl = node->sourceElement->asFunctionDefinition()) { CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>(); foe->node = funDecl; foe->parentNode = funDecl; @@ -983,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; } @@ -1022,7 +964,7 @@ QStringRef IRBuilder::asStringRef(QQmlJS::AST::Node *node) return textRefAt(node->firstSourceLocation(), node->lastSourceLocation()); } -void IRBuilder::extractVersion(QStringRef string, int *maj, int *min) +void IRBuilder::extractVersion(const QStringRef &string, int *maj, int *min) { *maj = -1; *min = -1; @@ -1051,7 +993,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->valueLocation.line = loc.startLine; binding->valueLocation.column = loc.startColumn; binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); @@ -1282,7 +1224,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo binding->flags = 0; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + if (_propertyDeclaration && _propertyDeclaration->isReadOnly) binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; // No type name on the initializer means it must be a group property @@ -1513,7 +1455,8 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O void IRBuilder::recordError(const QQmlJS::AST::SourceLocation &location, const QString &description) { QQmlJS::DiagnosticMessage error; - error.loc = location; + error.line = location.startLine; + error.column = location.startColumn; error.message = description; errors << error; } @@ -1545,7 +1488,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) { - if (property->type != QV4::CompiledData::Property::Custom) + if (property->isBuiltinType || property->isList) return false; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); if (!exprStmt) @@ -1559,17 +1502,12 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen output.jsGenerator.stringTable.registerString(output.jsModule.fileName); output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; - QV4::CompiledData::Unit *jsUnit = nullptr; - const bool finalize = !compilationUnit->data; // We may already have unit data if we're loading an ahead-of-time generated cache file. - if (!finalize) { - jsUnit = const_cast<QV4::CompiledData::Unit *>(compilationUnit->data); -#ifndef V4_BOOTSTRAP - output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); -#endif + if (output.javaScriptCompilationUnit.data) { + jsUnit = const_cast<QV4::CompiledData::Unit *>(output.javaScriptCompilationUnit.data); + output.javaScriptCompilationUnit.dynamicStrings = output.jsGenerator.stringTable.allStrings(); } else { QV4::CompiledData::Unit *createdUnit; jsUnit = createdUnit = output.jsGenerator.generateUnit(); @@ -1581,22 +1519,15 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen break; } } - // This unit's memory was allocated with malloc on the heap, so it's - // definitely not suitable for StaticData access. - createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData; -#ifndef V4_BOOTSTRAP if (dependencyHasher) { - QCryptographicHash hash(QCryptographicHash::Md5); - if (dependencyHasher(&hash)) { - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)); - memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum)); + const QByteArray checksum = dependencyHasher(); + if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) { + memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), + sizeof(createdUnit->dependencyMD5Checksum)); } } -#else - Q_UNUSED(dependencyHasher); -#endif + createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); } @@ -1730,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); @@ -1765,14 +1696,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen } } - if (finalize) { + if (!output.javaScriptCompilationUnit.data) { // Combine the qml data into the general unit data. jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize)); jsUnit->offsetToQmlUnit = jsUnit->unitSize; jsUnit->unitSize += totalSize; memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize); free(qmlUnit); - jsUnit->generateChecksum(); + QV4::Compiler::JSUnitGenerator::generateUnitChecksum(jsUnit); qmlUnit = jsUnit->qmlUnit(); } @@ -1798,7 +1729,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen qDebug() << " " << totalStringSize << "bytes total strings"; } - compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl); + output.javaScriptCompilationUnit.setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, + output.jsModule.finalUrl); } char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const @@ -1815,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; } @@ -1835,18 +1759,18 @@ 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); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); + Q_ASSERT(f.node != document->program); + Q_ASSERT(f.parentNode && f.parentNode != document->program); + auto function = f.node->asFunctionDefinition(); if (function) { scan.enterQmlFunction(function); @@ -1860,7 +1784,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil } scan.leaveEnvironment(); - if (hasError) + if (hasError()) return QVector<int>(); _context = nullptr; @@ -1868,9 +1792,9 @@ 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); + QQmlJS::AST::FunctionExpression *function = node->asFunctionDefinition(); QString name; if (function) @@ -1883,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) { @@ -1904,213 +1828,57 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil return runtimeFunctionIndices; } -#ifndef V4_BOOTSTRAP - -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else { - return d; - } -} - - -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const +bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots) { - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QLatin1String("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); + for (int i = 0; i < componentRoots.count(); ++i) { + if (!compileComponent(componentRoots.at(i))) + return false; } - return nullptr; -} - -IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) - : unit(qmlData) - , output(output) -{ - pool = output->jsParserEngine.pool(); + return compileComponent(/*root object*/0); } -void IRLoader::load() +bool JSCodeGen::compileComponent(int contextObject) { - output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - - const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); - - for (quint32 i = 0; i < qmlUnit->nImports; ++i) - output->imports << qmlUnit->importAt(i); - - if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { - QmlIR::Pragma *p = New<QmlIR::Pragma>(); - p->location = QV4::CompiledData::Location(); - p->type = QmlIR::Pragma::PragmaSingleton; - output->pragmas << p; + 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; } - for (uint i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); - QmlIR::Object *object = loadObject(serializedObject); - output->objects.append(object); - } + return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); } -struct FakeExpression : public QQmlJS::AST::NullExpression -{ - FakeExpression(int start, int length) - : location(start, length) - {} - - virtual QQmlJS::AST::SourceLocation firstSourceLocation() const - { return location; } - - virtual QQmlJS::AST::SourceLocation lastSourceLocation() const - { return location; } - -private: - QQmlJS::AST::SourceLocation location; -}; - -QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) { - QmlIR::Object *object = pool->New<QmlIR::Object>(); - object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); - - object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->flags = serializedObject->flags; - object->id = serializedObject->id; - object->location = serializedObject->location; - object->locationOfIdProperty = serializedObject->locationOfIdProperty; - - QVector<int> functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New<QmlIR::Binding>(); - *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; - object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { - functionIndices.append(b->value.compiledScriptIndex); - b->value.compiledScriptIndex = functionIndices.count() - 1; - - QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); - foe->nameIndex = 0; - - QQmlJS::AST::ExpressionNode *expr; - - if (b->stringIndex != quint32(0)) { - const int start = output->code.length(); - const QString script = output->stringAt(b->stringIndex); - const int length = script.length(); - output->code.append(script); - expr = new (pool) FakeExpression(start, length); - } else - expr = new (pool) QQmlJS::AST::NullExpression(); - foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy - object->functionsAndExpressions->append(foe); - } - } - - Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + QmlIR::Object *object = document->objects.at(objectIndex); + if (object->flags & QV4::CompiledData::Object::IsComponent) + return true; - for (uint i = 0; i < serializedObject->nSignals; ++i) { - const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); - QmlIR::Signal *s = pool->New<QmlIR::Signal>(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); - *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); - s->parameters->append(p); - } - - object->qmlSignals->append(s); - } - - for (uint i = 0; i < serializedObject->nEnums; ++i) { - const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); - QmlIR::Enum *e = pool->New<QmlIR::Enum>(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); - *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); - e->enumValues->append(v); - } - - object->qmlEnums->append(e); - } - - const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); - for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { - QmlIR::Property *p = pool->New<QmlIR::Property>(); - *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; - object->properties->append(p); - } + 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; - { - const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); - for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { - QmlIR::Alias *a = pool->New<QmlIR::Alias>(); - *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; - object->aliases->append(a); - } + object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), + runtimeFunctionIndices); } - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); - for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { - QmlIR::Function *f = pool->New<QmlIR::Function>(); - const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); - - functionIndices.append(*functionIdx); - f->index = functionIndices.count() - 1; - f->location = compiledFunction->location; - f->nameIndex = compiledFunction->nameIndex; + for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { + if (binding->type < QV4::CompiledData::Binding::Type_Object) + continue; - f->formals.allocate(pool, int(compiledFunction->nFormals)); - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); - for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) - f->formals[i] = *formalNameIdx; + int target = binding->value.objectIndex; + int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; - object->functions->append(f); + if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) + return false; } - object->runtimeFunctionIndices.allocate(pool, functionIndices); - - return object; + return true; } - -#endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 22bc2d2953..4279f5b768 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -55,25 +55,22 @@ #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> #include <QCoreApplication> -#ifndef V4_BOOTSTRAP -#include <private/qqmlpropertycache_p.h> -#endif - QT_BEGIN_NAMESPACE class QQmlPropertyCache; class QQmlContextData; class QQmlTypeNameCache; +struct QQmlIRLoader; namespace QmlIR { struct Document; -struct IRLoader; template <typename T> struct PoolList @@ -219,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; }; @@ -264,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() {} @@ -285,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: @@ -344,14 +350,14 @@ 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(); } private: - friend struct IRLoader; + friend struct ::QQmlIRLoader; PoolList<Property> *properties; PoolList<Alias> *aliases; @@ -361,7 +367,7 @@ private: PoolList<Function> *functions; }; -struct Q_QML_PRIVATE_EXPORT Pragma +struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma { enum PragmaType { PragmaSingleton = 0x1 @@ -371,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,15 +389,13 @@ struct Q_QML_PRIVATE_EXPORT Document QVector<Object*> objects; QV4::Compiler::JSUnitGenerator jsGenerator; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit; + QV4::CompiledData::CompilationUnit javaScriptCompilationUnit; 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; @@ -405,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: @@ -436,7 +440,7 @@ public: void throwRecursionDepthError() override { - recordError(AST::SourceLocation(), + recordError(QQmlJS::AST::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } @@ -449,19 +453,25 @@ public: static QString asString(QQmlJS::AST::UiQualifiedId *node); QStringRef asStringRef(QQmlJS::AST::Node *node); - static void extractVersion(QStringRef string, int *maj, int *min); + static void extractVersion(const QStringRef &string, int *maj, int *min); QStringRef textRefAt(const QQmlJS::AST::SourceLocation &loc) const { return QStringRef(&sourceCode, loc.offset, loc.length); } 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); @@ -503,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()); @@ -512,76 +522,23 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -#ifndef V4_BOOTSTRAP -struct Q_QML_EXPORT PropertyResolver -{ - PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) - : cache(cache) - {} - - QQmlPropertyData *property(int index) const - { - return cache->property(index); - } - - enum RevisionCheck { - CheckRevision, - IgnoreRevision - }; - - QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, RevisionCheck check = CheckRevision) const; - - // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - - QQmlRefPointer<QQmlPropertyCache> cache; -}; -#endif - -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); -private: - QString sourceCode; - QQmlJS::Engine *jsEngine; // needed for memory pool - QQmlJS::AST::UiProgram *qmlRoot; - const QV4::Compiler::StringTableGenerator *stringPool; -}; - -struct Q_QML_PRIVATE_EXPORT IRLoader { - IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); - - void load(); + bool generateCodeForComponents(const QVector<quint32> &componentRoots); + bool compileComponent(int contextObject); + bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); private: - QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); - - template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } - - const QV4::CompiledData::Unit *unit; - QmlIR::Document *output; - QQmlJS::MemoryPool *pool; + Document *document; }; } // namespace QmlIR -struct QQmlCompileError -{ - QQmlCompileError() {} - QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) - : location(location), description(description) {} - QV4::CompiledData::Location location; - QString description; - - bool isSet() const { return !description.isEmpty(); } -}; - 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 1d0a57c536..8c509dd9f1 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -62,12 +62,15 @@ class SourceLocation; } namespace QV4 { + +namespace Compiler { +struct Context; +} + namespace Moth { class BytecodeGenerator { public: - typedef CompiledData::Function::TraceInfoCount TraceInfoCount; - BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -164,15 +167,6 @@ public: addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); } - // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot - // be reused afterwards. - template<int InstrT> - void addTracingInstruction(InstrData<InstrT> data) - { - data.traceSlot = nextTraceInfo(); - addInstruction(data); - } - Q_REQUIRED_RESULT Jump jump() { QT_WARNING_PUSH @@ -184,12 +178,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - return addTracingJumpInstruction(Instruction::JumpTrue()); + return addJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - return addTracingJumpInstruction(Instruction::JumpFalse()); + return addJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -209,7 +203,7 @@ QT_WARNING_POP Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) @@ -217,7 +211,13 @@ QT_WARNING_POP Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + void checkException() + { + Instruction::CheckException chk; + addInstruction(chk); } void setUnwindHandler(ExceptionHandler *handler) @@ -258,13 +258,6 @@ QT_WARNING_POP void finalize(Compiler::Context *context); template<int InstrT> - Jump addTracingJumpInstruction(InstrData<InstrT> &&data) - { - data.traceSlot = nextTraceInfo(); - return addJumpInstruction(data); - } - - template<int InstrT> Jump addJumpInstruction(const InstrData<InstrT> &data) { Instr genericInstr; @@ -275,9 +268,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -285,27 +278,6 @@ QT_WARNING_POP lastInstrType = -1; } - TraceInfoCount nextTraceInfo() - { - // If tracing is disabled, use slot 0 to unconditionally store all trace info - if (nTraceInfos == CompiledData::Function::NoTracing()) - return TraceInfoCount(0); - return nTraceInfos++; - } - - void setTracing(bool onoff, int argumentCount) - { - if (onoff) - nTraceInfos = argumentCount; - else - nTraceInfos = CompiledData::Function::NoTracing(); - } - - TraceInfoCount traceInfoCount() const - { - return nTraceInfos; - } - void addLoopStart(const Label &start) { _labelInfos.push_back({ start.index }); @@ -346,8 +318,6 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; - TraceInfoCount nTraceInfos = TraceInfoCount(0); - struct LabelInfo { int labelIndex; }; diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 92b112c2fa..f9f755b8c0 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -58,9 +58,10 @@ ByteCodeHandler::~ByteCodeHandler() Q_UNUSED(base_ptr); \ _currentOffset = _nextOffset; \ _nextOffset = code - start; \ - startInstruction(Instr::Type::instr); \ - INSTR_##instr(DISPATCH) \ - endInstruction(Instr::Type::instr); \ + if (startInstruction(Instr::Type::instr) == ProcessInstruction) { \ + INSTR_##instr(DISPATCH) \ + endInstruction(Instr::Type::instr); \ + } \ continue; \ } diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index 797d25b8d0..f1e7c99447 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -105,7 +105,8 @@ public: protected: FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) - virtual void startInstruction(Moth::Instr::Type instr) = 0; + enum Verdict { ProcessInstruction, SkipInstruction }; + virtual Verdict startInstruction(Moth::Instr::Type instr) = 0; virtual void endInstruction(Moth::Instr::Type instr) = 0; private: diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 5acc64bd81..c43ea64e2e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -43,18 +43,18 @@ #include <QtCore/QCoreApplication> #include <QtCore/QStringList> #include <QtCore/QStack> +#include <QtCore/qurl.h> #include <QScopeGuard> #include <private/qqmljsast_p.h> -#include <private/qv4string_p.h> -#include <private/qv4value_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4compilercontrolflow_p.h> #include <private/qv4bytecodegenerator_p.h> #include <private/qv4compilerscanfunctions_p.h> - -#ifndef V4_BOOTSTRAP -# include <qqmlerror.h> -#endif +#include <private/qv4stringtoarrayindex_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <cmath> #include <iostream> @@ -68,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, @@ -97,7 +98,6 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) , jsUnitGenerator(jsUnitGenerator) , _strictMode(strict) , _fileNameIsUrl(false) - , hasError(false) { jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); pushExpr(); @@ -190,7 +190,7 @@ void Codegen::generateFromProgram(const QString &fileName, ScanFunctions scan(this, sourceCode, contextType); scan(node); - if (hasError) + if (hasError()) return; defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements); @@ -214,7 +214,7 @@ void Codegen::generateFromModule(const QString &fileName, ScanFunctions scan(this, sourceCode, ContextType::ESModule); scan(node); - if (hasError) + if (hasError()) return; { @@ -264,17 +264,30 @@ Context *Codegen::enterBlock(Node *node) Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) { - if (hasError) + if (hasError()) return exprResult(); if (expr.isConstant()) { - auto v = Value::fromReturnedValue(expr.constant); + auto v = StaticValue::fromReturnedValue(expr.constant); if (v.isNumber()) { switch (op) { case Not: return Reference::fromConst(this, Encode(!v.toBoolean())); case UMinus: - return Reference::fromConst(this, Runtime::method_uMinus(v)); + // This duplicates some of the logic from Runtime::UMinus::call() + ReturnedValue r; + if (v.isInteger()) { + int intVal = v.integerValue(); + if (intVal && intVal != std::numeric_limits<int>::min()) + r = QV4::Encode(-intVal); + else + r = QV4::Encode(-double(intVal)); + } else if (v.isDouble()) { + r = QV4::Encode(-v.doubleValue()); + } else { + r = QV4::Encode(-v.int_32()); + } + return Reference::fromConst(this, r); case UPlus: return expr; case Compl: @@ -289,12 +302,12 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case UMinus: { expr.loadInAccumulator(); Instruction::UMinus uminus = {}; - bytecodeGenerator->addTracingInstruction(uminus); + bytecodeGenerator->addInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { expr.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); return Reference::fromAccumulator(this); } @@ -314,11 +327,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -330,7 +343,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -340,11 +353,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) if (!exprAccept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::UPlus uplus; + Instruction::UPlus uplus = {}; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -356,7 +369,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -402,7 +415,7 @@ void Codegen::statement(ExpressionNode *ast) qSwap(_volatileMemoryLocations, vLocs); Reference result = popResult(); - if (hasError) + if (hasError()) return; if (result.loadTriggersSideEffect()) result.loadInAccumulator(); // triggers side effects @@ -412,7 +425,7 @@ void Codegen::statement(ExpressionNode *ast) void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition) { - if (hasError) + if (hasError()) return; if (!ast) @@ -422,7 +435,7 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift accept(ast); Result r = popExpr(); - if (hasError) + if (hasError()) return; if (r.format() == ex) { @@ -576,7 +589,7 @@ Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) if (!p->bindingTarget || p->destructuringPattern()) return Codegen::Reference::fromStackSlot(this); Reference lhs = expression(p->bindingTarget); - if (hasError) + if (hasError()) return lhs; if (!lhs.isLValue()) { throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference.")); @@ -594,14 +607,16 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con Reference varToStore = targetForPatternElement(e); if (isDefinition) varToStore.isReferenceToConst = false; - if (hasError) + if (hasError()) return; + accept(e->typeAnnotation); + if (e->initializer) { if (!baseRef.isValid()) { // assignment Reference expr = expression(e->initializer); - if (hasError) + if (hasError()) return; expr.loadInAccumulator(); varToStore.storeConsumeAccumulator(); @@ -609,7 +624,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con baseRef.loadInAccumulator(); BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); Reference expr = expression(e->initializer); - if (hasError) { + if (hasError()) { jump.link(); return; } @@ -620,7 +635,7 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con baseRef.loadInAccumulator(); BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined(); Reference expr = expression(e->initializer); - if (hasError) { + if (hasError()) { jump.link(); return; } @@ -657,7 +672,7 @@ Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &o Reference property; if (cname) { Reference computedName = expression(cname->expression); - if (hasError) + if (hasError()) return Reference(); computedName = computedName.storeOnStack(); property = Reference::fromSubscript(object, computedName).asLValue(); @@ -680,10 +695,10 @@ void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternP PatternProperty *p = it->property; RegisterScope scope(this); Reference property = referenceForPropertyName(object, p->name); - if (hasError) + if (hasError()) return; initializeAndDestructureBindingElement(p, property, isDefinition); - if (hasError) + if (hasError()) return; } } @@ -739,7 +754,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); initializeAndDestructureBindingElement(e, iteratorValue, isDefinition); - if (hasError) + if (hasError()) return; } } @@ -872,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(); @@ -1003,11 +1024,11 @@ bool Codegen::visit(ClassExpression *ast) if (ast->heritage) { bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation()); Reference r = expression(ast->heritage); - if (hasError) + if (hasError()) return false; r.storeOnStack(heritage.stackSlot()); } else { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); heritage.storeConsumeAccumulator(); } @@ -1022,7 +1043,7 @@ bool Codegen::visit(ClassExpression *ast) RegisterScope scope(this); bytecodeGenerator->setLocation(cname->firstSourceLocation()); Reference computedName = expression(cname->expression); - if (hasError) + if (hasError()) return false; computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++); } @@ -1055,7 +1076,7 @@ bool Codegen::visit(ClassDeclaration *ast) bool Codegen::visit(Expression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1068,7 +1089,7 @@ bool Codegen::visit(Expression *ast) bool Codegen::visit(ArrayPattern *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1085,12 +1106,12 @@ bool Codegen::visit(ArrayPattern *ast) if (args == -1) args = temp; if (!arg) { - auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue()); + auto c = Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()); (void) c.storeOnStack(temp); } else { RegisterScope scope(this); Reference r = expression(arg); - if (hasError) + if (hasError()) return; (void) r.storeOnStack(temp); } @@ -1108,7 +1129,7 @@ bool Codegen::visit(ArrayPattern *ast) continue; push(e->initializer); - if (hasError) + if (hasError()) return false; } @@ -1140,13 +1161,14 @@ bool Codegen::visit(ArrayPattern *ast) index.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); index.storeConsumeAccumulator(); }; while (it) { for (Elision *elision = it->elision; elision; elision = elision->next) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst( + this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); pushAccumulator(); } @@ -1168,7 +1190,7 @@ bool Codegen::visit(ArrayPattern *ast) { RegisterScope innerScope(this); Reference expr = expression(it->element->initializer); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -1202,13 +1224,14 @@ bool Codegen::visit(ArrayPattern *ast) lhsValue.loadInAccumulator(); pushAccumulator(); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); end.link(); } } else { RegisterScope innerScope(this); Reference expr = expression(it->element->initializer); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -1226,12 +1249,12 @@ bool Codegen::visit(ArrayPattern *ast) bool Codegen::visit(ArrayMemberExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { Reference index = expression(ast->expression).storeOnStack(); @@ -1239,11 +1262,11 @@ bool Codegen::visit(ArrayMemberExpression *ast) return false; } base = base.storeOnStack(); - if (hasError) + if (hasError()) return false; if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) { QString s = str->value.toString(); - uint arrayIndex = QV4::String::toArrayIndex(s); + uint arrayIndex = stringToArrayIndex(s); if (arrayIndex == UINT_MAX) { setExprResult(Reference::fromMember(base, str->value.toString())); return false; @@ -1253,7 +1276,7 @@ bool Codegen::visit(ArrayMemberExpression *ast) return false; } Reference index = expression(ast->expression); - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromSubscript(base, index)); return false; @@ -1280,7 +1303,7 @@ static QSOperator::Op baseOp(int op) bool Codegen::visit(BinaryExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -1298,7 +1321,7 @@ bool Codegen::visit(BinaryExpression *ast) auto endif = bytecodeGenerator->newLabel(); Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; left.loadInAccumulator(); @@ -1308,7 +1331,7 @@ bool Codegen::visit(BinaryExpression *ast) blockTailCalls.unblock(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right.loadInAccumulator(); @@ -1329,7 +1352,7 @@ bool Codegen::visit(BinaryExpression *ast) auto endif = bytecodeGenerator->newLabel(); Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; left.loadInAccumulator(); @@ -1339,7 +1362,7 @@ bool Codegen::visit(BinaryExpression *ast) blockTailCalls.unblock(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right.loadInAccumulator(); @@ -1352,7 +1375,7 @@ bool Codegen::visit(BinaryExpression *ast) if (AST::Pattern *p = ast->left->patternCast()) { RegisterScope scope(this); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; right = right.storeOnStack(); destructurePattern(p, right); @@ -1363,7 +1386,7 @@ bool Codegen::visit(BinaryExpression *ast) return false; } Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; if (!left.isLValue()) { @@ -1375,7 +1398,7 @@ bool Codegen::visit(BinaryExpression *ast) return false; blockTailCalls.unblock(); Reference r = expression(ast->right); - if (hasError) + if (hasError()) return false; r.loadInAccumulator(); if (exprAccept(nx)) @@ -1386,7 +1409,7 @@ bool Codegen::visit(BinaryExpression *ast) } Reference left = expression(ast->left); - if (hasError) + if (hasError()) return false; switch (ast->op) { @@ -1420,7 +1443,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference tempLeft = left.storeOnStack(); Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); @@ -1434,7 +1457,7 @@ bool Codegen::visit(BinaryExpression *ast) case QSOperator::BitXor: if (left.isConstant()) { Reference right = expression(ast->right); - if (hasError) + if (hasError()) return false; setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); break; @@ -1467,7 +1490,7 @@ bool Codegen::visit(BinaryExpression *ast) left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it right = expression(ast->right); } - if (hasError) + if (hasError()) return false; setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); @@ -1488,20 +1511,20 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(add); + bytecodeGenerator->addInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(sub); + bytecodeGenerator->addInstruction(sub); } break; } @@ -1518,7 +1541,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mul); + bytecodeGenerator->addInstruction(mul); break; } case QSOperator::Div: { @@ -1534,14 +1557,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mod); + bytecodeGenerator->addInstruction(mod); break; } case QSOperator::BitAnd: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() & rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1557,9 +1580,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitOr: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() | rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1575,9 +1598,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitXor: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1595,7 +1618,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::UShrConst ushr; - ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + ushr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(ushr); } else { right.loadInAccumulator(); @@ -1608,7 +1631,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShrConst shr; - shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shr); } else { right.loadInAccumulator(); @@ -1621,7 +1644,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShlConst shl; - shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shl.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shl); } else { right.loadInAccumulator(); @@ -1749,7 +1772,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe qSwap(left, right); // null==a -> a==null if (right.isConstant()) { - Value c = Value::fromReturnedValue(right.constant); + StaticValue c = StaticValue::fromReturnedValue(right.constant); if (c.isNull() || c.isUndefined()) { left.loadInAccumulator(); if (oper == QSOperator::Equal) { @@ -1851,7 +1874,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe bool Codegen::visit(CallExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -1859,7 +1882,7 @@ bool Codegen::visit(CallExpression *ast) Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; switch (base.type) { case Reference::Member: @@ -1882,7 +1905,7 @@ bool Codegen::visit(CallExpression *ast) int functionObject = bytecodeGenerator->newRegister(); auto calldata = pushArgs(ast->arguments); - if (hasError) + if (hasError()) return false; blockTailCalls.unblock(); @@ -1903,7 +1926,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1932,14 +1955,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -1947,33 +1970,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -1990,14 +2013,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } setExprResult(Reference::fromAccumulator(this)); @@ -2023,12 +2046,14 @@ Codegen::Arguments Codegen::pushArgs(ArgumentList *args) argc = 0; for (ArgumentList *it = args; it; it = it->next) { if (it->isSpreadElement) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); + Reference::fromConst( + this, + StaticValue::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); ++argc; } RegisterScope scope(this); Reference e = expression(it->expression); - if (hasError) + if (hasError()) break; if (!argc && !it->next && !hasSpread) { // avoid copy for functions taking a single argument @@ -2057,7 +2082,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args) for (TemplateLiteral *it = args; it && it->expression; it = it->next) { RegisterScope scope(this); Reference e = expression(it->expression); - if (hasError) + if (hasError()) break; (void) e.storeOnStack(calldata + argc); ++argc; @@ -2068,7 +2093,7 @@ Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args) bool Codegen::visit(ConditionalExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2082,14 +2107,14 @@ bool Codegen::visit(ConditionalExpression *ast) iftrue.link(); Reference ok = expression(ast->ok); - if (hasError) + if (hasError()) return false; ok.loadInAccumulator(); BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); iffalse.link(); Reference ko = expression(ast->ko); - if (hasError) { + if (hasError()) { jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering return false; } @@ -2103,13 +2128,13 @@ bool Codegen::visit(ConditionalExpression *ast) bool Codegen::visit(DeleteExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; switch (expr.type) { @@ -2174,7 +2199,7 @@ bool Codegen::visit(DeleteExpression *ast) bool Codegen::visit(FalseLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode(false))); @@ -2183,7 +2208,7 @@ bool Codegen::visit(FalseLiteral *) bool Codegen::visit(SuperLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromSuper(this)); @@ -2192,7 +2217,7 @@ bool Codegen::visit(SuperLiteral *) bool Codegen::visit(FieldMemberExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2215,7 +2240,7 @@ bool Codegen::visit(FieldMemberExpression *ast) } Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { Instruction::LoadRuntimeString load; @@ -2231,7 +2256,7 @@ bool Codegen::visit(FieldMemberExpression *ast) bool Codegen::visit(TaggedTemplate *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2240,7 +2265,7 @@ bool Codegen::visit(TaggedTemplate *ast) bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) { - if (hasError) + if (hasError()) return false; int functionObject = -1, thisObject = -1; @@ -2264,7 +2289,7 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot(); Q_UNUSED(templateObjectTemp); auto calldata = pushTemplateArgs(ast->templateLiteral); - if (hasError) + if (hasError()) return false; ++calldata.argc; Q_ASSERT(calldata.argv == templateObjectTemp + 1); @@ -2293,7 +2318,7 @@ void Codegen::createTemplateObject(TemplateLiteral *t) bool Codegen::visit(FunctionExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2301,7 +2326,7 @@ bool Codegen::visit(FunctionExpression *ast) RegisterScope scope(this); int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); - if (hasError) + if (hasError()) return false; loadClosure(function); setExprResult(Reference::fromAccumulator(this)); @@ -2357,7 +2382,7 @@ void Codegen::loadClosure(int closureId) bool Codegen::visit(IdentifierExpression *ast) { - if (hasError) + if (hasError()) return false; setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation())); @@ -2366,7 +2391,7 @@ bool Codegen::visit(IdentifierExpression *ast) bool Codegen::visit(NestedExpression *ast) { - if (hasError) + if (hasError()) return false; accept(ast->expression); @@ -2385,7 +2410,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) } auto calldata = pushArgs(arguments); - if (hasError) + if (hasError()) return; if (base.isSuper()) @@ -2415,14 +2440,14 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments) bool Codegen::visit(NewExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference base = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); @@ -2435,14 +2460,14 @@ bool Codegen::visit(NewExpression *ast) bool Codegen::visit(NewMemberExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); - if (hasError) + if (hasError()) return false; if (base.isSuper()) { throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super.")); @@ -2455,7 +2480,7 @@ bool Codegen::visit(NewMemberExpression *ast) bool Codegen::visit(NotExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2465,7 +2490,7 @@ bool Codegen::visit(NotExpression *ast) bool Codegen::visit(NullExpression *) { - if (hasError) + if (hasError()) return false; if (exprAccept(cx)) @@ -2478,7 +2503,7 @@ bool Codegen::visit(NullExpression *) bool Codegen::visit(NumericLiteral *ast) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value))); @@ -2487,7 +2512,7 @@ bool Codegen::visit(NumericLiteral *ast) bool Codegen::visit(ObjectPattern *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2513,7 +2538,7 @@ bool Codegen::visit(ObjectPattern *ast) if (cname || p->type != PatternProperty::Literal) break; QString name = p->name->asString(); - uint arrayIndex = QV4::String::toArrayIndex(name); + uint arrayIndex = stringToArrayIndex(name); if (arrayIndex != UINT_MAX) break; if (members.contains(name)) @@ -2523,7 +2548,7 @@ bool Codegen::visit(ObjectPattern *ast) { RegisterScope innerScope(this); Reference value = expression(p->initializer, name); - if (hasError) + if (hasError()) return false; value.loadInAccumulator(); } @@ -2550,7 +2575,7 @@ bool Codegen::visit(ObjectPattern *ast) if (cname) { RegisterScope innerScope(this); Reference name = expression(cname->expression); - if (hasError) + if (hasError()) return false; name.loadInAccumulator(); } else { @@ -2575,12 +2600,12 @@ bool Codegen::visit(ObjectPattern *ast) FunctionExpression *f = p->initializer->asFunctionDefinition(); Q_ASSERT(f); int function = defineFunction(f->name.toString(), f, f->formals, f->body); - if (hasError) + if (hasError()) return false; Reference::fromConst(this, Encode(function)).loadInAccumulator(); } else { Reference value = expression(p->initializer); - if (hasError) + if (hasError()) return false; value.loadInAccumulator(); } @@ -2599,11 +2624,11 @@ bool Codegen::visit(ObjectPattern *ast) bool Codegen::visit(PostDecrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->base); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); @@ -2619,11 +2644,11 @@ bool Codegen::visit(PostDecrementExpression *ast) bool Codegen::visit(PostIncrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->base); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); @@ -2637,11 +2662,11 @@ bool Codegen::visit(PostIncrementExpression *ast) } bool Codegen::visit(PreDecrementExpression *ast) -{ if (hasError) +{ if (hasError()) return false; Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); @@ -2656,11 +2681,11 @@ bool Codegen::visit(PreDecrementExpression *ast) bool Codegen::visit(PreIncrementExpression *ast) { - if (hasError) + if (hasError()) return false; Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (!expr.isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); @@ -2675,7 +2700,7 @@ bool Codegen::visit(PreIncrementExpression *ast) bool Codegen::visit(RegExpLiteral *ast) { - if (hasError) + if (hasError()) return false; auto r = Reference::fromStackSlot(this); @@ -2691,7 +2716,7 @@ bool Codegen::visit(RegExpLiteral *ast) bool Codegen::visit(StringLiteral *ast) { - if (hasError) + if (hasError()) return false; auto r = Reference::fromAccumulator(this); @@ -2706,7 +2731,7 @@ bool Codegen::visit(StringLiteral *ast) bool Codegen::visit(TemplateLiteral *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2723,7 +2748,7 @@ bool Codegen::visit(TemplateLiteral *ast) bytecodeGenerator->addInstruction(store); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (ast->next) { @@ -2733,14 +2758,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -2753,7 +2778,7 @@ bool Codegen::visit(TemplateLiteral *ast) bool Codegen::visit(ThisExpression *) { - if (hasError) + if (hasError()) return false; if (_context->isArrowFunction) { @@ -2768,7 +2793,7 @@ bool Codegen::visit(ThisExpression *) bool Codegen::visit(TildeExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2778,7 +2803,7 @@ bool Codegen::visit(TildeExpression *ast) bool Codegen::visit(TrueLiteral *) { - if (hasError) + if (hasError()) return false; setExprResult(Reference::fromConst(this, QV4::Encode(true))); @@ -2787,14 +2812,14 @@ bool Codegen::visit(TrueLiteral *) bool Codegen::visit(TypeOfExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; if (expr.type == Reference::Name) { @@ -2814,7 +2839,7 @@ bool Codegen::visit(TypeOfExpression *ast) bool Codegen::visit(UnaryMinusExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2824,7 +2849,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) bool Codegen::visit(UnaryPlusExpression *ast) { - if (hasError) + if (hasError()) return false; TailCallBlocker blockTailCalls(this); @@ -2834,7 +2859,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) bool Codegen::visit(VoidExpression *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -2847,7 +2872,7 @@ bool Codegen::visit(VoidExpression *ast) bool Codegen::visit(FunctionDeclaration * ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls: the function body isn't visited here. @@ -2869,7 +2894,7 @@ bool Codegen::visit(YieldExpression *ast) RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined()); - if (hasError) + if (hasError()) return false; Reference acc = Reference::fromAccumulator(this); @@ -2998,10 +3023,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool savedFunctionEndsWithReturn = functionEndsWithReturn; functionEndsWithReturn = endsWithReturn(_module, body); - bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size()); // reserve the js stack frame (Context & js Function & accumulator) - bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + bytecodeGenerator->newRegisterArray( + sizeof(CallData) / sizeof(StaticValue) - 1 + _context->arguments.size()); bool _inFormalParameterList = false; qSwap(_inFormalParameterList, inFormalParameterList); @@ -3047,7 +3072,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } else { if (e->bindingTarget || e->initializer) { initializeAndDestructureBindingElement(e, arg); - if (hasError) + if (hasError()) break; } } @@ -3063,7 +3088,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, statementList(body); - if (!hasError) { + if (!hasError()) { bytecodeGenerator->setLocation(ast->lastSourceLocation()); _context->emitBlockFooter(this); @@ -3085,7 +3110,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); _context->registerCountInFunction = bytecodeGenerator->registerCount(); - _context->nTraceInfos = bytecodeGenerator->traceInfoCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict @@ -3111,7 +3135,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool Codegen::visit(Block *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3123,7 +3147,7 @@ bool Codegen::visit(Block *ast) bool Codegen::visit(BreakStatement *ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls here: children aren't visited @@ -3148,7 +3172,7 @@ bool Codegen::visit(BreakStatement *ast) bool Codegen::visit(ContinueStatement *ast) { - if (hasError) + if (hasError()) return false; // no need to block tail calls here: children aren't visited @@ -3181,7 +3205,7 @@ bool Codegen::visit(DebuggerStatement *) bool Codegen::visit(DoWhileStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3191,22 +3215,28 @@ bool Codegen::visit(DoWhileStatement *ast) BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); ControlFlowLoop flow(this, &end, &cond); - bytecodeGenerator->jump().link(body); - - cond.link(); - bytecodeGenerator->addLoopStart(cond); - if (!AST::cast<TrueLiteral *>(ast->expression)) { - TailCallBlocker blockTailCalls(this); - condition(ast->expression, &body, &end, true); - } + // special case that is not a loop: + // do {...} while (false) + if (!AST::cast<FalseLiteral *>(ast->expression)) + bytecodeGenerator->addLoopStart(body); body.link(); statement(ast->statement); setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); - if (!AST::cast<FalseLiteral *>(ast->expression)) - bytecodeGenerator->jump().link(cond); + cond.link(); + if (AST::cast<TrueLiteral *>(ast->expression)) { + // do {} while (true) -> just jump back to the loop body, no need to generate a condition + bytecodeGenerator->checkException(); + bytecodeGenerator->jump().link(body); + } else if (AST::cast<FalseLiteral *>(ast->expression)) { + // do {} while (false) -> fall through, no need to generate a condition + } else { + TailCallBlocker blockTailCalls(this); + bytecodeGenerator->checkException(); + condition(ast->expression, &body, &end, false); + } end.link(); @@ -3220,7 +3250,7 @@ bool Codegen::visit(EmptyStatement *) bool Codegen::visit(ExpressionStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3228,7 +3258,7 @@ bool Codegen::visit(ExpressionStatement *ast) if (requiresReturnValue) { Reference e = expression(ast->expression); - if (hasError) + if (hasError()) return false; (void) e.storeOnStack(_returnAddress); } else { @@ -3239,7 +3269,7 @@ bool Codegen::visit(ExpressionStatement *ast) bool Codegen::visit(ForEachStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3255,7 +3285,7 @@ bool Codegen::visit(ForEachStatement *ast) RegisterScope innerScope(this); ControlFlowBlock controlFlow(this, ast); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -3285,7 +3315,7 @@ bool Codegen::visit(ForEachStatement *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); // each iteration gets it's own context, as per spec { @@ -3298,7 +3328,7 @@ bool Codegen::visit(ForEachStatement *ast) destructurePattern(p, lhsValue); } else { Reference lhs = expression(e); - if (hasError) + if (hasError()) goto error; if (!lhs.isLValue()) { throwReferenceError(e->firstSourceLocation(), QStringLiteral("Invalid left-hand side expression for 'in' expression")); @@ -3310,7 +3340,7 @@ bool Codegen::visit(ForEachStatement *ast) } } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) { initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true); - if (hasError) + if (hasError()) goto error; } else { Q_UNREACHABLE(); @@ -3321,6 +3351,7 @@ bool Codegen::visit(ForEachStatement *ast) setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); } + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); error: @@ -3335,7 +3366,7 @@ bool Codegen::visit(ForEachStatement *ast) bool Codegen::visit(ForStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3369,6 +3400,7 @@ bool Codegen::visit(ForStatement *ast) bytecodeGenerator->addInstruction(clone); } statement(ast->expression); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(cond); end.link(); @@ -3378,7 +3410,7 @@ bool Codegen::visit(ForStatement *ast) bool Codegen::visit(IfStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3410,7 +3442,7 @@ bool Codegen::visit(IfStatement *ast) bool Codegen::visit(LabelledStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3458,7 +3490,7 @@ void Codegen::emitReturn(const Reference &expr) bool Codegen::visit(ReturnStatement *ast) { - if (hasError) + if (hasError()) return false; if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) { @@ -3468,7 +3500,7 @@ bool Codegen::visit(ReturnStatement *ast) Reference expr; if (ast->expression) { expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; } else { expr = Reference::fromConst(this, Encode::undefined()); @@ -3481,7 +3513,7 @@ bool Codegen::visit(ReturnStatement *ast) bool Codegen::visit(SwitchStatement *ast) { - if (hasError) + if (hasError()) return false; if (requiresReturnValue) @@ -3494,7 +3526,7 @@ bool Codegen::visit(SwitchStatement *ast) BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); Reference lhs = expression(ast->expression); - if (hasError) + if (hasError()) return false; lhs = lhs.storeOnStack(); @@ -3513,7 +3545,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; Reference rhs = expression(clause->expression); - if (hasError) + if (hasError()) return false; rhs.loadInAccumulator(); bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); @@ -3522,7 +3554,7 @@ bool Codegen::visit(SwitchStatement *ast) for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; Reference rhs = expression(clause->expression); - if (hasError) + if (hasError()) return false; rhs.loadInAccumulator(); bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause)); @@ -3568,14 +3600,14 @@ bool Codegen::visit(SwitchStatement *ast) bool Codegen::visit(ThrowStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); - if (hasError) + if (hasError()) return false; expr.loadInAccumulator(); @@ -3612,7 +3644,7 @@ void Codegen::handleTryFinally(TryStatement *ast) bool Codegen::visit(TryStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); @@ -3628,7 +3660,7 @@ bool Codegen::visit(TryStatement *ast) bool Codegen::visit(VariableStatement *ast) { - if (hasError) + if (hasError()) return false; variableDeclarationList(ast->declarations); @@ -3637,7 +3669,7 @@ bool Codegen::visit(VariableStatement *ast) bool Codegen::visit(WhileStatement *ast) { - if (hasError) + if (hasError()) return false; if (AST::cast<FalseLiteral *>(ast->expression)) @@ -3651,6 +3683,8 @@ bool Codegen::visit(WhileStatement *ast) ControlFlowLoop flow(this, &end, &cond); bytecodeGenerator->addLoopStart(cond); + bytecodeGenerator->checkException(); + if (!AST::cast<TrueLiteral *>(ast->expression)) { TailCallBlocker blockTailCalls(this); condition(ast->expression, &start, &end, true); @@ -3667,14 +3701,14 @@ bool Codegen::visit(WhileStatement *ast) bool Codegen::visit(WithStatement *ast) { - if (hasError) + if (hasError()) return false; RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference src = expression(ast->expression); - if (hasError) + if (hasError()) return false; src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place src.loadInAccumulator(); @@ -3744,52 +3778,79 @@ bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, return isArgOrEval; } -void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +void Codegen::throwError(ErrorType errorType, const SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; - QQmlJS::DiagnosticMessage error; - error.message = detail; - error.loc = loc; - _errors << error; + _errorType = errorType; + _error.message = detail; + _error.line = loc.startLine; + _error.column = loc.startColumn; } -void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { - if (hasError) - return; - - hasError = true; - QQmlJS::DiagnosticMessage error; - error.message = detail; - error.loc = loc; - _errors << error; + throwError(SyntaxError, loc, detail); } -QList<QQmlJS::DiagnosticMessage> Codegen::errors() const +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) { - return _errors; + throwError(ReferenceError, loc, detail); } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData) +QQmlJS::DiagnosticMessage Codegen::error() const { - CompiledData::Unit *unitData = nullptr; - if (generateUnitData) - unitData = jsUnitGenerator->generateUnit(); - CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData); + return _error; +} - QQmlRefPointer<CompiledData::CompilationUnit> unit; - unit.adopt(compilationUnit); - return unit; +QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit( + bool generateUnitData) +{ + return QV4::CompiledData::CompilationUnit( + generateUnitData ? jsUnitGenerator->generateUnit() : nullptr); } -QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() +CompiledData::CompilationUnit Codegen::compileModule( + bool debugMode, const QString &url, const QString &sourceCode, + const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) { - QQmlRefPointer<CompiledData::CompilationUnit> result; - result.adopt(new CompiledData::CompilationUnit); - return result; + QQmlJS::Engine ee; + QQmlJS::Lexer lexer(&ee); + lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false); + QQmlJS::Parser parser(&ee); + + const bool parsed = parser.parseModule(); + + if (diagnostics) + *diagnostics = parser.diagnosticMessages(); + + if (!parsed) + return CompiledData::CompilationUnit(); + + QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); + if (!moduleNode) { + // if parsing was successful, and we have no module, then + // the file was empty. + if (diagnostics) + diagnostics->clear(); + return nullptr; + } + + using namespace QV4::Compiler; + Compiler::Module compilerModule(debugMode); + compilerModule.unitFlags |= CompiledData::Unit::IsESModule; + compilerModule.sourceTimeStamp = sourceTimeStamp; + JSUnitGenerator jsGenerator(&compilerModule); + Codegen cg(&jsGenerator, /*strictMode*/true); + cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule); + if (cg.hasError()) { + if (diagnostics) + *diagnostics << cg.error(); + return CompiledData::CompilationUnit(); + } + + return cg.generateCompilationUnit(); } class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor @@ -3905,34 +3966,11 @@ Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node return scanner.scan(ast); } - -#ifndef V4_BOOTSTRAP - -QList<QQmlError> Codegen::qmlErrors() const +QUrl Codegen::url() const { - QList<QQmlError> qmlErrors; - - // Short circuit to avoid costly (de)heap allocation of QUrl if there are no errors. - if (_errors.size() == 0) - return qmlErrors; - - qmlErrors.reserve(_errors.size()); - - QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); - for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) { - QQmlError e; - e.setUrl(url); - e.setLine(msg.loc.startLine); - e.setColumn(msg.loc.startColumn); - e.setDescription(msg.message); - qmlErrors << e; - } - - return qmlErrors; + return QUrl(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); } -#endif // V4_BOOTSTRAP - bool Codegen::RValue::operator==(const RValue &other) const { switch (type) { @@ -4222,7 +4260,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addTracingInstruction(store); + codegen->bytecodeGenerator->addInstruction(store); } return; case Invalid: case Accumulator: @@ -4280,7 +4318,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str Instruction::LoadUndefined load; codegen->bytecodeGenerator->addInstruction(load); } else { - Value p = Value::fromReturnedValue(constant); + StaticValue p = StaticValue::fromReturnedValue(constant); if (p.isNumber()) { double d = p.asDouble(); int i = static_cast<int>(d); @@ -4291,7 +4329,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str return; } Instruction::LoadInt load; - load.value = Value::fromReturnedValue(constant).toInt32(); + load.value = StaticValue::fromReturnedValue(constant).toInt32(); codegen->bytecodeGenerator->addInstruction(load); return; } @@ -4312,12 +4350,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4341,16 +4379,16 @@ QT_WARNING_POP if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Member: @@ -4359,11 +4397,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Import: { @@ -4378,7 +4416,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Invalid: break; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 958dd16816..82a4fc3289 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -50,19 +50,18 @@ // We mean it. // -#include "private/qv4global_p.h" #include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsengine_p.h> #include <private/qqmljsast_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <private/qv4compiler_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4util_p.h> #include <private/qv4bytecodegenerator_p.h> -#include <private/qv4stackframe_p.h> +#include <private/qv4calldata_p.h> QT_BEGIN_NAMESPACE -using namespace QQmlJS; - namespace QV4 { namespace Moth { @@ -79,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; @@ -91,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: @@ -256,7 +255,8 @@ public: } static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { Reference r(cg, StackSlot); - r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); + r.theStackSlot = Moth::StackSlot::createRegister( + index + sizeof(CallData) / sizeof(StaticValue) - 1); r.stackSlotIsLocalOrArgument = true; r.isVolatile = isVolatile; return r; @@ -484,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(); @@ -518,20 +518,20 @@ 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) + if (!ast || hasError()) return Reference(); pushExpr(name); @@ -539,160 +539,173 @@ protected: return popResult(); } - inline void accept(AST::Node *node) + inline void accept(QQmlJS::AST::Node *node) { - if (!hasError && 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")); } public: - QList<DiagnosticMessage> errors() const; -#ifndef V4_BOOTSTRAP - QList<QQmlError> qmlErrors() const; -#endif + enum ErrorType { + NoError, + SyntaxError, + ReferenceError + }; + + ErrorType errorType() const { return _errorType; } + bool hasError() const { return _errorType != NoError; } + 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()); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true); - static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading(); + QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true); + static QV4::CompiledData::CompilationUnit compileModule( + bool debugMode, const QString &url, const QString &sourceCode, + const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); Context *currentContext() const { return _context; } BytecodeGenerator *generator() const { return bytecodeGenerator; } @@ -751,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; @@ -767,8 +780,8 @@ protected: ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; - bool hasError; - QList<QQmlJS::DiagnosticMessage> _errors; + ErrorType _errorType = NoError; + QQmlJS::DiagnosticMessage _error; class TailCallBlocker { @@ -795,8 +808,10 @@ protected: }; private: - VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast); - void handleConstruct(const Reference &base, AST::ArgumentList *args); + 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 01c033cb2a..acc4b02e96 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -38,16 +38,24 @@ ****************************************************************************/ #include <qv4compiler_p.h> -#include <qv4compileddata_p.h> #include <qv4codegen_p.h> -#include <private/qv4string_p.h> -#include <private/qv4value_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 <wtf/MathExtras.h> +#include <private/qml_compile_hash_p.h> +#include <private/qqmlirbuilder_p.h> #include <QCryptographicHash> +// Efficient implementation that takes advantage of powers of two. +static inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + Q_ASSERT(divisor && !(divisor & (divisor - 1))); + const size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} + QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -92,7 +100,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast<char *>(unit); quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable); - char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); + char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); for (int i = backingUnitTableSize ; i < strings.size(); ++i) { const int index = i - backingUnitTableSize; stringTable[index] = stringData - dataStart; @@ -119,6 +127,25 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) } } +void QV4::Compiler::JSUnitGenerator::generateUnitChecksum(QV4::CompiledData::Unit *unit) +{ +#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + QCryptographicHash hash(QCryptographicHash::Md5); + + const int checksummableDataOffset + = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(unit->md5Checksum); + + const char *dataPtr = reinterpret_cast<const char *>(unit) + checksummableDataOffset; + hash.addData(dataPtr, unit->unitSize - checksummableDataOffset); + + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum)); + memcpy(unit->md5Checksum, checksum.constData(), sizeof(unit->md5Checksum)); +#else + memset(unit->md5Checksum, 0, sizeof(unit->md5Checksum)); +#endif +} + QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) : module(module) { @@ -242,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)); } @@ -385,7 +415,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO if (option == GenerateWithStringTable) stringTable.serialize(unit); - unit->generateChecksum(); + generateUnitChecksum(unit); return unit; } @@ -394,7 +424,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function))); function->nameIndex = getStringId(irFunction->name); function->flags = 0; @@ -410,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; @@ -424,7 +456,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte Q_ASSERT(function->lineNumberOffset() == currentOffset); currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; if (!irFunction->labelInfo.empty()) { @@ -440,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); @@ -545,7 +578,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block))); block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone; block->nLocals = irBlock->locals.size(); @@ -575,7 +608,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; - qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash); + qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH); memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); @@ -608,7 +641,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.constantTableSize = constants.size(); // Ensure we load constants from well-aligned addresses into for example SSE registers. - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); @@ -619,19 +652,19 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.translationTableSize = translations.count(); unit.offsetToTranslationTable = nextOffset; nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) { *tableSizePtr = count; *offsetPtr = nextOffset; nextOffset += count * sizeof(CompiledData::ExportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); }; reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); @@ -641,12 +674,12 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.importEntryTableSize = module->importEntries.count(); unit.offsetToImportEntryTable = nextOffset; nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.moduleRequestTableSize = module->moduleRequests.count(); unit.offsetToModuleRequestTable = nextOffset; nextOffset += unit.moduleRequestTableSize * sizeof(uint); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); quint32 functionSize = 0; for (int i = 0; i < module->functions.size(); ++i) { @@ -686,7 +719,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp if (option == GenerateWithStringTable) { unit.stringTableSize = stringTable.stringCount(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.offsetToStringTable = nextOffset; nextOffset += stringTable.sizeOfTableAndData(); } else { diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 49e334bb81..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,9 @@ private: bool frozen = false; }; -struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { +struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { + static void generateUnitChecksum(CompiledData::Unit *unit); + struct MemberInfo { QString name; bool isAccessor; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index ca2d5128f4..88837b0feb 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -156,7 +156,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS result.isConst = false; return result; } else { - result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; + result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1; result.scope = 0; result.type = ResolvedName::Stack; result.isConst = false; @@ -410,28 +410,4 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } -bool Context::canUseTracingJit() const -{ -#if QT_CONFIG(qml_tracing) - static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING"); - if (forceTracing) //### we can probably remove this when tracing is turned on by default - return true; // to be used by unittests - - static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING"); - if (disableTracing) - return false; - - static QStringList onlyTrace = - qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts); - if (!onlyTrace.isEmpty()) - return onlyTrace.contains(name); - - //### the next condition should be refined and have the IR distinguish between escaping and - // non-escaping locals - return !requiresExecutionContext && !hasNestedFunctions; -#else - return false; -#endif -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 57ef4be36e..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 { @@ -162,7 +166,6 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; - uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -190,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; @@ -289,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; @@ -363,8 +367,6 @@ struct Context { return parent->canHaveTailCalls(); return false; } - - bool canUseTracingJit() const; }; 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/qmldirparser/qqmlsourcecoordinate_p.h b/src/qml/compiler/qv4compilerglobal_p.h index 76ac741ae8..3478074827 100644 --- a/src/qml/qmldirparser/qqmlsourcecoordinate_p.h +++ b/src/qml/compiler/qv4compilerglobal_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLSOURCECOORDINATE_P_H -#define QQMLSOURCECOORDINATE_P_H +#ifndef QV4COMPILERGLOBAL_H +#define QV4COMPILERGLOBAL_H // // W A R N I N G @@ -52,21 +52,23 @@ // #include <QtCore/qglobal.h> +#include <QString> -#include <climits> +#include <private/qtqmlcompilerglobal_p.h> QT_BEGIN_NAMESPACE -inline quint16 qmlSourceCoordinate(int n) -{ - return (n > 0 && n <= static_cast<int>(USHRT_MAX)) ? static_cast<quint16>(n) : 0; -} +namespace QV4 { -inline int qmlSourceCoordinate(quint16 n) -{ - return (n == 0) ? -1 : static_cast<int>(n); -} +enum class ObjectLiteralArgument { + Value, + Method, + Getter, + Setter +}; + +} // namespace QV4 QT_END_NAMESPACE -#endif // QQMLSOURCECOORDINATE_P_H +#endif // QV4COMPILERGLOBAL_H diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 8984b6931e..ab0ebf3d4b 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -44,18 +44,26 @@ #include <QtCore/QSet> #include <QtCore/QBuffer> #include <QtCore/QBitArray> -#include <QtCore/QLinkedList> #include <QtCore/QStack> #include <private/qqmljsast_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4codegen_p.h> -#include <private/qv4string_p.h> 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) +{ + CompiledData::Location target; + target.line = astLocation.startLine; + target.column = astLocation.startColumn; + return target; +} + + ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType) : QQmlJS::AST::Visitor(cg->recursionDepth()) , _cg(cg) @@ -177,7 +185,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); entry.importName = QStringLiteral("*"); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->exportEntries << entry; } else if (declaration->exportClause) { for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) { @@ -190,22 +198,22 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) entry.moduleRequest = module; entry.exportName = spec->exportedIdentifier.toString(); - entry.location = it->firstSourceLocation(); + entry.location = location(it->firstSourceLocation()); _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.location = vstmt->firstSourceLocation(); + entry.localName = name.id; + entry.exportName = name.id; + entry.location = location(vstmt->firstSourceLocation()); _context->exportEntries << entry; } } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(declaration->variableStatementOrDeclaration)) { @@ -214,7 +222,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.localName = name; entry.exportName = name; - entry.location = classDecl->firstSourceLocation(); + entry.location = location(classDecl->firstSourceLocation()); _context->exportEntries << entry; if (declaration->exportDefault) localNameForDefaultExport = entry.localName; @@ -233,7 +241,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) Compiler::ExportEntry entry; entry.localName = functionName; entry.exportName = functionName; - entry.location = fdef->firstSourceLocation(); + entry.location = location(fdef->firstSourceLocation()); _context->exportEntries << entry; if (declaration->exportDefault) localNameForDefaultExport = entry.localName; @@ -245,7 +253,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) entry.localName = localNameForDefaultExport; _context->localNameForDefaultExport = localNameForDefaultExport; entry.exportName = QStringLiteral("default"); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->exportEntries << entry; } @@ -270,7 +278,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.moduleRequest = module; entry.importName = QStringLiteral("default"); entry.localName = import->importedDefaultBinding.toString(); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } @@ -279,7 +287,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.moduleRequest = module; entry.importName = QStringLiteral("*"); entry.localName = import->nameSpaceImport->importedBinding.toString(); - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } @@ -292,7 +300,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) entry.importName = it->importSpecifier->identifier.toString(); else entry.importName = entry.localName; - entry.location = declaration->firstSourceLocation(); + entry.location = location(declaration->firstSourceLocation()); _context->importEntries << entry; } } @@ -319,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; } } @@ -672,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(); } @@ -684,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) { @@ -705,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..2de80eac44 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::FunctionExpression *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.cpp b/src/qml/compiler/qv4instr_moth.cpp index 345f03ae8a..640a908dd3 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -39,7 +39,9 @@ #include "qv4instr_moth_p.h" #include <private/qv4compileddata_p.h> -#include <private/qv4stackframe_p.h> +#include <private/qv4calldata_p.h> + +#include <QtCore/qdebug.h> using namespace QV4; using namespace QV4::Moth; @@ -56,9 +58,7 @@ int InstrInfo::size(Instr::Type type) static QByteArray alignedNumber(int n) { QByteArray number = QByteArray::number(n); - while (number.size() < 8) - number.prepend(' '); - return number; + return number.prepend(8 - number.size(), ' '); } static QByteArray alignedLineNumber(int line) { @@ -83,25 +83,6 @@ static QByteArray rawBytes(const char *data, int n) return ba; } -static QString toString(QV4::ReturnedValue v) -{ -#ifdef V4_BOOTSTRAP - return QStringLiteral("string-const(%1)").arg(v); -#else // !V4_BOOTSTRAP - Value val = Value::fromReturnedValue(v); - QString result; - if (val.isInt32()) - result = QLatin1String("int "); - else if (val.isDouble()) - result = QLatin1String("double "); - if (val.isEmpty()) - result += QLatin1String("empty"); - else - result += val.toQStringNoThrow(); - return result; -#endif // V4_BOOTSTRAP -} - #define ABSOLUTE_OFFSET() \ (code - start + offset) @@ -128,22 +109,12 @@ const int InstrInfo::argumentCount[] = { FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS) }; - -void dumpConstantTable(const Value *constants, uint count) -{ - QDebug d = qDebug(); - d.nospace(); - for (uint i = 0; i < count; ++i) - d << alignedNumber(int(i)).constData() << ": " - << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; -} - QString dumpRegister(int reg, int nFormals) { Q_STATIC_ASSERT(offsetof(CallData, function) == 0); - Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(StaticValue)); if (reg == CallData::Function) return QStringLiteral("(function)"); else if (reg == CallData::Context) @@ -171,8 +142,6 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot) - void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) { MOTH_JUMP_TABLE; @@ -241,9 +210,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index << TRACE_SLOT; + d << "l" << index; else - d << "a" << (index - nLocals) << TRACE_SLOT; + d << "a" << (index - nLocals); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -255,9 +224,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope << TRACE_SLOT; + d << "l" << index << "@" << scope; else - d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; + d << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -280,15 +249,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name << TRACE_SLOT; + d << name; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -300,20 +269,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]" << TRACE_SLOT; + d << "acc[" << name << "]"; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")" << TRACE_SLOT; + d << "acc(" << index << ")"; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -343,49 +311,48 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + ; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) d << dumpRegister(base, nFormals) << "." << lookupIndex - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) @@ -528,11 +495,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -543,6 +510,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpNoException) + MOTH_BEGIN_INSTR(CheckException) + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) MOTH_END_INSTR(CmpEqNull) @@ -596,22 +566,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - d << TRACE_SLOT; MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(Increment) - d << TRACE_SLOT; MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - d << TRACE_SLOT; MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -667,7 +634,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -675,11 +642,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 26901c297c..c0dd696b8a 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -50,9 +50,8 @@ // // We mean it. // -#include <private/qv4global_p.h> -#include <private/qv4value_p.h> -#include <private/qv4runtime_p.h> + +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper #include <qendian.h> @@ -77,20 +76,20 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) #define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) -#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) #define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) #define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) #define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg) #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) -#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) -#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -100,18 +99,18 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) -#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) -#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) -#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot) -#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot) -#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot) -#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) -#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) -#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) +#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) #define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) @@ -148,10 +147,11 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) #define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) -#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) #define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) #define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) +#define INSTR_CheckException(op) INSTRUCTION(op, CheckException, 0) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) #define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) #define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) @@ -168,11 +168,11 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) #define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot) -#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot) -#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) #define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) #define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) #define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) @@ -186,10 +186,10 @@ QT_BEGIN_NAMESPACE #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) #define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) -#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -241,6 +241,7 @@ QT_BEGIN_NAMESPACE F(JumpFalse) \ F(JumpNoException) \ F(JumpNotUndefined) \ + F(CheckException) \ F(CmpEqNull) \ F(CmpNeNull) \ F(CmpEqInt) \ @@ -532,7 +533,6 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h -void dumpConstantTable(const Value *constants, uint count); void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()); inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/compiler/qv4util_p.h index 073832937d..bd9758c1fb 100644 --- a/src/qml/jsruntime/qv4util_p.h +++ b/src/qml/compiler/qv4util_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include "qv4global_p.h" #include <QtCore/QBitArray> #include <algorithm> #include <vector> diff --git a/src/qml/configure.json b/src/qml/configure.json index c35f5be06b..3fc1fd528b 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -8,7 +8,6 @@ "commandline": { "options": { "qml-network": "boolean", - "qml-tracing": "boolean", "qml-debug": "boolean" } }, @@ -24,6 +23,52 @@ ], "qmake": "CONFIG += c++11" } + }, + "pointer_32bit": { + "label": "32bit pointers", + "type": "compile", + "test": { + "main": "static_assert(sizeof(void *) == 4, \"fail\");" + } + }, + "pointer_64bit": { + "label": "64bit pointers", + "type": "compile", + "test": { + "main": "static_assert(sizeof(void *) == 8, \"fail\");" + } + }, + "arm_thumb": { + "label": "THUMB mode on ARM", + "type": "compile", + "test": { + "main": [ + "#if defined(thumb2) || defined(__thumb2__)", + "# define THUMB_OK", + "#elif (defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4", + "# define THUMB_OK", + "#elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2", + "// clang 3.5 and later will set this if the core supports the Thumb-2 ISA.", + "# define THUMB_OK", + "#else", + "# error \"fail\"", + "#endif" + ] + } + }, + "arm_fp": { + "label": "Sufficiently recent FPU on ARM", + "type": "compile", + "test": { + "main": [ + "// if !defined(__ARM_FP) we might be on MSVC or we might have a device", + "// without an FPU.", + "// TODO: The latter case is not supported, but the test still succeeds.", + "#if defined(__ARM_FP) && (__ARM_FP <= 0x04)", + "# error \"fail\"", + "#endif" + ] + } } }, @@ -40,12 +85,26 @@ "condition": "features.network", "output": [ "publicFeature" ] }, - "qml-tracing": { - "label": "QML tracing JIT support", - "purpose": "Provides a JIT that uses trace information generated by the interpreter.", + "qml-jit": { + "label": "QML just-in-time compiler", + "purpose": "Provides a JIT for QML and JavaScript", "section": "QML", + "condition": [ + " (arch.i386 && tests.pointer_32bit && features.sse2) + || (arch.x86_64 && tests.pointer_64bit && features.sse2) + || (arch.arm && tests.pointer_32bit && tests.arm_fp && tests.arm_thumb + && (config.linux || config.ios || config.tvos || config.qnx)) + || (arch.arm64 && tests.pointer_64bit && tests.arm_fp + && (config.linux || config.ios || config.tvos || config.qnx || config.integrity))" + ], "output": [ "privateFeature" ], - "autoDetect": false + "autoDetect": "!config.ios && !config.tvos", + "comment": "On arm and arm64 we need a specialization of cacheFlush() for each OS to be + enabeled. Therefore the config white list. + Also Mind that e.g. x86_32 has arch.x86_64 but 32bit pointers. Therefore + the checks for architecture and pointer size. + Finally, ios and tvos can technically use the JIT but Apple does not allow + it. Therefore, it's disabled by default." }, "qml-debug": { "label": "QML debugging and profiling support", @@ -90,12 +149,6 @@ "section": "QML", "output": [ "privateFeature" ] }, - "qml-list-model": { - "label": "QML list model", - "purpose": "Provides the ListModel QML type.", - "section": "QML", - "output": [ "privateFeature" ] - }, "qml-xml-http-request": { "label": "QML XML http request", "purpose": "Provides support for sending XML http requests.", @@ -119,18 +172,19 @@ "condition": "features.animation", "output": [ "privateFeature" ] }, - "qml-delegate-model": { - "label": "QML delegate model", - "purpose": "Provides the DelegateModel QML type.", - "section": "QML", - "output": [ "privateFeature" ] - }, "qml-worker-script": { "label": "QML WorkerScript", "purpose": "Enables the use of threads in QML.", "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" ] } }, @@ -140,12 +194,10 @@ "entries": [ "qml-network", "qml-debug", - "qml-tracing", + "qml-jit", "qml-sequence-object", - "qml-list-model", "qml-xml-http-request", - "qml-locale", - "qml-delegate-model" + "qml-locale" ] } ] diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 202a46e550..1281886816 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -14,7 +14,6 @@ qtConfig(qml-debug) { $$PWD/qqmldebugconnector.cpp \ $$PWD/qqmldebugservice.cpp \ $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlprofiler.cpp \ $$PWD/qqmldebugserviceinterfaces.cpp } @@ -24,7 +23,6 @@ HEADERS += \ $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ $$PWD/qqmldebug.h \ - $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlprofiler_p.h INCLUDEPATH += $$PWD diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p.h b/src/qml/debugger/qqmlconfigurabledebugservice_p.h index 96ec46f475..38f41047c0 100644 --- a/src/qml/debugger/qqmlconfigurabledebugservice_p.h +++ b/src/qml/debugger/qqmlconfigurabledebugservice_p.h @@ -64,7 +64,7 @@ class QQmlConfigurableDebugService : public Base { protected: QQmlConfigurableDebugService(float version, QObject *parent = nullptr) : - Base(version, parent), m_configMutex(QMutex::Recursive) + Base(version, parent) { init(); } @@ -103,7 +103,7 @@ protected: emit Base::attachedToEngine(engine); } - QMutex m_configMutex; + QRecursiveMutex m_configMutex; QList<QJSEngine *> m_waitingEngines; bool m_waitingForConfiguration; }; diff --git a/src/qml/debugger/qqmlmemoryprofiler.cpp b/src/qml/debugger/qqmlmemoryprofiler.cpp deleted file mode 100644 index b89dbfd02d..0000000000 --- a/src/qml/debugger/qqmlmemoryprofiler.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmlmemoryprofiler_p.h" - -QT_BEGIN_NAMESPACE - - -QQmlMemoryScope::LibraryState QQmlMemoryScope::state = QQmlMemoryScope::Unloaded; - -typedef void (qmlmemprofile_stats)(int *allocCount, int *bytesAllocated); -typedef void (qmlmemprofile_clear)(); -typedef void (qmlmemprofile_enable)(); -typedef void (qmlmemprofile_disable)(); -typedef void (qmlmemprofile_push_location)(const char *filename, int lineNumber); -typedef void (qmlmemprofile_pop_location)(); -typedef void (qmlmemprofile_save)(const char *filename); -typedef int (qmlmemprofile_is_enabled)(); - -static qmlmemprofile_stats *memprofile_stats; -static qmlmemprofile_clear *memprofile_clear; -static qmlmemprofile_enable *memprofile_enable; -static qmlmemprofile_disable *memprofile_disable; -static qmlmemprofile_push_location *memprofile_push_location; -static qmlmemprofile_pop_location *memprofile_pop_location; -static qmlmemprofile_save *memprofile_save; -static qmlmemprofile_is_enabled *memprofile_is_enabled; - -#if QT_CONFIG(library) -extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); -#endif - -bool QQmlMemoryScope::doOpenLibrary() -{ -#if defined(Q_OS_LINUX) && QT_CONFIG(library) - if (state == Unloaded) { - memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats"); - memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear"); - memprofile_enable = (qmlmemprofile_enable *) qt_linux_find_symbol_sys("qmlmemprofile_enable"); - memprofile_disable = (qmlmemprofile_disable *) qt_linux_find_symbol_sys("qmlmemprofile_disable"); - memprofile_push_location = (qmlmemprofile_push_location *) qt_linux_find_symbol_sys("qmlmemprofile_push_location"); - memprofile_pop_location = (qmlmemprofile_pop_location *) qt_linux_find_symbol_sys("qmlmemprofile_pop_location"); - memprofile_save = (qmlmemprofile_save *) qt_linux_find_symbol_sys("qmlmemprofile_save"); - memprofile_is_enabled = (qmlmemprofile_is_enabled *) qt_linux_find_symbol_sys("qmlmemprofile_is_enabled"); - - if (memprofile_stats && memprofile_clear && memprofile_enable && memprofile_disable && - memprofile_push_location && memprofile_pop_location && memprofile_save && memprofile_is_enabled) - state = Loaded; - else - state = Failed; - } -#endif // Q_OS_LINUX - - return state == Loaded; -} - -void QQmlMemoryScope::init(const char *string) -{ - if (memprofile_is_enabled()) { - memprofile_push_location(string, 0); - pushed = true; - } -} - -void QQmlMemoryScope::done() -{ - memprofile_pop_location(); -} - -bool QQmlMemoryProfiler::isEnabled() -{ - if (QQmlMemoryScope::openLibrary()) - return memprofile_is_enabled(); - - return false; -} - -void QQmlMemoryProfiler::enable() -{ - if (QQmlMemoryScope::openLibrary()) - memprofile_enable(); -} - -void QQmlMemoryProfiler::disable() -{ - if (QQmlMemoryScope::openLibrary()) - memprofile_disable(); -} - -void QQmlMemoryProfiler::clear() -{ - if (QQmlMemoryScope::openLibrary()) - memprofile_clear(); -} - -void QQmlMemoryProfiler::stats(int *allocCount, int *bytesAllocated) -{ - if (QQmlMemoryScope::openLibrary()) - memprofile_stats(allocCount, bytesAllocated); -} - -void QQmlMemoryProfiler::save(const char *filename) -{ - if (QQmlMemoryScope::openLibrary()) - memprofile_save(filename); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index d01e2bc429..d3eedab1c6 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -171,10 +171,11 @@ public: : Location(ref->sourceLocation()), locationType(Binding), sent(false) { function = ref; - function->compilationUnit->addref(); + function->executableCompilationUnit()->addref(); } - RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) + RefLocation(QV4::ExecutableCompilationUnit *ref, const QUrl &url, + const QV4::CompiledData::Object *obj, const QString &type) : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), locationType(Creating), sent(false) { @@ -230,7 +231,7 @@ public: switch (locationType) { case Binding: - function->compilationUnit->addref(); + function->executableCompilationUnit()->addref(); break; case Creating: unit->addref(); @@ -254,7 +255,7 @@ public: switch (locationType) { case Binding: - function->compilationUnit->release(); + function->executableCompilationUnit()->release(); break; case Creating: unit->release(); @@ -284,7 +285,7 @@ public: RangeType locationType; union { QV4::Function *function; - QV4::CompiledData::CompilationUnit *unit; + QV4::ExecutableCompilationUnit *unit; QQmlBoundSignalExpression *boundSignal; QQmlDataBlob *blob; void *something; @@ -356,7 +357,7 @@ public: } void updateCreating(const QV4::CompiledData::Object *obj, - QV4::CompiledData::CompilationUnit *ref, + QV4::ExecutableCompilationUnit *ref, const QUrl &url, const QString &type) { quintptr locationId(id(obj)); @@ -492,7 +493,7 @@ public: Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange<QQmlProfilerDefinitions::Creating>()); } - void update(QV4::CompiledData::CompilationUnit *ref, const QV4::CompiledData::Object *obj, + void update(QV4::ExecutableCompilationUnit *ref, const QV4::CompiledData::Object *obj, const QString &typeName, const QUrl &url) { profiler->updateCreating(obj, ref, url, typeName); diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf index cb9fb575b2..74d0a3b27c 100644 --- a/src/qml/doc/qtqml.qdocconf +++ b/src/qml/doc/qtqml.qdocconf @@ -37,11 +37,17 @@ tagfile = ../../../doc/qtqml/qtqml.tags depends += qtcore qtgui qtquick qtdoc qtlinguist qmake qtscript qtwidgets qtxmlpatterns qtquickcontrols headerdirs += .. \ - ../../imports/models + ../../imports/models \ + ../../qmlmodels \ + ../../qml \ + ../../qmlworkerscript sourcedirs += .. \ ../../imports/models \ - ../../imports/statemachine + ../../imports/statemachine \ + ../../qmlmodels \ + ../../qml \ + ../../qmlworkerscript exampledirs += ../../../examples/qml \ ../ \ diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel.qml b/src/qml/doc/snippets/delegatemodel/delegatemodel.qml deleted file mode 100644 index 1a7baa6b1e..0000000000 --- a/src/qml/doc/snippets/delegatemodel/delegatemodel.qml +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//![0] -import QtQuick 2.0 -import QtQml.Models 2.2 - -Rectangle { - width: 200; height: 100 - - DelegateModel { - id: visualModel - model: ListModel { - ListElement { name: "Apple" } - ListElement { name: "Orange" } - } - delegate: Rectangle { - height: 25 - width: 100 - Text { text: "Name: " + name} - } - } - - ListView { - anchors.fill: parent - model: visualModel - } -} -//![0] diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp deleted file mode 100644 index a56eb69616..0000000000 --- a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <QQuickView> -#include <QQmlContext> - -#include <QApplication> -#include <QDirModel> - -//![0] -int main(int argc, char ** argv) -{ - QApplication app(argc, argv); - - QQuickView view; - - QDirModel model; - view.rootContext()->setContextProperty("dirModel", &model); - - view.setSource(QUrl::fromLocalFile("view.qml")); - view.show(); - - return app.exec(); -} -//![0] - diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml deleted file mode 100644 index 2e17eed8f0..0000000000 --- a/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//![0] -import QtQuick 2.0 -import QtQml.Models 2.2 - -ListView { - id: view - width: 300 - height: 400 - - model: DelegateModel { - model: dirModel - - delegate: Rectangle { - width: 200; height: 25 - Text { text: filePath } - - MouseArea { - anchors.fill: parent - onClicked: { - if (model.hasModelChildren) - view.model.rootIndex = view.model.modelIndex(index) - } - } - } - } -} -//![0] diff --git a/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml b/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml deleted file mode 100644 index 8562deeeda..0000000000 --- a/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//![0] -import QtQuick 2.0 -import QtQml.Models 2.2 - -Rectangle { - width: 200; height: 100 - - DelegateModel { - id: visualModel - model: ListModel { - ListElement { name: "Apple" } - ListElement { name: "Orange" } - } - - groups: [ - DelegateModelGroup { name: "selected" } - ] - - delegate: Rectangle { - id: item - height: 25 - width: 200 - Text { - text: { - var text = "Name: " + name - if (item.DelegateModel.inSelected) - text += " (" + item.DelegateModel.selectedIndex + ")" - return text; - } - } - MouseArea { - anchors.fill: parent - onClicked: item.DelegateModel.inSelected = !item.DelegateModel.inSelected - } - } - } - - ListView { - anchors.fill: parent - model: visualModel - } -} -//![0] diff --git a/src/qml/doc/snippets/package/Delegate.qml b/src/qml/doc/snippets/package/Delegate.qml deleted file mode 100644 index 7c73f35c3d..0000000000 --- a/src/qml/doc/snippets/package/Delegate.qml +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -//! [0] -Package { - Text { id: listDelegate; width: parent.width; height: 25; text: 'Empty'; Package.name: 'list' } - Text { id: gridDelegate; width: parent.width / 2; height: 50; text: 'Empty'; Package.name: 'grid' } - - Rectangle { - id: wrapper - width: parent.width; height: 25 - color: 'lightsteelblue' - - Text { text: display; anchors.centerIn: parent } - state: root.upTo > index ? 'inGrid' : 'inList' - states: [ - State { - name: 'inList' - ParentChange { target: wrapper; parent: listDelegate } - }, - State { - name: 'inGrid' - ParentChange { - target: wrapper; parent: gridDelegate - x: 0; y: 0; width: gridDelegate.width; height: gridDelegate.height - } - } - ] - - transitions: [ - Transition { - ParentAnimation { - NumberAnimation { properties: 'x,y,width,height'; duration: 300 } - } - } - ] - } -} -//! [0] diff --git a/src/qml/doc/snippets/package/view.qml b/src/qml/doc/snippets/package/view.qml deleted file mode 100644 index 311cc3be8e..0000000000 --- a/src/qml/doc/snippets/package/view.qml +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtQml.Models 2.1 - -Rectangle { - id: root - color: "white" - width: 320 - height: 480 - property int upTo: 0 - SequentialAnimation on upTo { - loops: -1 - NumberAnimation { to: 8; duration: 3500 } - NumberAnimation { to: 0; duration: 3500 } - } - - ListModel { - id: myModel - ListElement { display: "One" } - ListElement { display: "Two" } - ListElement { display: "Three" } - ListElement { display: "Four" } - ListElement { display: "Five" } - ListElement { display: "Six" } - ListElement { display: "Seven" } - ListElement { display: "Eight" } - } - //![0] - DelegateModel { - id: visualModel - delegate: Delegate {} - model: myModel - } - - ListView { - id: lv - height: parent.height/2 - width: parent.width - - model: visualModel.parts.list - } - GridView { - y: parent.height/2 - height: parent.height/2 - width: parent.width - cellWidth: width / 2 - cellHeight: 50 - model: visualModel.parts.grid - } - //![0] - Text { - anchors.bottom: parent.bottom - } -} 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/listmodel/listelements.qml b/src/qml/doc/snippets/qml/listmodel/listelements.qml deleted file mode 100644 index 12146c1420..0000000000 --- a/src/qml/doc/snippets/qml/listmodel/listelements.qml +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -//! [document] -import QtQuick 2.0 - -Item { - width: 200; height: 250 - - //! [model] - ListModel { - id: fruitModel - - ListElement { - name: "Apple" - cost: 2.45 - } - ListElement { - name: "Orange" - cost: 3.25 - } - ListElement { - name: "Banana" - cost: 1.95 - } - } - //! [model] - - //! [view] - ListView { - anchors.fill: parent - model: fruitModel - delegate: Row { - Text { text: "Fruit: " + name } - Text { text: "Cost: $" + cost } - } - } - //! [view] -} -//! [document] diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml b/src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml deleted file mode 100644 index f293eff8ec..0000000000 --- a/src/qml/doc/snippets/qml/listmodel/listmodel-modify.qml +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.0 - -Rectangle { - width: 200; height: 200 - -ListModel { - id: fruitModel - - ListElement { - name: "Apple" - cost: 2.45 - attributes: [ - ListElement { description: "Core" }, - ListElement { description: "Deciduous" } - ] - } - ListElement { - name: "Orange" - cost: 3.25 - attributes: [ - ListElement { description: "Citrus" } - ] - } - ListElement { - name: "Banana" - cost: 1.95 - attributes: [ - ListElement { description: "Tropical" }, - ListElement { description: "Seedless" } - ] - } -} - -//![delegate] - Component { - id: fruitDelegate - Item { - width: 200; height: 50 - Text { text: name } - Text { text: '$' + cost; anchors.right: parent.right } - - // Double the price when clicked. - MouseArea { - anchors.fill: parent - onClicked: fruitModel.setProperty(index, "cost", cost * 2) - } - } - } -//![delegate] - -ListView { - width: 200; height: 200 - model: fruitModel - delegate: fruitDelegate -} - -} diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml b/src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml deleted file mode 100644 index 8c193d6a5e..0000000000 --- a/src/qml/doc/snippets/qml/listmodel/listmodel-nested.qml +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.0 - -Rectangle { - width: 200; height: 200 - - -//![model] -ListModel { - id: fruitModel - - ListElement { - name: "Apple" - cost: 2.45 - attributes: [ - ListElement { description: "Core" }, - ListElement { description: "Deciduous" } - ] - } - ListElement { - name: "Orange" - cost: 3.25 - attributes: [ - ListElement { description: "Citrus" } - ] - } - ListElement { - name: "Banana" - cost: 1.95 - attributes: [ - ListElement { description: "Tropical" }, - ListElement { description: "Seedless" } - ] - } -} -//![model] - -//![delegate] -Component { - id: fruitDelegate - Item { - width: 200; height: 50 - Text { id: nameField; text: name } - Text { text: '$' + cost; anchors.left: nameField.right } - Row { - anchors.top: nameField.bottom - spacing: 5 - Text { text: "Attributes:" } - Repeater { - model: attributes - Text { text: description } - } - } - } -} -//![delegate] - -ListView { - width: 200; height: 200 - model: fruitModel - delegate: fruitDelegate -} - -} diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml b/src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml deleted file mode 100644 index d07f868476..0000000000 --- a/src/qml/doc/snippets/qml/listmodel/listmodel-simple.qml +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//![0] -import QtQuick 2.0 - -Rectangle { - width: 200; height: 200 - - ListModel { - id: fruitModel -//![0] - ListElement { - name: "Apple" - cost: 2.45 - } - ListElement { - name: "Orange" - cost: 3.25 - } - ListElement { - name: "Banana" - cost: 1.95 - } -//![1] - } - - Component { - id: fruitDelegate - Row { - spacing: 10 - Text { text: name } - Text { text: '$' + cost } - } - } - - ListView { - anchors.fill: parent - model: fruitModel - delegate: fruitDelegate - } -} -//![1] diff --git a/src/qml/doc/snippets/qml/listmodel/listmodel.qml b/src/qml/doc/snippets/qml/listmodel/listmodel.qml deleted file mode 100644 index c2a69d6e8f..0000000000 --- a/src/qml/doc/snippets/qml/listmodel/listmodel.qml +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//![0] -import QtQuick 2.0 - -ListModel { - id: fruitModel - - ListElement { - name: "Apple" - cost: 2.45 - } - ListElement { - name: "Orange" - cost: 3.25 - } - ListElement { - name: "Banana" - cost: 1.95 - } -} -//![0] 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/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 969dd51433..b2d322465d 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -274,17 +274,16 @@ */ /*! - \fn int qmlRegisterType() - \relates QQmlEngine - \overload + \fn int qmlRegisterAnonymousType(const char *uri, int versionMajor) - This template function registers the C++ type in the QML - system. Instances of this type cannot be created from the QML - system. + This template function registers the C++ type in the QML system as an anonymous type. The + resulting QML type does not have a name. Therefore, instances of this type cannot be created from + the QML system. You can, however, access instances of the type when they are exposed as properties + of other types. - This function should be used when the type will not be referenced by name. - Specifically, it has to be used for C++ types that are used as the left-hand - side of a property binding. + Use this function when the type will not be referenced by name, specifically for C++ types that + are used on the left-hand side of a property binding. To indicate to which module the type belongs + use \a uri and \a versionMajor. For example, consider the following two classes: @@ -344,18 +343,29 @@ \code qmlRegisterType<Foo>("App", 1, 0, "Foo"); - qmlRegisterType<Bar>(); + qmlRegisterAnonymousType<Bar>("App", 1); \endcode As the \c Foo type is instantiated in QML, it must be registered - with the version of \l qmlRegisterType() that takes an import URI. + with the version of \l qmlRegisterType() that takes an element name. Returns the QML type id. + \since 5.14 \sa {Choosing the Correct Integration Method Between C++ and QML} */ /*! + \fn int qmlRegisterType() + \relates QQmlEngine + \overload + \deprecated + + Do not use this function. For anonymous type registrations, use \l qmlRegisterAnonymousType(), + and make sure to provide a URI and a major version. +*/ + +/*! \fn int qmlRegisterInterface(const char *typeName) \relates QQmlEngine @@ -448,7 +458,7 @@ */ /*! - \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create = true) + \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create) \relates QQmlEngine The form of this template function is: @@ -487,8 +497,7 @@ A QObject singleton type may be referenced via the type name with which it was registered, and this typename may be used as the target in a \l Connections type or otherwise used as any other type id would. - One exception to this is that a QObject singleton type property may not be aliased (because the - singleton type name does not identify an object within the same component as any other item). + One exception to this is that a QObject singleton type property may not be aliased. \b{NOTE:} A QObject singleton type instance returned from a singleton type provider is owned by the QML engine unless the object has explicit QQmlEngine::CppOwnership flag set. @@ -538,7 +547,7 @@ Alternatively, you can use a C++11 lambda: \code - qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { Q_UNUSED(engine) Q_UNUSED(scriptEngine) @@ -564,6 +573,14 @@ \sa {Choosing the Correct Integration Method Between C++ and QML} */ + +/*! + \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback) + \relates QQmlEngine + + \overload qmlRegisterSingletonType +*/ + /*! \fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) \relates QQmlEngine @@ -611,6 +628,99 @@ */ /*! + \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject* cppObject) + \relates QQmlEngine + \since 5.14 + + This function is used to register a singleton object \a cppObject, with a + particular \a uri and \a typeName. Its version is a combination of \a + versionMajor and \a versionMinor. + + Installing a singleton type into a URI allows you to provide arbitrary + functionality (methods and properties) to QML code without requiring + individual instances of the type to be instantiated by the client. + + Use this function to register an object of the given type T as a singleton + type. + + A QObject singleton type may be referenced via the type name with which it + was registered; in turn this type name may be used as the target in a \l + Connections type, or like any other type ID. However, there's one + exception: a QObject singleton type property can't be aliased because the + singleton type name does not identify an object within the same component + as any other item. + + \note \a cppObject must outlive the QML engine in which it is used. + Moreover, \cppObject must have the same thread affinity as the engine. If + you want separate singleton instances for multiple engines, you need to use + \l {qmlRegisterSingletonType}. See \l{Threads and QObjects} for more + information about thread safety. + + Usage: + \code + // First, define your QObject which provides the functionality. + class SingletonTypeExample : public QObject + { + Q_OBJECT + Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged) + + public: + explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {} + + Q_INVOKABLE int doSomething() + { + setSomeProperty(5); + return m_someProperty; + } + + int someProperty() const { return m_someProperty; } + void setSomeProperty(int val) { + if (m_someProperty != val) { + m_someProperty = val; + emit somePropertyChanged(val); + } + } + + signals: + void somePropertyChanged(int newValue); + + private: + int m_someProperty = 0; + }; + \endcode + + \code + // Second, create an instance of the object + + // allocate example before the engine to ensure that it outlives it + QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample); + QQmlEngine engine; + + // Third, register the singleton type provider with QML by calling this + // function in an initialization function. + qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get()); + \endcode + + + In order to use the registered singleton type in QML, you must import the + URI with the corresponding version. + \qml + import QtQuick 2.0 + import Qt.example.qobjectSingleton 1.0 + Item { + id: root + property int someValue: MyApi.someProperty + + Component.onCompleted: { + console.log(MyApi.doSomething()) + } + } + \endqml + + \sa qmlRegisterSingletonType + */ + +/*! \fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName); \relates QQmlEngine diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index 2c664af188..503ce0ebcd 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -2,13 +2,11 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD SOURCES += \ - $$PWD/qv4jithelpers.cpp \ $$PWD/qv4baselinejit.cpp \ $$PWD/qv4baselineassembler.cpp \ $$PWD/qv4assemblercommon.cpp HEADERS += \ - $$PWD/qv4jithelpers_p.h \ $$PWD/qv4baselinejit_p.h \ $$PWD/qv4baselineassembler_p.h \ $$PWD/qv4assemblercommon_p.h diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index dd810d9d70..800ee22cd7 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -53,8 +53,6 @@ #undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES -#ifdef V4_ENABLE_JIT - QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { @@ -366,5 +364,3 @@ void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr) } // QV4 namepsace QT_END_NAMESPACE - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h index 67c7c9cd82..b18d082be6 100644 --- a/src/qml/jit/qv4assemblercommon_p.h +++ b/src/qml/jit/qv4assemblercommon_p.h @@ -58,7 +58,7 @@ #include <wtf/Vector.h> #include <assembler/MacroAssembler.h> -#ifdef V4_ENABLE_JIT +QT_REQUIRE_CONFIG(qml_jit); QT_BEGIN_NAMESPACE @@ -449,7 +449,7 @@ public: // r6 is used by MacroAssemblerARMv7 static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8; static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10; -#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) +#if CPU(ARM_THUMB2) static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; static const RegisterID EngineRegister = JSC::ARMRegisters::r11; #else // Thumbs down @@ -619,6 +619,9 @@ public: for (Jump j : catchyJumps) j.link(this); + // We don't need to check for isInterrupted here because if that is set, + // then the first checkException() in any exception handler will find another "exception" + // and jump out of the exception handler. loadPtr(exceptionHandlerAddress(), ScratchRegister); Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0)); loadUndefined(); @@ -633,6 +636,8 @@ public: void checkException() { + // This actually reads 4 bytes, starting at hasException. + // Therefore, it also reads the isInterrupted flag, and triggers an exception on that. addCatchyJump( branch32(NotEqual, Address(EngineRegister, offsetof(EngineBase, hasException)), @@ -735,6 +740,4 @@ private: QT_END_NAMESPACE -#endif // V4_ENABLE_JIT - #endif // QV4PLATFORMASSEMBLER_P_H diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index 0751704a29..59de86a85d 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -55,8 +55,6 @@ #undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES -#ifdef V4_ENABLE_JIT - QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { @@ -943,7 +941,7 @@ void BaselineAssembler::uminus() saveAccumulatorInFrame(); pasm()->prepareCallWithArgCount(1); pasm()->passAccumulatorAsArg(0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator); checkException(); } @@ -1044,7 +1042,7 @@ void BaselineAssembler::add(int lhs) pasm()->passAccumulatorAsArg(2); pasm()->passJSSlotAsArg(lhs, 1); pasm()->passEngineAsArg(0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_add, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator); checkException(); // done. @@ -1196,7 +1194,7 @@ void BaselineAssembler::mul(int lhs) pasm()->prepareCallWithArgCount(2); pasm()->passAccumulatorAsArg(1); pasm()->passJSSlotAsArg(lhs, 0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_mul, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator); checkException(); // done. @@ -1209,7 +1207,7 @@ void BaselineAssembler::div(int lhs) pasm()->prepareCallWithArgCount(2); pasm()->passAccumulatorAsArg(1); pasm()->passJSSlotAsArg(lhs, 0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_div, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator); checkException(); } @@ -1219,7 +1217,7 @@ void BaselineAssembler::mod(int lhs) pasm()->prepareCallWithArgCount(2); pasm()->passAccumulatorAsArg(1); pasm()->passJSSlotAsArg(lhs, 0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_mod, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator); checkException(); } @@ -1239,7 +1237,7 @@ void BaselineAssembler::sub(int lhs) pasm()->prepareCallWithArgCount(2); pasm()->passAccumulatorAsArg(1); pasm()->passJSSlotAsArg(lhs, 0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_sub, CallResultDestination::InAccumulator); + ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator); checkException(); // done. @@ -1269,7 +1267,7 @@ void BaselineAssembler::cmpeqInt(int lhs) else pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1)); pasm()->pushAccumulatorAsArg(0); - pasm()->callRuntimeUnchecked("Runtime::method_equal", (void*)Runtime::method_equal); + pasm()->callRuntimeUnchecked("Equal", (void*)Runtime::Equal::call); pasm()->saveReturnValueInAccumulator(); if (PlatformAssembler::ArgInRegCount < 2) pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister); @@ -1293,7 +1291,7 @@ void BaselineAssembler::cmpneInt(int lhs) else pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1)); pasm()->pushAccumulatorAsArg(0); - pasm()->callRuntimeUnchecked("Runtime::method_notEqual", (void*)Runtime::method_notEqual); + pasm()->callRuntimeUnchecked("NotEqual", (void*)Runtime::NotEqual::call); pasm()->saveReturnValueInAccumulator(); if (PlatformAssembler::ArgInRegCount < 2) pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister); @@ -1314,7 +1312,6 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName pasm()->compare32(c, PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::AccumulatorRegisterValue); - pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); return PlatformAssembler::Jump(); }); @@ -1326,60 +1323,58 @@ void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName callRuntime(functionName, reinterpret_cast<void*>(function), CallResultDestination::InAccumulator); checkException(); - pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); // done. done.link(pasm()); + pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); } void BaselineAssembler::cmpeq(int lhs) { - cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual, - "Runtime::method_compareEqual", lhs); + cmp(PlatformAssembler::Equal, &Runtime::CompareEqual::call, + "CompareEqual", lhs); } void BaselineAssembler::cmpne(int lhs) { - cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual, - "Runtime::method_compareNotEqual", lhs); + cmp(PlatformAssembler::NotEqual, &Runtime::CompareNotEqual::call, + "CompareNotEqual", lhs); } void BaselineAssembler::cmpgt(int lhs) { - cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan, - "Runtime::method_compareGreaterThan", lhs); + cmp(PlatformAssembler::GreaterThan, &Runtime::CompareGreaterThan::call, + "CompareGreaterThan", lhs); } void BaselineAssembler::cmpge(int lhs) { - cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual, - "Runtime::method_compareGreaterEqual", lhs); + cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::CompareGreaterEqual::call, + "CompareGreaterEqual", lhs); } void BaselineAssembler::cmplt(int lhs) { - cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan, - "Runtime::method_compareLessThan", lhs); + cmp(PlatformAssembler::LessThan, &Runtime::CompareLessThan::call, + "CompareLessThan", lhs); } void BaselineAssembler::cmple(int lhs) { - cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual, - "Runtime::method_compareLessEqual", lhs); + cmp(PlatformAssembler::LessThanOrEqual, &Runtime::CompareLessEqual::call, + "CompareLessEqual", lhs); } void BaselineAssembler::cmpStrictEqual(int lhs) { - cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual, + cmp(PlatformAssembler::Equal, &Runtime::CompareStrictEqual::call, "RuntimeHelpers::strictEqual", lhs); } void BaselineAssembler::cmpStrictNotEqual(int lhs) { - cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual, - "RuntimeHelpers::strictEqual", lhs); - pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue); - pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); + cmp(PlatformAssembler::NotEqual, &Runtime::CompareStrictNotEqual::call, + "RuntimeHelpers::strictNotEqual", lhs); } int BaselineAssembler::jump(int offset) @@ -1487,7 +1482,7 @@ void BaselineAssembler::loadAccumulatorFromFrame() static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine) { - return Runtime::method_tailCall(frame, engine); + return Runtime::TailCall::call(frame, engine); } void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv) @@ -1594,9 +1589,8 @@ void BaselineAssembler::pushCatchContext(int index, int name) pasm()->prepareCallWithArgCount(3); pasm()->passInt32AsArg(name, 2); pasm()->passInt32AsArg(index, 1); - pasm()->passJSSlotAsArg(CallData::Context, 0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, CallResultDestination::InAccumulator); - pasm()->storeAccumulator(pasm()->contextAddress()); + pasm()->passEngineAsArg(0); + ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore); } void BaselineAssembler::popContext() @@ -1615,7 +1609,7 @@ void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variable prepareCallWithArgCount(2); passInt32AsArg(variableName, 1); passEngineAsArg(0); - ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore); + ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore); gotoCatchException(); valueIsAliveJump.link(pasm()); } @@ -1629,5 +1623,3 @@ void BaselineAssembler::ret() } // QV4 namepsace QT_END_NAMESPACE - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h index 97cb522244..33fd288ac3 100644 --- a/src/qml/jit/qv4baselineassembler_p.h +++ b/src/qml/jit/qv4baselineassembler_p.h @@ -55,6 +55,8 @@ #include <private/qv4function_p.h> #include <QHash> +QT_REQUIRE_CONFIG(qml_jit); + QT_BEGIN_NAMESPACE namespace QV4 { @@ -65,7 +67,7 @@ namespace JIT { #define GENERATE_RUNTIME_CALL(function, destination) \ callRuntime(JIT_STRINGIFY(function), \ - reinterpret_cast<void *>(&function), \ + reinterpret_cast<void *>(&Runtime::function::call), \ destination) #define GENERATE_TAIL_CALL(function) \ tailCallRuntime(JIT_STRINGIFY(function), \ diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index d8d4e36f35..fcaa87290e 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -38,13 +38,10 @@ ****************************************************************************/ #include "qv4baselinejit_p.h" -#include "qv4jithelpers_p.h" #include "qv4baselineassembler_p.h" #include <private/qv4lookup_p.h> #include <private/qv4generatorobject_p.h> -#ifdef V4_ENABLE_JIT - QT_USE_NAMESPACE using namespace QV4; using namespace QV4::JIT; @@ -52,7 +49,7 @@ using namespace QV4::Moth; BaselineJIT::BaselineJIT(Function *function) : function(function) - , as(new BaselineAssembler(function->compilationUnit->constants)) + , as(new BaselineAssembler(&(function->compilationUnit->constants->asValue<Value>()))) {} BaselineJIT::~BaselineJIT() @@ -78,10 +75,11 @@ void BaselineJIT::generate() #define STORE_IP() as->storeInstructionPointer(nextInstructionOffset()) #define STORE_ACC() as->saveAccumulatorInFrame() #define LOAD_ACC() as->loadAccumulatorFromFrame() -#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) \ - as->GENERATE_RUNTIME_CALL(function, destination) -#define BASELINEJIT_GENERATE_TAIL_CALL(function) \ - as->GENERATE_TAIL_CALL(function) +#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) { \ + as->GENERATE_RUNTIME_CALL(function, destination); \ + if (Runtime::function::throws) \ + as->checkException(); \ + else {} } // this else prevents else statements after the macro from attaching to the if above void BaselineJIT::generate_Ret() { @@ -152,7 +150,7 @@ void BaselineJIT::generate_LoadImport(int index) as->loadImport(index); } -void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadLocal(int index) { as->loadLocal(index); } @@ -163,7 +161,7 @@ void BaselineJIT::generate_StoreLocal(int index) as->storeLocal(index); } -void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadScopedLocal(int scope, int index) { as->loadLocal(index, scope); } @@ -184,7 +182,7 @@ void BaselineJIT::generate_MoveRegExp(int regExpId, int destReg) as->prepareCallWithArgCount(2); as->passInt32AsArg(regExpId, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(RegexpLiteral, CallResultDestination::InAccumulator); as->storeReg(destReg); } @@ -193,37 +191,33 @@ void BaselineJIT::generate_LoadClosure(int value) as->prepareCallWithArgCount(2); as->passInt32AsArg(value, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(Closure, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadName(int name) { STORE_IP(); as->prepareCallWithArgCount(2); as->passInt32AsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadGlobalLookup(int index) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); - as->passEngineAsArg(1); - as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, CallResultDestination::InAccumulator); - as->checkException(); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index) { - as->prepareCallWithArgCount(3); - as->passInt32AsArg(index, 2); - as->passEngineAsArg(1); - as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadQmlContextPropertyLookup, CallResultDestination::InAccumulator); - as->checkException(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator); } void BaselineJIT::generate_StoreNameSloppy(int name) @@ -234,8 +228,7 @@ void BaselineJIT::generate_StoreNameSloppy(int name) as->passAccumulatorAsArg(2); as->passInt32AsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameSloppy, CallResultDestination::Ignore); LOAD_ACC(); } @@ -247,12 +240,11 @@ void BaselineJIT::generate_StoreNameStrict(int name) as->passAccumulatorAsArg(2); as->passInt32AsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameStrict, CallResultDestination::Ignore); LOAD_ACC(); } -void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) +void BaselineJIT::generate_LoadElement(int base) { STORE_IP(); STORE_ACC(); @@ -260,11 +252,10 @@ void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) +void BaselineJIT::generate_StoreElement(int base, int index) { STORE_IP(); STORE_ACC(); @@ -273,12 +264,11 @@ void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) as->passJSSlotAsArg(index, 2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(StoreElement, CallResultDestination::Ignore); LOAD_ACC(); } -void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadProperty(int name) { STORE_IP(); STORE_ACC(); @@ -286,21 +276,19 @@ void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) as->passInt32AsArg(name, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_GetLookup(int index) { STORE_IP(); STORE_ACC(); as->prepareCallWithArgCount(4); as->passInt32AsArg(index, 3); as->passAccumulatorAsArg(2); - as->passEngineAsArg(1); - as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, CallResultDestination::InAccumulator); - as->checkException(); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(GetLookup, CallResultDestination::InAccumulator); } void BaselineJIT::generate_StoreProperty(int name, int base) @@ -312,8 +300,7 @@ void BaselineJIT::generate_StoreProperty(int name, int base) as->passInt32AsArg(name, 2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(StoreProperty, CallResultDestination::Ignore); LOAD_ACC(); } @@ -323,12 +310,13 @@ void BaselineJIT::generate_SetLookup(int index, int base) STORE_ACC(); as->prepareCallWithArgCount(4); as->passAccumulatorAsArg(3); - as->passJSSlotAsArg(base, 2); - as->passInt32AsArg(index, 1); + as->passInt32AsArg(index, 2); + as->passJSSlotAsArg(base, 1); as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL((function->isStrict() ? Helpers::setLookupStrict : Helpers::setLookupSloppy), - CallResultDestination::InAccumulator); - as->checkException(); + if (function->isStrict()) + BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupStrict, CallResultDestination::InAccumulator) + else + BASELINEJIT_GENERATE_RUNTIME_CALL(SetLookupSloppy, CallResultDestination::InAccumulator) } void BaselineJIT::generate_LoadSuperProperty(int property) @@ -337,8 +325,7 @@ void BaselineJIT::generate_LoadSuperProperty(int property) as->prepareCallWithArgCount(2); as->passJSSlotAsArg(property, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperProperty, CallResultDestination::InAccumulator); } void BaselineJIT::generate_StoreSuperProperty(int property) @@ -349,8 +336,7 @@ void BaselineJIT::generate_StoreSuperProperty(int property) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(property, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore); LOAD_ACC(); } @@ -372,7 +358,7 @@ void BaselineJIT::generate_Resume(int) Q_UNREACHABLE(); } -void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallValue(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -380,11 +366,10 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSl as->passJSSlotAsArg(argv, 2); as->passJSSlotAsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallValue, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -393,11 +378,10 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, as->passJSSlotAsArg(thisObject, 2); as->passJSSlotAsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithReceiver, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithReceiver, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -406,11 +390,10 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, as->passInt32AsArg(name, 2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -419,11 +402,10 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg as->passInt32AsArg(lookupIndex, 2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -432,11 +414,10 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, as->passJSSlotAsArg(index, 2); as->passJSSlotAsArg(base, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallName(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -444,22 +425,20 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlo as->passJSSlotAsArg(argv, 2); as->passInt32AsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(3); as->passInt32AsArg(argc, 2); as->passJSSlotAsArg(argv, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallPossiblyDirectEval, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -467,12 +446,10 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int / as->passJSSlotAsArg(argv, 2); as->passInt32AsArg(index, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, - int /*traceSlot*/) +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -480,11 +457,10 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int as->passJSSlotAsArg(argv, 2); as->passInt32AsArg(index, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextPropertyLookup, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -493,8 +469,7 @@ void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, in as->passJSSlotAsArg(thisObject, 2); as->passJSSlotAsArg(func, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithSpread, CallResultDestination::InAccumulator); } void BaselineJIT::generate_TailCall(int func, int thisObject, int argc, int argv) @@ -513,8 +488,7 @@ void BaselineJIT::generate_Construct(int func, int argc, int argv) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(func, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(Construct, CallResultDestination::InAccumulator); } void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv) @@ -527,8 +501,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(func, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(ConstructWithSpread, CallResultDestination::InAccumulator); } void BaselineJIT::generate_SetUnwindHandler(int offset) @@ -561,7 +534,7 @@ void BaselineJIT::generate_ThrowException() as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, CallResultDestination::Ignore); + BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowException, CallResultDestination::Ignore); as->gotoCatchException(); } @@ -572,8 +545,7 @@ void BaselineJIT::generate_CreateCallContext() { as->prepareCallWithArgCount(1); as->passCppFrameAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(ExecutionContext::newCallContext, CallResultDestination::Ignore); // keeps result in return value register - as->storeHeapObject(CallData::Context); + BASELINEJIT_GENERATE_RUNTIME_CALL(PushCallContext, CallResultDestination::Ignore); } void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); } @@ -583,11 +555,9 @@ void BaselineJIT::generate_PushWithContext() STORE_IP(); as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(2); - as->passJSSlotAsArg(0, 1); + as->passJSSlotAsArg(CallData::Accumulator, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, CallResultDestination::Ignore); // keeps result in return value register - as->checkException(); - as->storeHeapObject(CallData::Context); + BASELINEJIT_GENERATE_RUNTIME_CALL(PushWithContext, CallResultDestination::InAccumulator); } void BaselineJIT::generate_PushBlockContext(int index) @@ -595,8 +565,8 @@ void BaselineJIT::generate_PushBlockContext(int index) as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(2); as->passInt32AsArg(index, 1); - as->passJSSlotAsArg(0, 0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, CallResultDestination::Ignore); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(PushBlockContext, CallResultDestination::Ignore); as->loadAccumulatorFromFrame(); } @@ -604,29 +574,27 @@ void BaselineJIT::generate_CloneBlockContext() { as->saveAccumulatorInFrame(); as->prepareCallWithArgCount(1); - as->passJSSlotAsArg(CallData::Context, 0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, CallResultDestination::Ignore); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(CloneBlockContext, CallResultDestination::Ignore); as->loadAccumulatorFromFrame(); } void BaselineJIT::generate_PushScriptContext(int index) { as->saveAccumulatorInFrame(); - as->prepareCallWithArgCount(3); - as->passInt32AsArg(index, 2); - as->passEngineAsArg(1); - as->passJSSlotAsArg(0, 0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, CallResultDestination::Ignore); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(PushScriptContext, CallResultDestination::Ignore); as->loadAccumulatorFromFrame(); } void BaselineJIT::generate_PopScriptContext() { as->saveAccumulatorInFrame(); - as->prepareCallWithArgCount(2); - as->passEngineAsArg(1); - as->passJSSlotAsArg(0, 0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, CallResultDestination::Ignore); + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(PopScriptContext, CallResultDestination::Ignore); as->loadAccumulatorFromFrame(); } @@ -639,8 +607,7 @@ void BaselineJIT::generate_GetIterator(int iterator) as->passInt32AsArg(iterator, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(GetIterator, CallResultDestination::InAccumulator); } void BaselineJIT::generate_IteratorNext(int value, int done) @@ -650,9 +617,8 @@ void BaselineJIT::generate_IteratorNext(int value, int done) as->passJSSlotAsArg(value, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNext, CallResultDestination::InAccumulator); as->storeReg(done); - as->checkException(); } void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object) @@ -663,8 +629,7 @@ void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object) as->passJSSlotAsArg(iterator, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNextForYieldStar, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorNextForYieldStar, CallResultDestination::InAccumulator); } void BaselineJIT::generate_IteratorClose(int done) @@ -674,8 +639,7 @@ void BaselineJIT::generate_IteratorClose(int done) as->passJSSlotAsArg(done, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(IteratorClose, CallResultDestination::InAccumulator); } void BaselineJIT::generate_DestructureRestElement() @@ -684,29 +648,28 @@ void BaselineJIT::generate_DestructureRestElement() as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(DestructureRestElement, CallResultDestination::InAccumulator); } void BaselineJIT::generate_DeleteProperty(int base, int index) { STORE_IP(); - as->prepareCallWithArgCount(3); - as->passJSSlotAsArg(index, 2); - as->passJSSlotAsArg(base, 1); - as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, CallResultDestination::InAccumulator); - as->checkException(); + as->prepareCallWithArgCount(4); + as->passJSSlotAsArg(index, 3); + as->passJSSlotAsArg(base, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteProperty, CallResultDestination::InAccumulator); } void BaselineJIT::generate_DeleteName(int name) { STORE_IP(); - as->prepareCallWithArgCount(2); - as->passInt32AsArg(name, 1); - as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, CallResultDestination::InAccumulator); - as->checkException(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(name, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(DeleteName, CallResultDestination::InAccumulator); } void BaselineJIT::generate_TypeofName(int name) @@ -714,7 +677,7 @@ void BaselineJIT::generate_TypeofName(int name) as->prepareCallWithArgCount(2); as->passInt32AsArg(name, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofName, CallResultDestination::InAccumulator); } void BaselineJIT::generate_TypeofValue() @@ -723,7 +686,7 @@ void BaselineJIT::generate_TypeofValue() as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(TypeofValue, CallResultDestination::InAccumulator); } void BaselineJIT::generate_DeclareVar(int varName, int isDeletable) @@ -733,7 +696,7 @@ void BaselineJIT::generate_DeclareVar(int varName, int isDeletable) as->passInt32AsArg(varName, 2); as->passInt32AsArg(isDeletable, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, CallResultDestination::Ignore); + BASELINEJIT_GENERATE_RUNTIME_CALL(DeclareVar, CallResultDestination::Ignore); LOAD_ACC(); } @@ -743,7 +706,7 @@ void BaselineJIT::generate_DefineArray(int argc, int args) as->passInt32AsArg(argc, 2); as->passJSSlotAsArg(args, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(ArrayLiteral, CallResultDestination::InAccumulator); } void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args) @@ -753,7 +716,7 @@ void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, in as->passJSSlotAsArg(args, 2); as->passInt32AsArg(internalClassId, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(ObjectLiteral, CallResultDestination::InAccumulator); } void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames) @@ -763,14 +726,14 @@ void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int compute as->passJSSlotAsArg(heritage, 2); as->passInt32AsArg(classIndex, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(CreateClass, CallResultDestination::InAccumulator); } void BaselineJIT::generate_CreateMappedArgumentsObject() { as->prepareCallWithArgCount(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject, + BASELINEJIT_GENERATE_RUNTIME_CALL(CreateMappedArgumentsObject, CallResultDestination::InAccumulator); } @@ -778,7 +741,7 @@ void BaselineJIT::generate_CreateUnmappedArgumentsObject() { as->prepareCallWithArgCount(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject, + BASELINEJIT_GENERATE_RUNTIME_CALL(CreateUnmappedArgumentsObject, CallResultDestination::InAccumulator); } @@ -787,7 +750,7 @@ void BaselineJIT::generate_CreateRestParameter(int argIndex) as->prepareCallWithArgCount(2); as->passInt32AsArg(argIndex, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, CallResultDestination::InAccumulator); + BASELINEJIT_GENERATE_RUNTIME_CALL(CreateRestParameter, CallResultDestination::InAccumulator); } void BaselineJIT::generate_ConvertThisToObject() @@ -796,8 +759,8 @@ void BaselineJIT::generate_ConvertThisToObject() as->prepareCallWithArgCount(2); as->passJSSlotAsArg(CallData::This, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(ConvertThisToObject, CallResultDestination::InAccumulator); + as->storeReg(CallData::This); LOAD_ACC(); } @@ -806,8 +769,7 @@ void BaselineJIT::generate_LoadSuperConstructor() as->prepareCallWithArgCount(2); as->passJSSlotAsArg(CallData::Function, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperConstructor, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(LoadSuperConstructor, CallResultDestination::InAccumulator); } void BaselineJIT::generate_ToObject() @@ -816,8 +778,7 @@ void BaselineJIT::generate_ToObject() as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::toObject, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(ToObject, CallResultDestination::InAccumulator); } @@ -826,12 +787,12 @@ void BaselineJIT::generate_Jump(int offset) labels.insert(as->jump(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpTrue(int offset) { labels.insert(as->jumpTrue(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpFalse(int offset) { labels.insert(as->jumpFalse(absoluteOffset(offset))); } @@ -846,6 +807,11 @@ void BaselineJIT::generate_JumpNotUndefined(int offset) labels.insert(as->jumpNotUndefined(absoluteOffset(offset))); } +void BaselineJIT::generate_CheckException() +{ + as->checkException(); +} + void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); } @@ -866,8 +832,7 @@ void BaselineJIT::generate_CmpIn(int lhs) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(lhs, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_in, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(In, CallResultDestination::InAccumulator); } void BaselineJIT::generate_CmpInstanceOf(int lhs) @@ -877,17 +842,16 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) as->passAccumulatorAsArg(2); as->passJSSlotAsArg(lhs, 1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(Instanceof, CallResultDestination::InAccumulator); } void BaselineJIT::generate_UNot() { as->unot(); } void BaselineJIT::generate_UPlus() { as->toNumber(); } -void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); } +void BaselineJIT::generate_UMinus() { as->uminus(); } void BaselineJIT::generate_UCompl() { as->ucompl(); } -void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); } -void BaselineJIT::generate_Decrement(int /*traceSlot*/) { as->dec(); } -void BaselineJIT::generate_Add(int lhs, int /*traceSlot*/) { as->add(lhs); } +void BaselineJIT::generate_Increment() { as->inc(); } +void BaselineJIT::generate_Decrement() { as->dec(); } +void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } @@ -909,13 +873,12 @@ void BaselineJIT::generate_Exp(int lhs) { as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passJSSlotAsArg(lhs, 0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(Exp, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); } +void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } -void BaselineJIT::generate_Mod(int lhs, int /*traceSlot*/) { as->mod(lhs); } -void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); } +void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } //void BaselineJIT::generate_BinopContext(int alu, int lhs) //{ @@ -942,8 +905,7 @@ void BaselineJIT::generate_ThrowOnNullOrUndefined() as->prepareCallWithArgCount(2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::throwOnNullOrUndefined, CallResultDestination::Ignore); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(ThrowOnNullOrUndefined, CallResultDestination::Ignore); LOAD_ACC(); } @@ -952,19 +914,17 @@ void BaselineJIT::generate_GetTemplateObject(int index) as->prepareCallWithArgCount(2); as->passInt32AsArg(index, 1); as->passFunctionAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::getTemplateObject, CallResultDestination::InAccumulator); - as->checkException(); + BASELINEJIT_GENERATE_RUNTIME_CALL(GetTemplateObject, CallResultDestination::InAccumulator); } -void BaselineJIT::startInstruction(Instr::Type /*instr*/) +ByteCodeHandler::Verdict BaselineJIT::startInstruction(Instr::Type /*instr*/) { if (labels.contains(currentInstructionOffset())) as->addLabel(currentInstructionOffset()); + return ProcessInstruction; } void BaselineJIT::endInstruction(Instr::Type instr) { Q_UNUSED(instr); } - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 10c89bc74b..284faf0ff0 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -56,7 +56,7 @@ #include <private/qv4instr_moth_p.h> #include <private/qv4bytecodehandler_p.h> -//QT_REQUIRE_CONFIG(qml_jit); +QT_REQUIRE_CONFIG(qml_jit); QT_BEGIN_NAMESPACE @@ -65,7 +65,6 @@ namespace JIT { class BaselineAssembler; -#ifdef V4_ENABLE_JIT class BaselineJIT final: public Moth::ByteCodeHandler { public: @@ -88,22 +87,22 @@ public: void generate_StoreReg(int reg) override; void generate_MoveReg(int srcReg, int destReg) override; void generate_LoadImport(int index) override; - void generate_LoadLocal(int index, int traceSlot) override; + void generate_LoadLocal(int index) override; void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; + void generate_LoadScopedLocal(int scope, int index) override; void generate_StoreScopedLocal(int scope, int index) override; void generate_LoadRuntimeString(int stringId) override; void generate_MoveRegExp(int regExpId, int destReg) override; void generate_LoadClosure(int value) override; - void generate_LoadName(int name, int traceSlot) override; - void generate_LoadGlobalLookup(int index, int traceSlot) override; - void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override; + void generate_LoadName(int name) override; + void generate_LoadGlobalLookup(int index) override; + void generate_LoadQmlContextPropertyLookup(int index) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int traceSlot) override; - void generate_StoreElement(int base, int index, int traceSlot) override; - void generate_LoadProperty(int name, int traceSlot) override; - void generate_GetLookup(int index, int traceSlot) override; + void generate_LoadElement(int base) override; + void generate_StoreElement(int base, int index) override; + void generate_LoadProperty(int name) override; + void generate_GetLookup(int index) override; void generate_StoreProperty(int name, int base) override; void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; @@ -112,16 +111,16 @@ public: void generate_YieldStar() override; void generate_Resume(int) override; - void generate_CallValue(int name, int argc, int argv, int traceSlot) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int traceSlot) override; - void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int traceSlot) override; - void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; - void generate_CallName(int name, int argc, int argv, int traceSlot) override; - void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; - void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override; + void generate_CallValue(int name, int argc, int argv) override; + void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override; + void generate_CallProperty(int name, int base, int argc, int argv) override; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override; + void generate_CallElement(int base, int index, int argc, int argv) override; + void generate_CallName(int name, int argc, int argv) override; + void generate_CallPossiblyDirectEval(int argc, int argv) override; + void generate_CallGlobalLookup(int index, int argc, int argv) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; + void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; void generate_ConstructWithSpread(int func, int argc, int argv) override; @@ -160,10 +159,11 @@ public: void generate_LoadSuperConstructor() override; void generate_ToObject() override; void generate_Jump(int offset) override; - void generate_JumpTrue(int traceSlot, int offset) override; - void generate_JumpFalse(int traceSlot, int offset) override; + void generate_JumpTrue(int offset) override; + void generate_JumpFalse(int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; + void generate_CheckException() override; void generate_CmpEqNull() override; void generate_CmpNeNull() override; void generate_CmpEqInt(int lhs) override; @@ -180,11 +180,11 @@ public: void generate_CmpInstanceOf(int lhs) override; void generate_UNot() override; void generate_UPlus() override; - void generate_UMinus(int traceSlot) override; + void generate_UMinus() override; void generate_UCompl() override; - void generate_Increment(int traceSlot) override; - void generate_Decrement(int traceSlot) override; - void generate_Add(int lhs, int traceSlot) override; + void generate_Increment() override; + void generate_Decrement() override; + void generate_Add(int lhs) override; void generate_BitAnd(int lhs) override; void generate_BitOr(int lhs) override; void generate_BitXor(int lhs) override; @@ -198,15 +198,15 @@ public: void generate_ShrConst(int rhs) override; void generate_ShlConst(int rhs) override; void generate_Exp(int lhs) override; - void generate_Mul(int lhs, int traceSlot) override; + void generate_Mul(int lhs) override; void generate_Div(int lhs) override; - void generate_Mod(int lhs, int traceSlot) override; - void generate_Sub(int lhs, int traceSlot) override; + void generate_Mod(int lhs) override; + void generate_Sub(int lhs) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; - void startInstruction(Moth::Instr::Type instr) override; + Verdict startInstruction(Moth::Instr::Type instr) override; void endInstruction(Moth::Instr::Type instr) override; private: @@ -214,7 +214,6 @@ private: QScopedPointer<BaselineAssembler> as; QSet<int> labels; }; -#endif // V4_ENABLE_JIT } // namespace JIT } // namespace QV4 diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp deleted file mode 100644 index 674fd8c8c8..0000000000 --- a/src/qml/jit/qv4jithelpers.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qv4jithelpers_p.h" -#include "qv4engine_p.h" -#include "qv4function_p.h" -#include "qv4value_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4lookup_p.h" -#include <QtCore/private/qnumeric_p.h> - -#ifdef V4_ENABLE_JIT - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace JIT { -namespace Helpers { - -void convertThisToObject(ExecutionEngine *engine, Value *t) -{ - if (!t->isObject()) { - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - *t = t->toObject(engine)->asReturnedValue(); - } - } -} - -ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index) -{ - Lookup *l = f->compilationUnit->runtimeLookups + index; - return l->globalGetter(l, engine); -} - -ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index) -{ - Lookup *l = f->compilationUnit->runtimeLookups + index; - return l->qmlContextPropertyGetter(l, engine, nullptr); -} - -ReturnedValue toObject(ExecutionEngine *engine, const Value &obj) -{ - if (obj.isObject()) - return obj.asReturnedValue(); - - return obj.toObject(engine)->asReturnedValue(); -} - -ReturnedValue exp(const Value &base, const Value &exp) -{ - double b = base.toNumber(); - double e = exp.toNumber(); - if (qt_is_inf(e) && (b == 1 || b == -1)) - return Encode(qt_snan()); - return Encode(pow(b,e)); -} - -ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index) -{ - Lookup *l = f->compilationUnit->runtimeLookups + index; - return l->getter(l, engine, base); -} - -void setLookupSloppy(Function *f, int index, Value &base, const Value &value) -{ - ExecutionEngine *engine = f->internalClass->engine; - QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; - l->setter(l, engine, base, value); -} - -void setLookupStrict(Function *f, int index, Value &base, const Value &value) -{ - ExecutionEngine *engine = f->internalClass->engine; - QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; - if (!l->setter(l, engine, base, value)) - engine->throwTypeError(); -} - - -void pushBlockContext(Value *stack, int index) -{ - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - stack[CallData::Context] = Runtime::method_createBlockContext(c, index); -} - -void cloneBlockContext(Value *contextSlot) -{ - *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot)); -} - -void pushScriptContext(Value *stack, ExecutionEngine *engine, int index) -{ - stack[CallData::Context] = Runtime::method_createScriptContext(engine, index); -} - -void popScriptContext(Value *stack, ExecutionEngine *engine) -{ - stack[CallData::Context] = Runtime::method_popScriptContext(engine); -} - -ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index) -{ - auto engine = function->internalClass->engine; - if (!Runtime::method_deleteProperty(engine, base, index)) { - if (function->isStrict()) - engine->throwTypeError(); - return Encode(false); - } else { - return Encode(true); - } -} - -ReturnedValue deleteName(Function *function, int name) -{ - auto engine = function->internalClass->engine; - if (!Runtime::method_deleteName(engine, name)) { - if (function->isStrict()) - engine->throwTypeError(); - return Encode(false); - } else { - return Encode(true); - } -} - -void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v) -{ - if (v.isNullOrUndefined()) - engine->throwTypeError(); -} - -} // Helpers namespace -} // JIT namespace -} // QV4 namespace -QT_END_NAMESPACE - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index c41738c811..065fbc1c0a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -41,7 +41,6 @@ #include "qjsengine_p.h" #include "qjsvalue.h" #include "qjsvalue_p.h" -#include "private/qv8engine_p.h" #include "private/qv4engine_p.h" #include "private/qv4mm_p.h" @@ -348,7 +347,6 @@ QJSEngine::QJSEngine(QObject *parent) : QObject(*new QJSEnginePrivate, parent) , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); QJSEnginePrivate::addToDebugServer(this); @@ -361,7 +359,6 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) : QObject(dd, parent) , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); } @@ -375,7 +372,6 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) QJSEngine::~QJSEngine() { QJSEnginePrivate::removeFromDebugServer(this); - delete m_v4Engine->v8Engine; delete m_v4Engine; } @@ -470,6 +466,33 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal QV4::GlobalExtensions::init(obj, extensions); } +/*! + \since 5.14 + Interrupts or re-enables JavaScript execution. + + If \a interrupted is \c true, any JavaScript executed by this engine + immediately aborts and returns an error object until this function is + called again with a value of \c false for \a interrupted. + + This function is thread safe. You may call it from a different thread + in order to interrupt, for example, an infinite loop in JavaScript. +*/ +void QJSEngine::setInterrupted(bool interrupted) +{ + m_v4Engine->isInterrupted = interrupted; +} + +/*! + \since 5.14 + Returns whether JavaScript execution is currently interrupted. + + \sa setInterrupted() +*/ +bool QJSEngine::isInterrupted() const +{ + return m_v4Engine->isInterrupted.loadAcquire(); +} + static QUrl urlForFileName(const QString &fileName) { if (!fileName.startsWith(QLatin1Char(':'))) @@ -527,6 +550,8 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in result = script.run(); if (scope.engine->hasException) result = v4->catchException(); + if (v4->isInterrupted.loadAcquire()) + result = v4->newErrorObject(QStringLiteral("Interrupted")); QJSValue retval(v4, result->asReturnedValue()); @@ -565,7 +590,12 @@ QJSValue QJSEngine::importModule(const QString &fileName) if (m_v4Engine->hasException) return QJSValue(m_v4Engine, m_v4Engine->catchException()); moduleUnit->evaluate(); - return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue()); + if (!m_v4Engine->isInterrupted.loadAcquire()) + return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue()); + + return QJSValue( + m_v4Engine, + m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue()); } /*! diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 6300842341..31a4d68baa 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -113,6 +113,9 @@ public: void installExtensions(Extensions extensions, const QJSValue &object = QJSValue()); + void setInterrupted(bool interrupted); + bool isInterrupted() const; + QV4::ExecutionEngine *handle() const { return m_v4Engine; } void throwError(const QString &message); diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h index a77d710cff..164a70d000 100644 --- a/src/qml/jsapi/qjsengine_p.h +++ b/src/qml/jsapi/qjsengine_p.h @@ -74,7 +74,7 @@ public: static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); } static QJSEnginePrivate* get(QV4::ExecutionEngine *e); - QJSEnginePrivate() : mutex(QMutex::Recursive) {} + QJSEnginePrivate() = default; ~QJSEnginePrivate() override; static void addToDebugServer(QJSEngine *q); @@ -105,7 +105,7 @@ public: }; // Shared by QQmlEngine - mutable QMutex mutex; + mutable QRecursiveMutex mutex; // These methods may be called from the QML loader thread diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 1ab6e8c767..c2957dd294 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -51,7 +51,6 @@ #include "qv4variantobject_p.h" #include "qv4regexpobject_p.h" #include "qv4errorobject_p.h" -#include "private/qv8engine_p.h" #include <private/qv4mm_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> @@ -770,6 +769,8 @@ QJSValue QJSValue::call(const QJSValueList &args) ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted.loadAcquire()) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } @@ -826,6 +827,8 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted.loadAcquire()) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } @@ -874,6 +877,8 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) ScopedValue result(scope, f->callAsConstructor(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted.loadAcquire()) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } @@ -1042,7 +1047,7 @@ bool QJSValue::equals(const QJSValue& other) const if (!ov) return other.equals(*this); - return Runtime::method_compareEqual(*v, *ov); + return Runtime::CompareEqual::call(*v, *ov); } /*! diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a24ee0a188..32acc6affc 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -1,8 +1,8 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -!qmldevtools_build { SOURCES += \ + $$PWD/qv4engine.cpp \ $$PWD/qv4context.cpp \ $$PWD/qv4persistent.cpp \ $$PWD/qv4lookup.cpp \ @@ -36,13 +36,13 @@ SOURCES += \ $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ $$PWD/qv4stackframe.cpp \ + $$PWD/qv4string.cpp \ $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ $$PWD/qv4regexp.cpp \ $$PWD/qv4runtimecodegen.cpp \ - $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ $$PWD/qv4symbol.cpp \ $$PWD/qv4setobject.cpp \ @@ -57,13 +57,17 @@ SOURCES += \ $$PWD/qv4mapiterator.cpp \ $$PWD/qv4estable.cpp \ $$PWD/qv4module.cpp \ - $$PWD/qv4promiseobject.cpp + $$PWD/qv4promiseobject.cpp \ + $$PWD/qv4runtime.cpp \ + $$PWD/qv4value.cpp \ + $$PWD/qv4compilationunitmapper.cpp \ + $$PWD/qv4executablecompilationunit.cpp \ + $$PWD/qv4executableallocator.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp HEADERS += \ $$PWD/qv4global_p.h \ - $$PWD/qv4alloca_p.h \ $$PWD/qv4engine_p.h \ $$PWD/qv4enginebase_p.h \ $$PWD/qv4context_p.h \ @@ -103,13 +107,13 @@ HEADERS += \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ $$PWD/qv4stackframe_p.h \ + $$PWD/qv4string_p.h \ $$PWD/qv4stringiterator_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ $$PWD/qv4objectiterator_p.h \ $$PWD/qv4regexp_p.h \ - $$PWD/qv4serialize_p.h \ $$PWD/qv4script_p.h \ $$PWD/qv4symbol_p.h \ $$PWD/qv4setobject_p.h \ @@ -128,7 +132,13 @@ HEADERS += \ $$PWD/qv4estable_p.h \ $$PWD/qv4vtable_p.h \ $$PWD/qv4module_p.h \ - $$PWD/qv4promiseobject_p.h + $$PWD/qv4promiseobject_p.h \ + $$PWD/qv4runtime_p.h \ + $$PWD/qv4value_p.h \ + $$PWD/qv4compilationunitmapper_p.h \ + $$PWD/qv4executablecompilationunit_p.h \ + $$PWD/qv4functiontable_p.h \ + $$PWD/qv4runtimeapi_p.h qtConfig(qml-sequence-object) { HEADERS += \ @@ -138,29 +148,10 @@ qtConfig(qml-sequence-object) { $$PWD/qv4sequenceobject.cpp } -} - - -HEADERS += \ - $$PWD/qv4runtime_p.h \ - $$PWD/qv4runtimeapi_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4string_p.h \ - $$PWD/qv4util_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4functiontable_p.h - -SOURCES += \ - $$PWD/qv4engine.cpp \ - $$PWD/qv4runtime.cpp \ - $$PWD/qv4string.cpp \ - $$PWD/qv4value.cpp \ - $$PWD/qv4executableallocator.cpp +unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp +else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -qmldevtools_build { - SOURCES += \ - $$PWD/qv4functiontable_noop.cpp -} else:win32 { +win32 { !winrt:equals(QT_ARCH, x86_64) { SOURCES += \ $$PWD/qv4functiontable_win64.cpp diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 4a21f62cf2..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); @@ -116,6 +117,9 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); args->fullyCreate(); + if (!id.isArrayIndex()) + return Object::virtualDefineOwnProperty(m, id, desc, attrs); + uint index = id.asArrayIndex(); if (!args->isMapped(index)) @@ -148,36 +152,42 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - uint index = id.asArrayIndex(); - if (index < args->d()->argCount && !args->d()->fullyCreated) { - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + if (id.isArrayIndex()) { + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); + uint index = id.asArrayIndex(); + if (index < args->d()->argCount && !args->d()->fullyCreated) { + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } + + if (args->isMapped(index)) { + Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount())); + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } } - if (!args->isMapped(index)) - return Object::virtualGet(m, id, receiver, hasProperty); - Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount())); - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + return Object::virtualGet(m, id, receiver, hasProperty); } bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - uint index = id.asArrayIndex(); - - if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) { - args->context()->setArg(index, value); - return true; + if (id.isArrayIndex()) { + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + uint index = id.asArrayIndex(); + + if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) { + args->context()->setArg(index, value); + return true; + } + + bool isMapped = (args == receiver && args->isMapped(index)); + if (isMapped) + args->context()->setArg(index, value); } - bool isMapped = (args == receiver && args->isMapped(index)); - if (isMapped) - args->context()->setArg(index, value); - return Object::virtualPut(m, id, value, receiver); } @@ -186,13 +196,16 @@ bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id) ArgumentsObject *args = static_cast<ArgumentsObject *>(m); args->fullyCreate(); bool result = Object::virtualDeleteProperty(m, id); - if (result) + if (result && id.isArrayIndex()) args->removeMapping(id.asArrayIndex()); return result; } PropertyAttributes ArgumentsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) { + if (!id.isArrayIndex()) + return Object::virtualGetOwnProperty(m, id, p); + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); uint index = id.asArrayIndex(); if (index < args->d()->argCount && !args->d()->fullyCreated) { diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index b5b421fa39..af1a2d1de0 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -175,7 +175,8 @@ static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObje // this isn't completely kosher. for instance: // Array.from.call(Object, []).constructor == Object // is expected by the tests, but naturally, we get Number. - ScopedValue argument(scope, useLen ? QV4::Encode(len) : Value::undefinedValue()); + ScopedValue argument(scope, useLen ? Value::fromReturnedValue(QV4::Encode(len)) + : Value::undefinedValue()); a = ctor->callAsConstructor(argument, useLen ? 1 : 0); } else { a = scope.engine->newArrayObject(len); @@ -219,7 +220,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V // Item iteration supported, so let's go ahead and try use that. ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0)); CHECK_EXCEPTION(); - ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true)); + ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); CHECK_EXCEPTION(); // symbol_iterator threw; whoops. if (!iterator) { return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. @@ -236,11 +237,11 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V if (k > (static_cast<qint64>(1) << 53) - 1) { ScopedValue falsey(scope, Encode(false)); ScopedValue error(scope, scope.engine->throwTypeError()); - return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + return Runtime::IteratorClose::call(scope.engine, iterator, falsey); } // Retrieve the next value. If the iteration ends, we're done here. - done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue)); + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue)); CHECK_EXCEPTION(); if (done->toBoolean()) { if (ArrayObject *ao = a->as<ArrayObject>()) { @@ -257,7 +258,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); if (scope.engine->hasException) - return Runtime::method_iteratorClose(scope.engine, iterator, Value::fromBoolean(false)); + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); } else { mappedValue = *nextValue; } @@ -271,7 +272,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V if (scope.engine->hasException) { ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + return Runtime::IteratorClose::call(scope.engine, iterator, falsey); } k++; @@ -387,7 +388,7 @@ ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, con v = instance->get(k); if (v->isNullOrUndefined()) continue; - v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 3e5f51c302..a9b4ecb607 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -103,7 +103,7 @@ ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const V if (exception) return v4->throwTypeError(); - return Encode(result ? v4->id_true() : v4->id_false()); + return (result ? v4->id_true() : v4->id_false())->asReturnedValue(); } ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp index 350f6f9485..74f34a284d 100644 --- a/src/qml/compiler/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/compiler/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h index 80f914c141..80f914c141 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp index 6768bc9596..a9ab2f5ccb 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp @@ -45,7 +45,7 @@ #include <QScopeGuard> #include <QDateTime> -#include "qv4compileddata_p.h" +#include "qv4executablecompilationunit_p.h" QT_BEGIN_NAMESPACE @@ -73,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!header.verifyHeader(sourceTimeStamp, errorString)) + if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp index 779c1288fe..9e8babc5e6 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp @@ -39,7 +39,7 @@ #include "qv4compilationunitmapper_p.h" -#include "qv4compileddata_p.h" +#include "qv4executablecompilationunit_p.h" #include <QScopeGuard> #include <QFileInfo> #include <QDateTime> @@ -87,7 +87,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!header.verifyHeader(sourceTimeStamp, errorString)) + if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 94b1a9fb73..018571e325 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -60,7 +60,7 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b { Function *function = frame->v4Function; - Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex); + Heap::InternalClass *ic = function->executableCompilationUnit()->runtimeBlocks.at(blockIndex); uint nLocals = ic->size; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; @@ -71,27 +71,28 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); c->locals.size = nLocals; c->locals.alloc = nLocals; - c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex)); + c->setupLocalTemporalDeadZone(function->executableCompilationUnit()->unitData()->blockAt(blockIndex)); return c; } -Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context) +Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine, + Heap::CallContext *callContext) { - uint nLocals = context->locals.alloc; + uint nLocals = callContext->locals.alloc; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; - ExecutionEngine *v4 = context->internalClass->engine; - Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass); - memcpy(c, context, requiredMemory); + Heap::CallContext *c = engine->memoryManager->allocManaged<CallContext>( + requiredMemory, callContext->internalClass); + memcpy(c, callContext, requiredMemory); return c; - } Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) @@ -108,7 +109,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->init(); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); const CompiledData::Function *compiledFunction = function->compiledFunction; uint nLocals = compiledFunction->nLocals; @@ -128,7 +130,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) return c; } -Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) +Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) const { Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext); c->outer.set(engine(), d()); diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 5cd2f9ddf0..75fa2d08e6 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -150,9 +150,10 @@ struct Q_QML_EXPORT ExecutionContext : public Managed V4_INTERNALCLASS(ExecutionContext) static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); - static Heap::CallContext *cloneBlockContext(Heap::CallContext *context); + static Heap::CallContext *cloneBlockContext(ExecutionEngine *engine, + Heap::CallContext *callContext); static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); - Heap::ExecutionContext *newWithContext(Heap::Object *with); + Heap::ExecutionContext *newWithContext(Heap::Object *with) const; static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); void createMutableBinding(String *name, bool deletable); diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index e313ebe300..cc89947cec 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -52,11 +52,9 @@ #include <time.h> -#include <private/qqmljsengine_p.h> - #include <wtf/MathExtras.h> -#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) +#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) && !defined(Q_OS_ANDROID) /* See QTBUG-56899. Although we don't (yet) have a proper way to reset the system zone, the code below, that uses QTimeZone::systemTimeZone(), works @@ -618,8 +616,7 @@ static inline double ParseString(const QString &s, double localTZA) QStringLiteral("d MMMM, yyyy hh:mm:ss"), }; - for (uint i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { - const QString &format(formats[i]); + for (const QString &format : formats) { dt = format.indexOf(QLatin1String("hh:mm")) < 0 ? QDateTime(QDate::fromString(s, format), QTime(0, 0, 0), Qt::UTC) @@ -746,14 +743,16 @@ void Heap::DateObject::init(const QTime &time) * time from it, which shall (via toQDateTime(), below) discard the date * part. We need a date for which time-zone data is likely to be sane (so * MakeDay(0, 0, 0) was a bad choice; 2 BC, December 31st is before - * time-zones were standardized), with no transition nearby in date. We - * ignore DST transitions before 1970, but even then zone transitions did - * happen. Some do happen at new year, others on DST transitions in spring - * and autumn; so pick the three hundredth anniversary of the birth of - * Giovanni Domenico Cassini (1625-06-08), whose work first let us - * synchronize clocks tolerably accurately at distant locations. + * time-zones were standardized), with no transition nearby in date. + * QDateTime ignores DST transitions before 1970, but even then zone + * transitions did happen; and DaylightSavingTA() will include DST, at odds + * with QDateTime. So pick a date since 1970 and prefer one when no zone + * was in DST. One such interval (according to the Olson database, at + * least) was 1971 March 15th to April 17th. Since converting a time to a + * date-time without specifying a date is foolish, let's use April Fools' + * day. */ - static const double d = MakeDay(1925, 5, 8); + static const double d = MakeDay(1971, 3, 1); double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA)); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 5b9934282c..a87eb92caf 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -96,7 +96,7 @@ struct DateObject: Object { double date() const { return d()->date; } void setDate(double date) { d()->date = date; } - QDateTime toQDateTime() const; + Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const; }; template<> 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 3002945945..3d3f452073 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -38,21 +38,18 @@ ****************************************************************************/ #include <qv4engine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_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> #include <QtCore/QTextStream> #include <QDateTime> #include <QDir> #include <QFileInfo> #include <QLoggingCategory> - -#ifndef V4_BOOTSTRAP +#if QT_CONFIG(regularexpression) +#include <QRegularExpression> +#endif #include <qv4qmlcontext_p.h> #include <qv4value_p.h> @@ -104,7 +101,6 @@ #include "qv4dataview_p.h" #include "qv4promiseobject_p.h" #include "qv4typedarray_p.h" -#include <private/qv8engine_p.h> #include <private/qjsvalue_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> @@ -112,9 +108,15 @@ #include <private/qqmllistwrapper_p.h> #include <private/qqmllist_p.h> #include <private/qqmltypeloader_p.h> +#include <private/qqmlbuiltinfunctions_p.h> #if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> #endif +#if QT_CONFIG(qml_xml_http_request) +#include <private/qv4domerrors_p.h> +#include <private/qqmlxmlhttprequest_p.h> +#endif +#include <private/qv4sqlerrors_p.h> #include <qqmlfile.h> #if USE(PTHREADS) @@ -131,16 +133,12 @@ #include <valgrind/memcheck.h> #endif -#endif // #ifndef V4_BOOTSTRAP +Q_DECLARE_METATYPE(QList<int>) QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") - using namespace QV4; -#ifndef V4_BOOTSTRAP - static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int) @@ -150,6 +148,43 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const qint32 ExecutionEngine::maxCallDepth = -1; +template <typename ReturnType> +ReturnType convertJSValueToVariantType(const QJSValue &value) +{ + return value.toVariant().value<ReturnType>(); +} + +static void saveJSValue(QDataStream &stream, const void *data) +{ + const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data); + quint32 isNullOrUndefined = 0; + if (jsv->isNull()) + isNullOrUndefined |= 0x1; + if (jsv->isUndefined()) + isNullOrUndefined |= 0x2; + stream << isNullOrUndefined; + if (!isNullOrUndefined) + reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream); +} + +static void restoreJSValue(QDataStream &stream, void *data) +{ + QJSValue *jsv = reinterpret_cast<QJSValue*>(data); + + quint32 isNullOrUndefined; + stream >> isNullOrUndefined; + + if (isNullOrUndefined & 0x1) { + *jsv = QJSValue(QJSValue::NullValue); + } else if (isNullOrUndefined & 0x2) { + *jsv = QJSValue(); + } else { + QVariant v; + v.load(stream); + QJSValuePrivate::setVariant(jsv, v); + } +} + ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) @@ -157,19 +192,31 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) , jsStack(new WTF::PageAllocation) , gcStack(new WTF::PageAllocation) , globalCode(nullptr) - , v8Engine(nullptr) , publicEngine(jsEngine) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(nullptr) , m_multiplyWrappedQObjects(nullptr) -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) , m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory()) #endif +#if QT_CONFIG(qml_xml_http_request) + , m_xmlHttpRequestData(nullptr) +#endif + , m_qmlEngine(nullptr) { + bool ok = false; + const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok); + if (ok && envMaxJSStackSize > 0) + m_maxJSStackSize = envMaxJSStackSize; + + const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok); + if (ok && envMaxGCStackSize > 0) + m_maxGCStackSize = envMaxGCStackSize; + memoryManager = new QV4::MemoryManager(this); if (maxCallDepth == -1) { - bool ok = false; + ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); if (!ok || maxCallDepth <= 0) { #if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !QT_HAS_FEATURE(address_sanitizer) @@ -183,24 +230,24 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) Q_ASSERT(maxCallDepth > 0); // reserve space for the JS stack - // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues + // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages, + *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, JSStackLimit + 256*1024); + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024); #endif jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages, + *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); { - bool ok = false; + ok = false; jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok); if (!ok) jitCallCountThreshold = 3; @@ -218,7 +265,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsSymbols = jsAlloca(NJSSymbols); // set up stack limits - jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); + jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value); identifierTable = new IdentifierTable(this); @@ -643,7 +690,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError)); t->defineReadonlyProperty(id_length(), Value::fromInt32(0)); - t->setInternalClass(t->internalClass()->frozen()); + t->setInternalClass(t->internalClass()->cryopreserved()); jsObjects[ThrowerObject] = t; ScopedProperty pd(scope); @@ -651,18 +698,27 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) pd->set = thrower(); functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); + + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) + QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) + QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) + QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); + QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); + + QV4::QObjectWrapper::initializeBindings(this); + + m_delayedCallQueue.init(this); } ExecutionEngine::~ExecutionEngine() { - if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) { - for (auto cu : compilationUnits) { - for (auto f : qAsConst(cu->runtimeFunctions)) - qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString(); - } - } - modules.clear(); + qDeleteAll(m_extensionData); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; delete identifierTable; @@ -679,6 +735,11 @@ ExecutionEngine::~ExecutionEngine() delete jsStack; gcStack->deallocate(); delete gcStack; + +#if QT_CONFIG(qml_xml_http_request) + qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); + m_xmlHttpRequestData = nullptr; +#endif } ExecutionContext *ExecutionEngine::currentContext() const @@ -860,6 +921,13 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) return memoryManager->allocate<RegExpObject>(re); } +#if QT_CONFIG(regularexpression) +Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re) +{ + return memoryManager->allocate<RegExpObject>(re); +} +#endif + Heap::Object *ExecutionEngine::newErrorObject(const Value &value) { return ErrorObject::create<ErrorObject>(this, value, errorCtor()); @@ -1072,6 +1140,12 @@ extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext) return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext)); } +extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine) +{ + auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine)); + return v4StackTrace(engine->currentContext()); +} + QUrl ExecutionEngine::resolvedUrl(const QString &file) { QUrl src(file); @@ -1269,6 +1343,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va const QByteArray &targetType, void **result); static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); +static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap); static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value) { @@ -1305,7 +1380,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { - return qVariantFromValue<QObject *>(wrapper->object()); + return QVariant::fromValue<QObject *>(wrapper->object()); } else if (object->as<QV4::QQmlContextWrapper>()) { return QVariant(); } else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) { @@ -1336,7 +1411,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int } } - return qVariantFromValue<QList<QObject*> >(list); + return QVariant::fromValue<QList<QObject*> >(list); } else if (typeHint == QMetaType::QJsonArray) { return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } @@ -1379,8 +1454,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int QV4::ScopedObject o(scope, value); Q_ASSERT(o); - if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) + if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) { +#if QT_CONFIG(regularexpression) + if (typeHint != QMetaType::QRegExp) + return re->toQRegularExpression(); +#endif return re->toQRegExp(); + } if (createJSValueForObjects) return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue())); @@ -1442,35 +1522,6 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V return result; } -static QV4::ReturnedValue arrayFromVariantList(QV4::ExecutionEngine *e, const QVariantList &list) -{ - QV4::Scope scope(e); - QV4::ScopedArrayObject a(scope, e->newArrayObject()); - int len = list.count(); - a->arrayReserve(len); - QV4::ScopedValue v(scope); - for (int ii = 0; ii < len; ++ii) - a->arrayPut(ii, (v = scope.engine->fromVariant(list.at(ii)))); - - a->setArrayLengthUnchecked(len); - return a.asReturnedValue(); -} - -static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QVariantMap &map) -{ - QV4::Scope scope(e); - QV4::ScopedObject o(scope, e->newObject()); - QV4::ScopedString s(scope); - QV4::ScopedValue v(scope); - for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) { - s = e->newString(iter.key()); - o->put(s, (v = e->fromVariant(iter.value()))); - } - return o.asReturnedValue(); -} - -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) { int type = variant.userType(); @@ -1520,6 +1571,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr))); case QMetaType::QRegExp: return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr))); +#endif case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); #if QT_CONFIG(qml_sequence_object) @@ -1534,9 +1589,9 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) } #endif case QMetaType::QVariantList: - return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); + return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: - return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); + return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr)); case QMetaType::QJsonValue: return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr)); case QMetaType::QJsonObject: @@ -1597,6 +1652,11 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return retn->asReturnedValue(); #endif + if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { + QSequentialIterable lst = variant.value<QSequentialIterable>(); + return sequentialIterableToJS(this, lst); + } + if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); } @@ -1661,9 +1721,8 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian s = v4->newIdentifier(it.key()); key = s->propertyKey(); v = variantToJS(v4, it.value()); - uint idx = key->asArrayIndex(); - if (idx < UINT_MAX) - o->arraySet(idx, v); + if (key->isArrayIndex()) + o->arraySet(key->asArrayIndex(), v); else o->insertMember(s, v); } @@ -1676,99 +1735,23 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) { Q_ASSERT(data != nullptr); - // check if it's one of the types we know - switch (QMetaType::Type(type)) { - case QMetaType::UnknownType: - case QMetaType::Void: - return QV4::Encode::undefined(); - case QMetaType::Nullptr: - case QMetaType::VoidStar: - return QV4::Encode::null(); - case QMetaType::Bool: - return QV4::Encode(*reinterpret_cast<const bool*>(data)); - case QMetaType::Int: - return QV4::Encode(*reinterpret_cast<const int*>(data)); - case QMetaType::UInt: - return QV4::Encode(*reinterpret_cast<const uint*>(data)); - case QMetaType::LongLong: - return QV4::Encode(double(*reinterpret_cast<const qlonglong*>(data))); - case QMetaType::ULongLong: -#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 -#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") - return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) - return QV4::Encode(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#else - return QV4::Encode(double(*reinterpret_cast<const qulonglong*>(data))); -#endif - case QMetaType::Double: - return QV4::Encode(*reinterpret_cast<const double*>(data)); - case QMetaType::QString: - return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); - case QMetaType::QByteArray: - return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue(); - case QMetaType::Float: - return QV4::Encode(*reinterpret_cast<const float*>(data)); - case QMetaType::Short: - return QV4::Encode((int)*reinterpret_cast<const short*>(data)); - case QMetaType::UShort: - return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(data)); - case QMetaType::Char: - return QV4::Encode((int)*reinterpret_cast<const char*>(data)); - case QMetaType::UChar: - return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(data)); - case QMetaType::QChar: - return QV4::Encode((int)(*reinterpret_cast<const QChar*>(data)).unicode()); - case QMetaType::QStringList: - return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(data))); - case QMetaType::QVariantList: - return variantListToJS(this, *reinterpret_cast<const QVariantList *>(data)); - case QMetaType::QVariantMap: - return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(data)); - case QMetaType::QDateTime: - return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(data))); - case QMetaType::QDate: - return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(data)))); - case QMetaType::QRegExp: - return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(data))); - case QMetaType::QObjectStar: - return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data)); - case QMetaType::QVariant: + QVariant variant(type, data); + if (QMetaType::Type(variant.type()) == QMetaType::QVariant) { + // unwrap it: this is tested in QJSEngine, and makes the most sense for + // end-user code too. return variantToJS(this, *reinterpret_cast<const QVariant*>(data)); - case QMetaType::QJsonValue: - return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(data)); - case QMetaType::QJsonObject: - return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(data)); - case QMetaType::QJsonArray: - return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(data)); - default: - if (type == qMetaTypeId<QJSValue>()) { - return QJSValuePrivate::convertedToValue(this, *reinterpret_cast<const QJSValue*>(data)); - } else { - QByteArray typeName = QMetaType::typeName(type); - if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) { - return QV4::Encode::null(); - } - QMetaType mt(type); - if (auto metaObject = mt.metaObject()) { - auto flags = mt.flags(); - if (flags & QMetaType::IsGadget) { - return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), metaObject, type); - } else if (flags & QMetaType::PointerToQObject) { - return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data)); - } - } - if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { - auto v = QVariant(type, data); - QSequentialIterable lst = v.value<QSequentialIterable>(); - return sequentialIterableToJS(this, lst); - } - // Fall back to wrapping in a QVariant. - return QV4::Encode(newVariantObject(QVariant(type, data))); - } } - Q_UNREACHABLE(); - return 0; + return fromVariant(variant); +} + +int ExecutionEngine::maxJSStackSize() const +{ + return m_maxJSStackSize; +} + +int ExecutionEngine::maxGCStackSize() const +{ + return m_maxGCStackSize; } ReturnedValue ExecutionEngine::global() @@ -1776,13 +1759,12 @@ ReturnedValue ExecutionEngine::global() return globalObject->asReturnedValue(); } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const QUrl &url) { QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url, &cacheError)) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit; - jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString())); - return jsUnit; + return ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString())); } QFile f(QQmlFile::urlToLocalFileOrQrc(url)); @@ -1800,69 +1782,26 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(con } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule( + const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp) { QList<QQmlJS::DiagnosticMessage> diagnostics; - auto unit = compileModule(/*debugMode*/debugger() != nullptr, url.toString(), sourceCode, sourceTimeStamp, &diagnostics); + auto unit = Compiler::Codegen::compileModule(/*debugMode*/debugger() != nullptr, url.toString(), + sourceCode, sourceTimeStamp, &diagnostics); for (const QQmlJS::DiagnosticMessage &m : diagnostics) { if (m.isError()) { - throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn); + throwSyntaxError(m.message, url.toString(), m.line, m.column); return nullptr; } else { - qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn + qWarning() << url << ':' << m.line << ':' << m.column << ": warning: " << m.message; } } - return unit; -} - -#endif // ifndef V4_BOOTSTRAP - -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QString &url, const QString &sourceCode, - const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) -{ - QQmlJS::Engine ee; - QQmlJS::Lexer lexer(&ee); - lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false); - QQmlJS::Parser parser(&ee); - - const bool parsed = parser.parseModule(); - - if (diagnostics) - *diagnostics = parser.diagnosticMessages(); - - if (!parsed) - return nullptr; - - QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); - if (!moduleNode) { - // if parsing was successful, and we have no module, then - // the file was empty. - if (diagnostics) - diagnostics->clear(); - return nullptr; - } - - using namespace QV4::Compiler; - Compiler::Module compilerModule(debugMode); - compilerModule.unitFlags |= CompiledData::Unit::IsESModule; - compilerModule.sourceTimeStamp = sourceTimeStamp; - JSUnitGenerator jsGenerator(&compilerModule); - Codegen cg(&jsGenerator, /*strictMode*/true); - cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule); - auto errors = cg.errors(); - if (diagnostics) - *diagnostics << errors; - - if (!errors.isEmpty()) - return nullptr; - return cg.generateCompilationUnit(); + return ExecutableCompilationUnit::create(std::move(unit)); } -#ifndef V4_BOOTSTRAP - -void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit) +void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit) { // Injection can happen from the QML type loader thread for example, but instantiation and // evaluation must be limited to the ExecutionEngine's thread. @@ -1870,7 +1809,7 @@ void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::Compilatio modules.insert(moduleUnit->finalUrl(), moduleUnit); } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer) const +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const { QUrl url = QQmlTypeLoader::normalize(_url); if (referrer) @@ -1883,7 +1822,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(cons return *existingModule; } -QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer) { QUrl url = QQmlTypeLoader::normalize(_url); if (referrer) @@ -1905,6 +1844,131 @@ QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const return newModule; } +void ExecutionEngine::initQmlGlobalObject() +{ + initializeGlobal(); + freezeObject(*globalObject); +} + +void ExecutionEngine::initializeGlobal() +{ + QV4::Scope scope(this); + QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); + + QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine())); + globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + +#if QT_CONFIG(qml_locale) + QQmlLocale::registerStringLocaleCompare(this); + QQmlDateExtension::registerExtension(this); + QQmlNumberExtension::registerExtension(this); +#endif + +#if QT_CONFIG(qml_xml_http_request) + qt_add_domexceptions(this); + m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); +#endif + + qt_add_sqlexceptions(this); + + { + for (uint i = 0; i < globalObject->internalClass()->size; ++i) { + if (globalObject->internalClass()->nameMap.at(i).isString()) { + QV4::PropertyKey id = globalObject->internalClass()->nameMap.at(i); + m_illegalNames.insert(id.toQString()); + } + } + } +} + +const QSet<QString> &ExecutionEngine::illegalNames() const +{ + return m_illegalNames; +} + +void ExecutionEngine::setQmlEngine(QQmlEngine *engine) +{ + m_qmlEngine = engine; + initQmlGlobalObject(); +} + +static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) +{ + if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen) + return; + + QV4::Scope scope(v4); + + bool instanceOfObject = false; + QV4::ScopedObject p(scope, object->getPrototypeOf()); + while (p) { + if (p->d() == v4->objectPrototype()->d()) { + instanceOfObject = true; + break; + } + p = p->getPrototypeOf(); + } + if (!instanceOfObject) + return; + + Heap::InternalClass *frozen = object->internalClass()->frozen(); + object->setInternalClass(frozen); // Immediately assign frozen to prevent it from getting GC'd + + QV4::ScopedObject o(scope); + for (uint i = 0; i < frozen->size; ++i) { + if (!frozen->nameMap.at(i).isStringOrSymbol()) + continue; + o = *object->propertyData(i); + if (o) + freeze_recursive(v4, o); + } +} + +void ExecutionEngine::freezeObject(const QV4::Value &value) +{ + QV4::Scope scope(this); + QV4::ScopedObject o(scope, value); + freeze_recursive(this, o); +} + +void ExecutionEngine::startTimer(const QString &timerName) +{ + if (!m_time.isValid()) + m_time.start(); + m_startedTimers[timerName] = m_time.elapsed(); +} + +qint64 ExecutionEngine::stopTimer(const QString &timerName, bool *wasRunning) +{ + if (!m_startedTimers.contains(timerName)) { + *wasRunning = false; + return 0; + } + *wasRunning = true; + qint64 startedAt = m_startedTimers.take(timerName); + return m_time.elapsed() - startedAt; +} + +int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint16 column) +{ + const QString key = file + QString::number(line) + QString::number(column); + int number = m_consoleCount.value(key, 0); + number++; + m_consoleCount.insert(key, number); + return number; +} + +void ExecutionEngine::setExtensionData(int index, Deletable *data) +{ + if (m_extensionData.count() <= index) + m_extensionData.resize(index + 1); + + if (m_extensionData.at(index)) + delete m_extensionData.at(index); + + m_extensionData[index] = data; +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. @@ -1980,6 +2044,13 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) *reinterpret_cast<QRegExp *>(data) = r->toQRegExp(); return true; } break; +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { + *reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression(); + return true; + } break; +#endif case QMetaType::QObjectStar: { const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>(); if (qobjectWrapper || value->isNull()) { @@ -2144,6 +2215,23 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v return wrapper->object(); } -#endif // ifndef V4_BOOTSTRAP +struct QV4EngineRegistrationData +{ + QV4EngineRegistrationData() : extensionCount(0) {} + + QMutex mutex; + int extensionCount; +}; +Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData); + +QMutex *ExecutionEngine::registrationMutex() +{ + return ®istrationData()->mutex; +} + +int ExecutionEngine::registerExtension() +{ + return registrationData()->extensionCount++; +} QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 86367c0ece..d233347060 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -53,25 +53,82 @@ #include "qv4global_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" +#include "qv4stackframe_p.h" #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> -#ifndef V4_BOOTSTRAP -# include "qv4function_p.h" -# include <private/qv8engine_p.h> -# include <private/qv4compileddata_p.h> -#endif +#include "qv4function_p.h" +#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> namespace WTF { class BumpPointerAllocator; class PageAllocation; } +#define V4_DEFINE_EXTENSION(dataclass, datafunction) \ + static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \ + { \ + static int extensionId = -1; \ + if (extensionId == -1) { \ + QV4::ExecutionEngine::registrationMutex()->lock(); \ + if (extensionId == -1) \ + extensionId = QV4::ExecutionEngine::registerExtension(); \ + QV4::ExecutionEngine::registrationMutex()->unlock(); \ + } \ + dataclass *rv = (dataclass *)engine->extensionData(extensionId); \ + if (!rv) { \ + rv = new dataclass(engine); \ + engine->setExtensionData(extensionId, rv); \ + } \ + return rv; \ + } \ + + QT_BEGIN_NAMESPACE -class QV8Engine; +namespace QV4 { struct QObjectMethod; } + +// Used to allow a QObject method take and return raw V4 handles without having to expose +// 48 in the public API. +// Use like this: +// class MyClass : public QObject { +// Q_OBJECT +// ... +// Q_INVOKABLE void myMethod(QQmlV4Function*); +// }; +// The QQmlV8Function - and consequently the arguments and return value - only remains +// valid during the call. If the return value isn't set within myMethod(), the will return +// undefined. + +class QQmlV4Function +{ +public: + int length() const { return callData->argc(); } + QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } + void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } + QV4::ExecutionEngine *v4engine() const { return e; } +private: + friend struct QV4::QObjectMethod; + QQmlV4Function(); + QQmlV4Function(const QQmlV4Function &); + QQmlV4Function &operator=(const QQmlV4Function &); + + QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e) + : callData(callData), retVal(retVal), e(e) + { + callData->thisObject = QV4::Encode::undefined(); + } + + QV4::CallData *callData; + QV4::Value *retVal; + QV4::ExecutionEngine *e; +}; + class QQmlError; class QJSEngine; class QQmlEngine; @@ -112,10 +169,6 @@ public: WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - enum { - JSStackLimit = 4*1024*1024, - GCStackLimit = 2*1024*1024 - }; WTF::PageAllocation *jsStack; WTF::PageAllocation *gcStack; @@ -128,14 +181,8 @@ public: Function *globalCode; -#ifdef V4_BOOTSTRAP - QJSEngine *jsEngine() const; - QQmlEngine *qmlEngine() const; -#else // !V4_BOOTSTRAP QJSEngine *jsEngine() const { return publicEngine; } - QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; } -#endif // V4_BOOTSTRAP - QV8Engine *v8Engine; + QQmlEngine *qmlEngine() const { return m_qmlEngine; } QJSEngine *publicEngine; enum JSObjects { @@ -438,9 +485,7 @@ public: Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } -#ifndef V4_BOOTSTRAP - QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; -#endif + QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits; quint32 m_engineId; @@ -464,7 +509,7 @@ public: // but any time a QObject is wrapped a second time in another engine, we have to do // bookkeeping. MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) const bool m_canAllocateExecutableMemory; #endif @@ -520,6 +565,9 @@ public: Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re); Heap::RegExpObject *newRegExpObject(const QRegExp &re); +#if QT_CONFIG(regularexpression) + Heap::RegExpObject *newRegExpObject(const QRegularExpression &re); +#endif Heap::Object *newErrorObject(const Value &value); Heap::Object *newErrorObject(const QString &message); @@ -588,11 +636,14 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); + int maxJSStackSize() const; + int maxGCStackSize() const; + bool checkStackLimits(); bool canJIT(Function *f = nullptr) { -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) if (!m_canAllocateExecutableMemory) return false; if (f) @@ -605,42 +656,86 @@ public: } QV4::ReturnedValue global(); + void initQmlGlobalObject(); + void initializeGlobal(); + + void freezeObject(const QV4::Value &value); + + // Return the list of illegal id names (the names of the properties on the global object) + const QSet<QString> &illegalNames() const; + +#if QT_CONFIG(qml_xml_http_request) + void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } +#endif + + void setQmlEngine(QQmlEngine *engine); + + QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } + + // used for console.time(), console.timeEnd() + void startTimer(const QString &timerName); + qint64 stopTimer(const QString &timerName, bool *wasRunning); + + // used for console.count() + int consoleCountHelper(const QString &file, quint16 line, quint16 column); + + struct Deletable { + virtual ~Deletable() {} + }; + + static QMutex *registrationMutex(); + static int registerExtension(); + + void setExtensionData(int, Deletable *); + Deletable *extensionData(int index) const + { + if (index < m_extensionData.count()) + return m_extensionData[index]; + else + return nullptr; + } double localTZA = 0.0; // local timezone, initialized at startup - static QQmlRefPointer<CompiledData::CompilationUnit> compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); -#ifndef V4_BOOTSTRAP - QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url); - QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); + QQmlRefPointer<ExecutableCompilationUnit> compileModule(const QUrl &url); + QQmlRefPointer<ExecutableCompilationUnit> compileModule( + const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); mutable QMutex moduleMutex; - QHash<QUrl, QQmlRefPointer<CompiledData::CompilationUnit>> modules; - void injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit); - QQmlRefPointer<CompiledData::CompilationUnit> moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr) const; - QQmlRefPointer<CompiledData::CompilationUnit> loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr); -#endif + QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules; + void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit); + QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; + QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; #endif + QSet<QString> m_illegalNames; int jitCallCountThreshold; // used by generated Promise objects to handle 'then' events QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler; -}; -// This is a trick to tell the code generators that functions taking a NoThrowContext won't -// throw exceptions and therefore don't need a check after the call. -#ifndef V4_BOOTSTRAP -struct NoThrowEngine : public ExecutionEngine -{ -}; -#else -struct NoThrowEngine; +#if QT_CONFIG(qml_xml_http_request) + void *m_xmlHttpRequestData; #endif + QQmlEngine *m_qmlEngine; + + QQmlDelayedCallQueue m_delayedCallQueue; + + QElapsedTimer m_time; + QHash<QString, qint64> m_startedTimers; + + QHash<QString, quint32> m_consoleCount; + + QVector<Deletable *> m_extensionData; + + int m_maxJSStackSize = 4 * 1024 * 1024; + int m_maxGCStackSize = 2 * 1024 * 1024; +}; #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index b5cfea8863..788897bdad 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -69,9 +69,20 @@ struct Q_QML_EXPORT EngineBase { CppStackFrame *currentStackFrame = nullptr; Value *jsStackTop = nullptr; + + // The JIT expects hasException and isInterrupted to be in the same 32bit word in memory. quint8 hasException = false; - quint8 writeBarrierActive = false; + // isInterrupted is expected to be set from a different thread +#if defined(Q_ATOMIC_INT8_IS_SUPPORTED) + QAtomicInteger<quint8> isInterrupted = false; quint16 unused = 0; +#elif defined(Q_ATOMIC_INT16_IS_SUPPORTED) + quint8 unused = 0; + QAtomicInteger<quint16> isInterrupted = false; +#else +# error V4 needs either 8bit or 16bit atomics. +#endif + quint8 isExecutingInRegExpJIT = false; quint8 padding[3]; MemoryManager *memoryManager = nullptr; @@ -136,6 +147,7 @@ Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8); Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, isInterrupted) + sizeof(EngineBase::isInterrupted) <= offsetof(EngineBase, hasException) + 4); } 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/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index c836d121e3..7ee6f39aa2 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -147,9 +147,7 @@ bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const } ExecutableAllocator::ExecutableAllocator() - : mutex(QMutex::NonRecursive) -{ -} + = default; ExecutableAllocator::~ExecutableAllocator() { diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index 013c6d7120..f98f2c7d33 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -130,7 +130,7 @@ public: private: QMultiMap<size_t, Allocation*> freeAllocations; QMap<quintptr, ChunkOfPages*> chunks; - mutable QMutex mutex; + mutable QRecursiveMutex mutex; }; } diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index c0ce125741..79e2ec2a5d 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. +** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,48 +37,31 @@ ** ****************************************************************************/ -#include "qv4compileddata_p.h" -#include <private/qv4value_p.h> -#ifndef V4_BOOTSTRAP +#include "qv4executablecompilationunit_p.h" + #include <private/qv4engine_p.h> -#include <private/qv4function_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4lookup_p.h> -#include <private/qv4regexpobject_p.h> #include <private/qv4regexp_p.h> -#include <private/qqmlpropertycache_p.h> -#include <private/qqmltypeloader_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4objectproto_p.h> #include <private/qqmlengine_p.h> -#include <private/qv4vme_moth_p.h> -#include <private/qv4module_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#include "qv4compilationunitmapper_p.h" -#include <QQmlPropertyMap> -#include <QDateTime> -#include <QFile> -#include <QFileInfo> -#include <QScopedValueRollback> -#include <QStandardPaths> -#include <QDir> -#include <private/qv4identifiertable_p.h> -#endif -#include <private/qqmlirbuilder_p.h> -#include <QCoreApplication> -#include <QCryptographicHash> -#include <QSaveFile> -#include <QScopeGuard> - -// generated by qmake: -#include "qml_compile_hash_p.h" - -#include <algorithm> - -QT_BEGIN_NAMESPACE +#include <private/qqmlscriptdata_p.h> +#include <private/qv4module_p.h> +#include <private/qv4compilationunitmapper_p.h> +#include <private/qml_compile_hash_p.h> +#include <private/qqmltypewrapper_p.h> -namespace QV4 { +#include <QtQml/qqmlfile.h> +#include <QtQml/qqmlpropertymap.h> -namespace CompiledData { +#include <QtCore/qdir.h> +#include <QtCore/qstandardpaths.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qscopeguard.h> +#include <QtCore/qcryptographichash.h> #if defined(QML_COMPILE_HASH) # ifdef Q_OS_LINUX @@ -87,49 +70,29 @@ namespace CompiledData { __attribute__((section(".qml_compile_hash"))) # endif const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; -static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); +static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, + "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); #else # error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" #endif +QT_BEGIN_NAMESPACE -CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, const QString &finalUrlString) -{ - setUnitData(unitData, nullptr, fileName, finalUrlString); -} - -CompilationUnit::~CompilationUnit() -{ -#ifndef V4_BOOTSTRAP - unlink(); -#endif +namespace QV4 { - if (data) { - if (data->qmlUnit() != qmlData) - free(const_cast<QmlUnit *>(qmlData)); - qmlData = nullptr; +ExecutableCompilationUnit::ExecutableCompilationUnit() = default; -#ifndef V4_BOOTSTRAP - if (!(data->flags & QV4::CompiledData::Unit::StaticData)) - free(const_cast<Unit *>(data)); -#else - // Unconditionally free the memory. In the dev tools we create units that have - // the flag set and will be saved to disk, so intended to persist later. - free(const_cast<Unit *>(data)); -#endif - } - data = nullptr; -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - delete [] constants; - constants = nullptr; -#endif +ExecutableCompilationUnit::ExecutableCompilationUnit( + CompiledData::CompilationUnit &&compilationUnit) + : CompiledData::CompilationUnit(std::move(compilationUnit)) +{} - delete [] imports; - imports = nullptr; +ExecutableCompilationUnit::~ExecutableCompilationUnit() +{ + unlink(); } -#ifndef V4_BOOTSTRAP -QString CompilationUnit::localCacheFilePath(const QUrl &url) +QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url) { const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); @@ -140,7 +103,32 @@ QString CompilationUnit::localCacheFilePath(const QUrl &url) return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; } -QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) +static QString toString(QV4::ReturnedValue v) +{ + Value val = Value::fromReturnedValue(v); + QString result; + if (val.isInt32()) + result = QLatin1String("int "); + else if (val.isDouble()) + result = QLatin1String("double "); + if (val.isEmpty()) + result += QLatin1String("empty"); + else + result += val.toQStringNoThrow(); + return result; +} + +static void dumpConstantTable(const StaticValue *constants, uint count) +{ + QDebug d = qDebug(); + d.nospace() << right; + for (uint i = 0; i < count; ++i) { + d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": " + << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; + } +} + +QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) { this->engine = engine; engine->compilationUnits.insert(this); @@ -154,14 +142,17 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < stringCount; ++i) runtimeStrings[i] = engine->newString(stringAt(i)); - runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; + runtimeRegularExpressions + = new QV4::Value[data->regexpTableSize]; // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value)); + memset(runtimeRegularExpressions, 0, + data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); uint f = re->flags; const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f); - runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags); + runtimeRegularExpressions[i] = QV4::RegExp::create( + engine, stringAt(re->stringIndex), flags); } if (data->lookupTableSize) { @@ -171,7 +162,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup *l = runtimeLookups + i; - Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags)); + CompiledData::Lookup::Type type + = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags)); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -185,15 +177,26 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) } if (data->jsClassTableSize) { - runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); + runtimeClasses + = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize + * sizeof(QV4::Heap::InternalClass *)); // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); + memset(runtimeClasses, 0, + data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; - const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); - runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object); + const CompiledData::JSClassMember *member + = data->jsClassAt(i, &memberCount); + runtimeClasses[i] + = engine->internalClasses(QV4::ExecutionEngine::Class_Object); for (int j = 0; j < memberCount; ++j, ++member) - runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); + runtimeClasses[i] + = runtimeClasses[i]->addMember( + engine->identifierTable->asPropertyKey( + runtimeStrings[member->nameOffset]), + member->isAccessor + ? QV4::Attr_Accessor + : QV4::Attr_Data); } } @@ -214,21 +217,25 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) // first locals const quint32_le *localsIndices = compiledBlock->localsTable(); for (quint32 j = 0; j < compiledBlock->nLocals; ++j) - ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), Attr_NotConfigurable); + ic = ic->addMember( + engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), + Attr_NotConfigurable); runtimeBlocks[i] = ic->d(); } static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Constant table"; - Moth::dumpConstantTable(constants, data->constantTableSize); + dumpConstantTable(constants, data->constantTableSize); qDebug() << "=== String table"; for (uint i = 0, end = totalStringCount(); i < end; ++i) qDebug() << " " << i << ":" << runtimeStrings[i]->toQString(); qDebug() << "=== Closure table"; for (uint i = 0; i < data->functionTableSize; ++i) qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString(); - qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); + qDebug() << "root function at index " + << (data->indexOfRootFunction != -1 + ? data->indexOfRootFunction : 0); } if (data->indexOfRootFunction != -1) @@ -237,7 +244,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) return nullptr; } -Heap::Object *CompilationUnit::templateObjectAt(int index) const +Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const { Q_ASSERT(index < int(data->templateObjectTableSize)); if (!templateObjects.size()) @@ -267,7 +274,7 @@ Heap::Object *CompilationUnit::templateObjectAt(int index) const return templateObjects.at(index); } -void CompilationUnit::unlink() +void ExecutableCompilationUnit::unlink() { if (engine) nextCompilationUnit.remove(); @@ -285,15 +292,18 @@ void CompilationUnit::unlink() if (runtimeLookups) { for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup &l = runtimeLookups[i]; - if (l.getter == QV4::QObjectWrapper::lookupGetter) { + if (l.getter == QV4::QObjectWrapper::lookupGetter + || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) pc->release(); - } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) { + } else if (l.getter == QQmlValueTypeWrapper::lookupGetter + || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) pc->release(); } - if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) { + if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty + || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) pc->release(); } @@ -309,20 +319,23 @@ void CompilationUnit::unlink() engine = nullptr; qmlEngine = nullptr; - free(runtimeStrings); - runtimeStrings = nullptr; + delete [] runtimeLookups; runtimeLookups = nullptr; + + for (QV4::Function *f : qAsConst(runtimeFunctions)) + f->destroy(); + runtimeFunctions.clear(); + + free(runtimeStrings); + runtimeStrings = nullptr; delete [] runtimeRegularExpressions; runtimeRegularExpressions = nullptr; free(runtimeClasses); runtimeClasses = nullptr; - for (QV4::Function *f : qAsConst(runtimeFunctions)) - f->destroy(); - runtimeFunctions.clear(); } -void CompilationUnit::markObjects(QV4::MarkStack *markStack) +void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) { if (runtimeStrings) { for (uint i = 0, end = totalStringCount(); i < end; ++i) @@ -331,7 +344,7 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) } if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(markStack); + Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack); } if (runtimeClasses) { for (uint i = 0; i < data->jsClassTableSize; ++i) @@ -354,11 +367,11 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) runtimeLookups[i].markObjects(markStack); } - if (m_module) - m_module->mark(markStack); + if (auto mod = module()) + mod->mark(markStack); } -IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) +IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) { IdentifierHash namedObjectCache(engine); const CompiledData::Object *component = objectAt(componentObjectIndex); @@ -370,7 +383,7 @@ IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObje return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } -void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) +void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) { this->qmlEngine = qmlEngine; @@ -417,7 +430,7 @@ void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) totalObjectCount = objectCount; } -bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const +bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const { if (!dependencyHasher) { for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { @@ -426,16 +439,13 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe } return true; } - QCryptographicHash hash(QCryptographicHash::Md5); - if (!dependencyHasher(&hash)) - return false; - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum)); - return memcmp(data->dependencyMD5Checksum, checksum.constData(), - sizeof(data->dependencyMD5Checksum)) == 0; + const QByteArray checksum = dependencyHasher(); + return checksum.size() == sizeof(data->dependencyMD5Checksum) + && memcmp(data->dependencyMD5Checksum, checksum.constData(), + sizeof(data->dependencyMD5Checksum)) == 0; } -QStringList CompilationUnit::moduleRequests() const +QStringList ExecutableCompilationUnit::moduleRequests() const { QStringList requests; requests.reserve(data->moduleRequestTableSize); @@ -444,10 +454,10 @@ QStringList CompilationUnit::moduleRequests() const return requests; } -Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) +Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) { - if (isESModule() && m_module) - return m_module; + if (isESModule() && module()) + return module(); if (data->indexOfRootFunction < 0) return nullptr; @@ -459,7 +469,7 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this)); if (isESModule()) - m_module = module->d(); + setModule(module->d()); for (const QString &request: moduleRequests()) { auto dependentModuleUnit = engine->loadModule(QUrl(request), this); @@ -472,12 +482,12 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) const uint importCount = data->importEntryTableSize; if (importCount > 0) { - imports = new const Value *[importCount]; - memset(imports, 0, importCount * sizeof(Value *)); + imports = new const StaticValue *[importCount]; + memset(imports, 0, importCount * sizeof(StaticValue *)); } for (uint i = 0; i < importCount; ++i) { const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); + auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); importName = runtimeStrings[entry.importName]; const Value *valuePtr = dependentModuleUnit->resolveExport(importName); if (!valuePtr) { @@ -491,7 +501,7 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); + auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); if (!dependentModuleUnit) return nullptr; @@ -507,26 +517,10 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine) return module->d(); } -const Value *CompilationUnit::resolveExport(QV4::String *exportName) -{ - QVector<ResolveSetEntry> resolveSet; - return resolveExportRecursively(exportName, &resolveSet); -} - -QStringList CompilationUnit::exportedNames() const +const Value *ExecutableCompilationUnit::resolveExportRecursively( + QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) { - QStringList names; - QVector<const CompiledData::CompilationUnit*> exportNameSet; - getExportedNamesRecursively(&names, &exportNameSet); - names.sort(); - auto last = std::unique(names.begin(), names.end()); - names.erase(last, names.end()); - return names; -} - -const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) -{ - if (!m_module) + if (!module()) return nullptr; for (const auto &entry: *resolveSet) @@ -536,22 +530,24 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, (*resolveSet) << ResolveSetEntry(this, exportName); if (exportName->toQString() == QLatin1String("*")) - return &m_module->self; + return &module()->self; Scope scope(engine); - if (auto localExport = lookupNameInExportTable(data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) { + if (auto localExport = lookupNameInExportTable( + data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) { ScopedString localName(scope, runtimeStrings[localExport->localName]); - uint index = m_module->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey()); + uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey()); if (index == UINT_MAX) return nullptr; - if (index >= m_module->scope->locals.size) - return imports[index - m_module->scope->locals.size]; - return &m_module->scope->locals[index]; + if (index >= module()->scope->locals.size) + return &(imports[index - module()->scope->locals.size]->asValue<Value>()); + return &module()->scope->locals[index]; } - if (auto indirectExport = lookupNameInExportTable(data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(indirectExport->moduleRequest)), this); + if (auto indirectExport = lookupNameInExportTable( + data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { + auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this); if (!dependentModuleUnit) return nullptr; ScopedString importName(scope, runtimeStrings[indirectExport->importName]); @@ -566,7 +562,7 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, for (uint i = 0; i < data->starExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); + auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); if (!dependentModuleUnit) return nullptr; @@ -585,7 +581,8 @@ const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, return starResolution; } -const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const +const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable( + const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const { const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize; auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) { @@ -596,7 +593,9 @@ const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *f return matchingExport; } -void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit*> *exportNameSet, bool includeDefaultExport) const +void ExecutableCompilationUnit::getExportedNamesRecursively( + QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet, + bool includeDefaultExport) const { if (exportNameSet->contains(this)) return; @@ -620,21 +619,21 @@ void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<co for (uint i = 0; i < data->starExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this); + auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); if (!dependentModuleUnit) return; dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false); } } -void CompilationUnit::evaluate() +void ExecutableCompilationUnit::evaluate() { QV4::Scope scope(engine); - QV4::Scoped<Module> module(scope, m_module); - module->evaluate(); + QV4::Scoped<Module> mod(scope, module()); + mod->evaluate(); } -void CompilationUnit::evaluateModuleRequests() +void ExecutableCompilationUnit::evaluateModuleRequests() { for (const QString &request: moduleRequests()) { auto dependentModuleUnit = engine->loadModule(QUrl(request), this); @@ -646,7 +645,7 @@ void CompilationUnit::evaluateModuleRequests() } } -bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) +bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) { if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); @@ -662,20 +661,23 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS if (!mappedUnit) continue; - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - const Unit *oldData = data; + const CompiledData::Unit * const oldDataPtr + = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data + : nullptr; + const CompiledData::Unit *oldData = data; auto dataPtrRevert = qScopeGuard([this, oldData](){ setUnitData(oldData); }); setUnitData(mappedUnit); - if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + if (data->sourceFileIndex != 0 + && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { *errorString = QStringLiteral("QML source file has moved to a different location."); continue; } dataPtrRevert.dismiss(); - free(const_cast<Unit*>(oldDataPtr)); + free(const_cast<CompiledData::Unit*>(oldDataPtr)); backingFile.reset(cacheFile.take()); return true; } @@ -683,17 +685,8 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS return false; } -#endif // V4_BOOTSTRAP - -#if defined(V4_BOOTSTRAP) -bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) -#else -bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) -#endif +bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) { - errorString->clear(); - -#if !defined(V4_BOOTSTRAP) if (data->sourceTimeStamp == 0) { *errorString = QStringLiteral("Missing time stamp for source file"); return false; @@ -703,168 +696,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) *errorString = QStringLiteral("File has to be a local file."); return false; } - const QString outputFileName = localCacheFilePath(unitUrl); -#endif - -#if QT_CONFIG(temporaryfile) - // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(outputFileName); - if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - *errorString = cacheFile.errorString(); - return false; - } - - QByteArray modifiedUnit; - modifiedUnit.resize(data->unitSize); - memcpy(modifiedUnit.data(), data, data->unitSize); - const char *dataPtr = modifiedUnit.data(); - Unit *unitPtr; - memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); - unitPtr->flags |= Unit::StaticData; - - qint64 headerWritten = cacheFile.write(modifiedUnit); - if (headerWritten != modifiedUnit.size()) { - *errorString = cacheFile.errorString(); - return false; - } - if (!cacheFile.commit()) { - *errorString = cacheFile.errorString(); - return false; - } - - return true; -#else - Q_UNUSED(outputFileName) - *errorString = QStringLiteral("features.temporaryfile is disabled."); - return false; -#endif // QT_CONFIG(temporaryfile) -} - -void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit, - const QString &fileName, const QString &finalUrlString) -{ - data = unitData; - qmlData = nullptr; -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - delete [] constants; -#endif - constants = nullptr; - m_fileName.clear(); - m_finalUrlString.clear(); - if (!data) - return; - - qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - Value *bigEndianConstants = new Value[data->constantTableSize]; - const quint64_le *littleEndianConstants = data->constants(); - for (uint i = 0; i < data->constantTableSize; ++i) - bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); - constants = bigEndianConstants; -#else - constants = reinterpret_cast<const Value*>(data->constants()); -#endif - - m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); - m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); -} - -#ifndef V4_BOOTSTRAP -QString Binding::valueAsString(const CompilationUnit *unit) const -{ - switch (type) { - case Type_Script: - case Type_String: - return unit->stringAt(stringIndex); - case Type_Null: - return QStringLiteral("null"); - case Type_Boolean: - return value.b ? QStringLiteral("true") : QStringLiteral("false"); - case Type_Number: - return QString::number(valueAsNumber(unit->constants)); - case Type_Invalid: - return QString(); -#if !QT_CONFIG(translation) - case Type_TranslationById: - case Type_Translation: - return unit->stringAt(unit->unitData()->translations()[value.translationDataIndex].stringIndex); -#else - case Type_TranslationById: { - const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex]; - QByteArray id = unit->stringAt(translation.stringIndex).toUtf8(); - return qtTrId(id.constData(), translation.number); - } - case Type_Translation: { - const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex]; - // This code must match that in the qsTr() implementation - const QString &path = unit->fileName(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5) - : QStringRef(); - QByteArray contextUtf8 = context.toUtf8(); - QByteArray comment = unit->stringAt(translation.commentIndex).toUtf8(); - QByteArray text = unit->stringAt(translation.stringIndex).toUtf8(); - return QCoreApplication::translate(contextUtf8.constData(), text.constData(), - comment.constData(), translation.number); - } -#endif - default: - break; - } - return QString(); -} - -//reverse of Lexer::singleEscape() -QString Binding::escapedString(const QString &string) -{ - QString tmp = QLatin1String("\""); - for (int i = 0; i < string.length(); ++i) { - const QChar &c = string.at(i); - switch (c.unicode()) { - case 0x08: - tmp += QLatin1String("\\b"); - break; - case 0x09: - tmp += QLatin1String("\\t"); - break; - case 0x0A: - tmp += QLatin1String("\\n"); - break; - case 0x0B: - tmp += QLatin1String("\\v"); - break; - case 0x0C: - tmp += QLatin1String("\\f"); - break; - case 0x0D: - tmp += QLatin1String("\\r"); - break; - case 0x22: - tmp += QLatin1String("\\\""); - break; - case 0x27: - tmp += QLatin1String("\\\'"); - break; - case 0x5C: - tmp += QLatin1String("\\\\"); - break; - default: - tmp += c; - break; - } - } - tmp += QLatin1Char('\"'); - return tmp; -} - -QString Binding::valueAsScriptString(const CompilationUnit *unit) const -{ - if (type == Type_String) - return escapedString(unit->stringAt(stringIndex)); - else - return valueAsString(unit); + return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( + [&unitUrl, errorString](const char *data, quint32 size) { + return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data, + size, errorString); + }); } /*! @@ -902,7 +739,8 @@ bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engi } if (!compilationUnit) return false; - hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum)); + hash->addData(compilationUnit->data->md5Checksum, + sizeof(compilationUnit->data->md5Checksum)); return true; } @@ -938,93 +776,106 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e return true; } -#endif - -void CompilationUnit::destroy() +QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const { -#if !defined(V4_BOOTSTRAP) - if (qmlEngine) - QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); - else + using namespace CompiledData; + switch (binding->type) { + case Binding::Type_Script: + case Binding::Type_String: + return stringAt(binding->stringIndex); + case Binding::Type_Null: + return QStringLiteral("null"); + case Binding::Type_Boolean: + return binding->value.b ? QStringLiteral("true") : QStringLiteral("false"); + case Binding::Type_Number: + return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest); + case Binding::Type_Invalid: + return QString(); +#if !QT_CONFIG(translation) + case Binding::Type_TranslationById: + case Binding::Type_Translation: + return stringAt( + data->translations()[binding->value.translationDataIndex].stringIndex); +#else + case Binding::Type_TranslationById: { + const TranslationData &translation + = data->translations()[binding->value.translationDataIndex]; + QByteArray id = stringAt(translation.stringIndex).toUtf8(); + return qtTrId(id.constData(), translation.number); + } + case Binding::Type_Translation: { + const TranslationData &translation + = data->translations()[binding->value.translationDataIndex]; + // This code must match that in the qsTr() implementation + const QString &path = fileName(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5) + : QStringRef(); + QByteArray contextUtf8 = context.toUtf8(); + QByteArray comment = stringAt(translation.commentIndex).toUtf8(); + QByteArray text = stringAt(translation.stringIndex).toUtf8(); + return QCoreApplication::translate(contextUtf8.constData(), text.constData(), + comment.constData(), translation.number); + } #endif - delete this; + default: + break; + } + return QString(); } - -void Unit::generateChecksum() +QString ExecutableCompilationUnit::bindingValueAsScriptString( + const CompiledData::Binding *binding) const { -#ifndef V4_BOOTSTRAP - QCryptographicHash hash(QCryptographicHash::Md5); - - const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); - - const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset; - hash.addData(dataPtr, unitSize - checksummableDataOffset); - - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(md5Checksum)); - memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum)); -#else - memset(md5Checksum, 0, sizeof(md5Checksum)); -#endif + return (binding->type == CompiledData::Binding::Type_String) + ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) + : bindingValueAsString(binding); } -bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const +bool ExecutableCompilationUnit::verifyHeader( + const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString) { -#ifndef V4_BOOTSTRAP - if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) { + if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) { *errorString = QStringLiteral("Magic bytes in the header do not match"); return false; } - if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2") + .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); return false; } - if (qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + if (unit->qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2") + .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); return false; } - if (sourceTimeStamp) { + if (unit->sourceTimeStamp) { // Files from the resource system do not have any time stamps, so fall back to the application // executable. if (!expectedSourceTimeStamp.isValid()) expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) { + if (expectedSourceTimeStamp.isValid() + && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) { *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); return false; } } #if defined(QML_COMPILE_HASH) - if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) { + if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) { *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); return false; } #else #error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" #endif - return true; -#else - Q_UNUSED(expectedSourceTimeStamp) - Q_UNUSED(errorString) - return false; -#endif } -Location &Location::operator=(const QQmlJS::AST::SourceLocation &astLocation) -{ - line = astLocation.startLine; - column = astLocation.startColumn; - return *this; -} - -} - -} +} // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h new file mode 100644 index 0000000000..6eef3b12c3 --- /dev/null +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QV4EXECUTABLECOMPILATIONUNIT_P_H +#define QV4EXECUTABLECOMPILATIONUNIT_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/qv4compileddata_p.h> +#include <private/qv4identifier_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qintrusivelist_p.h> +#include <private/qqmlpropertycachevector_p.h> +#include <private/qqmltype_p.h> +#include <private/qqmlnullablevalue_p.h> + +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 +// 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. +struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> +{ + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; +}; + +class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit, + public QQmlRefCount +{ + Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit) +public: + friend class QQmlRefPointer<ExecutableCompilationUnit>; + + static QQmlRefPointer<ExecutableCompilationUnit> create( + CompiledData::CompilationUnit &&compilationUnit) + { + return QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit(std::move(compilationUnit)), + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + } + + static QQmlRefPointer<ExecutableCompilationUnit> create() + { + return QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit, + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + } + + QIntrusiveListNode nextCompilationUnit; + ExecutionEngine *engine = nullptr; + QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. + + // url() and fileName() shall be used to load the actual QML/JS code or to show errors or + // warnings about that code. They include any potential URL interceptions and thus represent the + // "physical" location of the code. + // + // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code + // They are _not_ intercepted and thus represent the "logical" name for the code. + + QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } + QUrl finalUrl() const + { + if (m_finalUrl.isNull) + m_finalUrl = QUrl(finalUrlString()); + return m_finalUrl; + } + + QV4::Lookup *runtimeLookups = nullptr; + QVector<QV4::Function *> runtimeFunctions; + QVector<QV4::Heap::InternalClass *> runtimeBlocks; + mutable QVector<QV4::Heap::Object *> templateObjects; + mutable QQmlNullableValue<QUrl> m_url; + mutable QQmlNullableValue<QUrl> m_finalUrl; + + // QML specific fields + QQmlPropertyCacheVector propertyCaches; + QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } + + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + + // index is object index. This allows fast access to the + // property data when initializing bindings, avoiding expensive + // lookups by string (property name). + 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 + QHash<int, IdentifierHash> namedObjectsPerComponentCache; + inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); + + void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); + + int totalBindingsCount = 0; // Number of bindings used in this type + int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int totalObjectCount = 0; // Number of objects explicitly instantiated + + QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; + ResolvedTypeReferenceMap resolvedTypes; + ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } + + bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; + + int metaTypeId = -1; + int listMetaTypeId = -1; + bool isRegisteredWithEngine = false; + + QScopedPointer<CompilationUnitMapper> backingFile; + + // --- interface for QQmlPropertyCacheCreator + using CompiledObject = CompiledData::Object; + using CompiledFunction = CompiledData::Function; + + int objectCount() const { return qmlData->nObjects; } + const CompiledObject *objectAt(int index) const + { + return qmlData->objectAt(index); + } + + int importCount() const { return qmlData->nImports; } + const CompiledData::Import *importAt(int index) const + { + return qmlData->importAt(index); + } + + Heap::Object *templateObjectAt(int index) const; + + struct FunctionIterator + { + FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index) + : unit(unit), object(object), index(index) {} + const CompiledData::Unit *unit; + const CompiledObject *object; + int index; + + const CompiledFunction *operator->() const + { + return unit->functionAt(object->functionOffsetTable()[index]); + } + + void operator++() { ++index; } + bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } + bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } + }; + + FunctionIterator objectFunctionsBegin(const CompiledObject *object) const + { + return FunctionIterator(data, object, 0); + } + + FunctionIterator objectFunctionsEnd(const CompiledObject *object) const + { + return FunctionIterator(data, object, object->nFunctions); + } + + bool isESModule() const + { + return data->flags & CompiledData::Unit::IsESModule; + } + + bool isSharedLibrary() const + { + return data->flags & CompiledData::Unit::IsSharedLibrary; + } + + QStringList moduleRequests() const; + Heap::Module *instantiate(ExecutionEngine *engine); + const Value *resolveExport(QV4::String *exportName) + { + QVector<ResolveSetEntry> resolveSet; + return resolveExportRecursively(exportName, &resolveSet); + } + + QStringList exportedNames() const + { + QStringList names; + QVector<const ExecutableCompilationUnit*> exportNameSet; + getExportedNamesRecursively(&names, &exportNameSet); + names.sort(); + auto last = std::unique(names.begin(), names.end()); + names.erase(last, names.end()); + return names; + } + + void evaluate(); + void evaluateModuleRequests(); + + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); + void unlink(); + + void markObjects(MarkStack *markStack); + + bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); + + static QString localCacheFilePath(const QUrl &url); + bool saveToDisk(const QUrl &unitUrl, QString *errorString); + + QString bindingValueAsString(const CompiledData::Binding *binding) const; + QString bindingValueAsScriptString(const CompiledData::Binding *binding) const; + double bindingValueAsNumber(const CompiledData::Binding *binding) const + { + if (binding->type != CompiledData::Binding::Type_Number) + return 0.0; + return constants[binding->value.constantValueIndex].doubleValue(); + } + + static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, + QString *errorString); + +protected: + quint32 totalStringCount() const + { return data->stringTableSize; } + +private: + struct ResolveSetEntry + { + ResolveSetEntry() {} + ResolveSetEntry(ExecutableCompilationUnit *module, QV4::String *exportName) + : module(module), exportName(exportName) {} + ExecutableCompilationUnit *module = nullptr; + QV4::String *exportName = nullptr; + }; + + ExecutableCompilationUnit(); + ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit); + ~ExecutableCompilationUnit(); + + const Value *resolveExportRecursively(QV4::String *exportName, + QVector<ResolveSetEntry> *resolveSet); + + QUrl urlAt(int index) const { return QUrl(stringAt(index)); } + + Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); + const CompiledData::ExportEntry *lookupNameInExportTable( + const CompiledData::ExportEntry *firstExportEntry, int tableSize, + QV4::String *name) const; + + void getExportedNamesRecursively( + QStringList *names, QVector<const ExecutableCompilationUnit *> *exportNameSet, + bool includeDefaultExport = true) const; +}; + +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlRefPointer<QQmlPropertyCache> propertyCache() const; + QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + + void doDynamicTypeCheck(); +}; + +IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) + return createNamedObjectsPerComponent(componentObjectIndex); + return *it; +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1bd4329fe8..aeb4835c40 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -73,24 +73,19 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg return result; } -Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) +Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function) { - quint16 traceSlotCount = 0; -#if QT_CONFIG(qml_tracing) - traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing() - ? 1 - : function->nTraceInfos; -#endif - quint8 *storage = new quint8[sizeof(Function) + traceSlotCount]; - return new(storage) Function(engine, unit, function); + return new Function(engine, unit, function); } void Function::destroy() { - delete[] reinterpret_cast<quint8 *>(this); + delete this; } -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) +Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function) : FunctionData(unit) , compiledFunction(function) , codeData(function->code()) @@ -105,19 +100,12 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *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; - -#if QT_CONFIG(qml_tracing) - if (tracingEnabled()) { - for (uint i = 0; i < function->nTraceInfos; ++i) - *traceInfo(i) = 0; - } -#endif } Function::~Function() @@ -160,8 +148,11 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); - for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { + internalClass = internalClass->addMember( + engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), + Attr_NotConfigurable); + } Scope scope(engine); ScopedString arg(scope); @@ -188,22 +179,4 @@ QQmlSourceLocation Function::sourceLocation() const return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } -QString Function::traceInfoToString() -{ - QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':'); - if (!tracingEnabled()) - return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount); - if (compiledFunction->nTraceInfos == 0) - return info + QLatin1String(" none.\n"); - - info += QLatin1Char('\n'); - for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) { - auto bits = QString::number(*traceInfo(i), 2); - if (bits.size() < 8) - bits.prepend(QString(8 - bits.size(), '0')); - info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits); - } - return info; -} - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index f8125a58f8..51960863c4 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -51,8 +51,9 @@ // #include "qv4global_p.h" -#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <private/qv4context_p.h> +#include <private/qv4string_p.h> namespace JSC { class MacroAssemblerCodeRef; @@ -65,9 +66,13 @@ struct QQmlSourceLocation; namespace QV4 { struct Q_QML_EXPORT FunctionData { - CompiledData::CompilationUnit *compilationUnit; + CompiledData::CompilationUnitBase *compilationUnit; - FunctionData(CompiledData::CompilationUnit *compilationUnit) + // Intentionally require an ExecutableCompilationUnit but save only a pointer to + // CompilationUnitBase. This is so that we can take advantage of the standard layout + // of CompilationUnitBase in the JIT. Furthermore we can safely static_cast to + // ExecutableCompilationUnit where we need it. + FunctionData(ExecutableCompilationUnit *compilationUnit) : compilationUnit(compilationUnit) {} }; @@ -76,12 +81,24 @@ Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value); struct Q_QML_EXPORT Function : public FunctionData { private: - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function); ~Function(); public: const CompiledData::Function *compiledFunction; + QV4::ExecutableCompilationUnit *executableCompilationUnit() const + { + // This is safe: We require an ExecutableCompilationUnit in the ctor. + return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit); + } + + QV4::Heap::String *runtimeString(uint i) const + { + return compilationUnit->runtimeStrings[i]; + } + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); const char *codeData; @@ -96,20 +113,21 @@ public: int interpreterCallCount = 0; bool isEval = false; - static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + static Function *create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, + const CompiledData::Function *function); void destroy(); // used when dynamically assigning signal handlers (QQmlConnection) 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); - inline QString sourceFile() const { return compilationUnit->fileName(); } - inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } + inline QString sourceFile() const { return executableCompilationUnit()->fileName(); } + inline QUrl finalUrl() const { return executableCompilationUnit()->finalUrl(); } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } @@ -121,32 +139,7 @@ public: { if (compiledFunction->nestedFunctionIndex == std::numeric_limits<uint32_t>::max()) return nullptr; - return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; - } - - Q_NEVER_INLINE QString traceInfoToString(); - - quint8 *traceInfo(uint i) - { -#if QT_CONFIG(qml_tracing) - Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0)); - return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i; -#else - Q_UNUSED(i); - return nullptr; -#endif - } - - quint32 traceInfoCount() const - { return compiledFunction->nTraceInfos; } - - bool tracingEnabled() const - { -#if QT_CONFIG(qml_tracing) - return traceInfoCount() != CompiledData::Function::NoTracing(); -#else - return false; -#endif + return executableCompilationUnit()->runtimeFunctions[compiledFunction->nestedFunctionIndex]; } }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 41a21ba379..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; @@ -135,13 +135,13 @@ void Heap::FunctionObject::setFunction(Function *f) { if (f) { function = f; - function->compilationUnit->addref(); + function->executableCompilationUnit()->addref(); } } void Heap::FunctionObject::destroy() { if (function) - function->compilationUnit->release(); + function->executableCompilationUnit()->release(); Object::destroy(); } @@ -229,7 +229,7 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) +QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) { QString arguments; QString body; @@ -273,14 +273,15 @@ QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngin if (engine->hasException) return nullptr; - return cg.generateCompilationUnit(); + return ExecutableCompilationUnit::create(cg.generateCompilationUnit()); } ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit + = parse(engine, argv, argc, Type_Function); if (engine->hasException) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index e03d49c74d..c99cad8e33 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -87,11 +87,11 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { } Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); - void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); - void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); - void init(QV4::ExecutionContext *scope, const QString &name); - void init(); - void destroy(); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); + Q_QML_PRIVATE_EXPORT void init(); + Q_QML_PRIVATE_EXPORT void destroy(); void setFunction(Function *f); @@ -244,7 +244,7 @@ protected: Type_Function, Type_Generator }; - static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); + static QQmlRefPointer<ExecutableCompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); }; struct FunctionPrototype: FunctionObject @@ -260,7 +260,7 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct IndexedBuiltinFunction : FunctionObject +struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 14caa6953f..4eee6f4338 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -58,7 +58,7 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje { ExecutionEngine *engine = f->engine(); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); if (engine->hasException) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index d47393b3bb..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 @@ -63,10 +64,6 @@ #include <qtqmlglobal.h> #include <private/qtqmlglobal_p.h> -#ifdef QT_QML_BOOTSTRAPPED -#define V4_BOOTSTRAP -#endif - #if defined(Q_CC_MSVC) #include <float.h> #include <math.h> @@ -82,53 +79,9 @@ inline bool isfinite(double d) { return _finite(d); } inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #endif -// Decide whether to enable or disable the JIT - -// White list architectures -// -// NOTE: This should match the logic in qv4targetplatform_p.h! - -#if defined(Q_PROCESSOR_X86_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_INTEGRITY)) -# if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4) -# define V4_ENABLE_JIT -# elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2 // clang 3.5 and later will set this if the core supports the Thumb-2 ISA. -# define V4_ENABLE_JIT -# endif -#elif defined(Q_PROCESSOR_ARM_64) && (QT_POINTER_SIZE == 8) -# if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY) -# define V4_ENABLE_JIT -# endif -//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -//# define V4_ENABLE_JIT -#endif - -// check FPU with double precision on ARM platform -#if (defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)) && defined(V4_ENABLE_JIT) && defined(__ARM_FP) && (__ARM_FP <= 0x04) -# undef V4_ENABLE_JIT -#endif - -// Black list some platforms -#if defined(V4_ENABLE_JIT) -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# undef V4_ENABLE_JIT -#endif -#endif - -// For debug purposes: add CONFIG+=force-compile-jit to qmake's command-line to always compile the JIT. -#if defined(V4_FORCE_COMPILE_JIT) && !defined(V4_ENABLE_JIT) -# define V4_ENABLE_JIT -#endif - // Do certain things depending on whether the JIT is enabled or disabled -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) #define ENABLE_YARR_JIT 1 #define ENABLE_JIT 1 #define ENABLE_ASSEMBLER 1 @@ -255,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; @@ -280,20 +228,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -enum class ObservedTraceValues : quint8 { - Integer = 1 << 0, - Boolean = 1 << 1, - Double = 1 << 2, - Other = 1 << 3, - TypeMask = Integer | Boolean | Double | Other, - - TruePathTaken = 1 << 0, - FalsePathTaken = 1 << 1, - - ArrayWasAccessed = 1 << 7, - ArrayAccessNeededFallback = 1 << 6, -}; - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, @@ -403,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/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 5db5bd46ec..c3d7165f71 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -39,29 +39,19 @@ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" #include "qv4string_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) , identifierTable(table) { - refCount.store(1); - alloc = primeForNumBits(numBits); + refCount.storeRelaxed(1); + alloc = qPrimeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); identifierTable->addIdentifierHash(this); @@ -72,7 +62,7 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other) , numBits(other->numBits) , identifierTable(other->identifierTable) { - refCount.store(1); + refCount.storeRelaxed(1); alloc = other->alloc; entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); @@ -92,7 +82,7 @@ IdentifierHash::IdentifierHash(ExecutionEngine *engine) void IdentifierHash::detach() { - if (!d || d->refCount == 1) + if (!d || d->refCount.loadAcquire() == 1) return; IdentifierHashData *newData = new IdentifierHashData(d); if (d && !d->refCount.deref()) @@ -110,7 +100,7 @@ IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) if (grow) { ++d->numBits; - int newAlloc = primeForNumBits(d->numBits); + int newAlloc = qPrimeForNumBits(d->numBits); IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 4305bc4647..21b47c3909 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -38,28 +38,18 @@ ****************************************************************************/ #include "qv4identifiertable_p.h" #include "qv4symbol_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits) : engine(engine) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); @@ -87,7 +77,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) if (grow) { ++numBits; - int newAlloc = primeForNumBits(numBits); + int newAlloc = qPrimeForNumBits(numBits); Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (uint i = 0; i < alloc; ++i) { @@ -216,9 +206,8 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str) Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const { - uint arrayIdx = i.asArrayIndex(); - if (arrayIdx < UINT_MAX) - return engine->newString(QString::number(arrayIdx)); + if (i.isArrayIndex()) + return engine->newString(QString::number(i.asArrayIndex())); if (!i.isValid()) return nullptr; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 36569b0a60..92face6f94 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -72,13 +72,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_resultObject.set(v4, resultValue(v4)); #if QT_CONFIG(qml_network) - m_network = engine->v8Engine->networkAccessManager(); + if (QQmlEngine *qmlEngine = engine->qmlEngine()) { + m_network = qmlEngine->networkAccessManager(); - QNetworkRequest request; - request.setUrl(url); + QNetworkRequest request; + request.setUrl(url); - m_reply = m_network->get(request); - QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + } else { + finished(); + } #else finished(); #endif @@ -163,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()); @@ -197,7 +200,7 @@ void QV4Include::finished() } /* - Documented in qv8engine.cpp + Documented in qv4engine.cpp */ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc) { @@ -214,7 +217,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons if (argc >= 2 && argv[1].as<QV4::FunctionObject>()) callbackFunction = argv[1]; -#if QT_CONFIG(qml_network) QUrl url(scope.engine->resolvedUrl(argv[0].toQStringNoThrow())); if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); @@ -225,9 +227,13 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext()); if (localFile.isEmpty()) { +#if QT_CONFIG(qml_network) QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction); result = i->result(); - +#else + result = resultValue(scope.engine, NetworkError); + callback(callbackFunction, result); +#endif } else { QScopedPointer<QV4::Script> script; QString error; @@ -252,12 +258,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons callback(callbackFunction, result); } -#else - QV4::ScopedValue result(scope); - result = resultValue(scope.engine, NetworkError); - callback(callbackFunction, result); -#endif - return result->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index a10fda79f2..70849775cb 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -45,27 +45,18 @@ #include "qv4identifiertable_p.h" #include "qv4value_p.h" #include "qv4mm_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - PropertyHashData::PropertyHashData(int numBits) : refCount(1) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); } @@ -299,7 +290,6 @@ void InternalClass::init(ExecutionEngine *engine) void InternalClass::init(Heap::InternalClass *other) { Base::init(); - Q_ASSERT(!other->isFrozen); new (&propertyTable) PropertyHash(other->propertyTable); new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap); new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData); @@ -573,22 +563,6 @@ Heap::InternalClass *InternalClass::sealed() if (isSealed) return this; - bool alreadySealed = !extensible; - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - if (attrs.isConfigurable()) { - alreadySealed = false; - break; - } - } - - if (alreadySealed) { - isSealed = true; - return this; - } - Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; Transition &t = lookupOrInsertTransition(temp); @@ -601,14 +575,15 @@ Heap::InternalClass *InternalClass::sealed() Scoped<QV4::InternalClass> ic(scope, engine->newClass(this)); Heap::InternalClass *s = ic->d(); - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - attrs.setConfigurable(false); - s->propertyData.set(i, attrs); + if (!isFrozen) { // freezing also makes all properties non-configurable + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + attrs.setConfigurable(false); + s->propertyData.set(i, attrs); + } } - s->extensible = false; s->isSealed = true; t.lookup = s; @@ -620,28 +595,11 @@ Heap::InternalClass *InternalClass::frozen() if (isFrozen) return this; - bool alreadyFrozen = !extensible; - for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (attrs.isEmpty()) - continue; - if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) { - alreadyFrozen = false; - break; - } - } - - if (alreadyFrozen) { - isSealed = true; - isFrozen = true; - return this; - } - Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen); + Q_ASSERT(t.lookup && t.lookup->isFrozen); return t.lookup; } @@ -658,29 +616,42 @@ Heap::InternalClass *InternalClass::frozen() attrs.setConfigurable(false); f->propertyData.set(i, attrs); } - f->extensible = false; - f->isSealed = true; f->isFrozen = true; t.lookup = f; return f; } -Heap::InternalClass *InternalClass::propertiesFrozen() +InternalClass *InternalClass::canned() +{ + // scope the intermediate result to prevent it from getting garbage collected + Scope scope(engine); + Scoped<QV4::InternalClass> ic(scope, sealed()); + return ic->d()->nonExtensible(); +} + +InternalClass *InternalClass::cryopreserved() { + // scope the intermediate result to prevent it from getting garbage collected Scope scope(engine); - Scoped<QV4::InternalClass> frozen(scope, this); + Scoped<QV4::InternalClass> ic(scope, frozen()); + return ic->d()->canned(); +} + +bool InternalClass::isImplicitlyFrozen() const +{ + if (isFrozen) + return true; + for (uint i = 0; i < size; ++i) { - PropertyAttributes attrs = propertyData.at(i); - if (!nameMap.at(i).isValid()) + const PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) continue; - if (!attrs.isEmpty()) { - attrs.setWritable(false); - attrs.setConfigurable(false); - } - frozen = frozen->changeMember(nameMap.at(i), attrs); + if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) + return false; } - return frozen->d(); + + return true; } Heap::InternalClass *InternalClass::asProtoClass() diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 7bb10f47a3..403702ae55 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -432,7 +432,9 @@ struct InternalClass : Base { Q_REQUIRED_RESULT InternalClass *sealed(); Q_REQUIRED_RESULT InternalClass *frozen(); - Q_REQUIRED_RESULT InternalClass *propertiesFrozen(); + Q_REQUIRED_RESULT InternalClass *canned(); // sealed + nonExtensible + Q_REQUIRED_RESULT InternalClass *cryopreserved(); // frozen + sealed + nonExtensible + bool isImplicitlyFrozen() const; Q_REQUIRED_RESULT InternalClass *asProtoClass(); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 6b8fb509a8..0cda6b864a 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -467,11 +467,11 @@ bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, return false; } - if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { + if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) { l->objectLookupTwoClasses.ic = first.objectLookup.ic; l->objectLookupTwoClasses.ic2 = second.objectLookup.ic; - l->objectLookupTwoClasses.offset = first.objectLookup.offset; - l->objectLookupTwoClasses.offset2 = second.objectLookup.offset; + l->objectLookupTwoClasses.offset = first.objectLookup.index; + l->objectLookupTwoClasses.offset2 = second.objectLookup.index; l->setter = setter0setter0; return true; } @@ -492,11 +492,11 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c return o->put(name, value); } -bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o && o->internalClass == l->objectLookup.ic) { - o->setProperty(engine, l->objectLookup.offset, value); + o->memberData->values.set(engine, l->objectLookup.offset, value); return true; } @@ -507,7 +507,7 @@ bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, co { Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o && o->internalClass == l->objectLookup.ic) { - o->setInlineProperty(engine, l->objectLookup.offset, value); + o->setInlinePropertyWithOffset(engine, l->objectLookup.offset, value); return true; } diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 4fd5e133f7..31c90b31f6 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -54,17 +54,14 @@ #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" - -#if !defined(V4_BOOTSTRAP) #include "qv4object_p.h" #include "qv4internalclass_p.h" -#endif QT_BEGIN_NAMESPACE namespace QV4 { -struct Lookup { +struct Q_QML_PRIVATE_EXPORT Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); @@ -81,8 +78,9 @@ struct Lookup { } markDef; struct { Heap::InternalClass *ic; - quintptr _unused; - int offset; + quintptr unused; + uint index; + uint offset; } objectLookup; struct { quintptr protoId; @@ -92,8 +90,8 @@ struct Lookup { struct { Heap::InternalClass *ic; Heap::InternalClass *ic2; - int offset; - int offset2; + uint offset; + uint offset2; } objectLookupTwoClasses; struct { quintptr protoId; @@ -111,12 +109,14 @@ struct Lookup { struct { Heap::InternalClass *newClass; quintptr protoId; - int offset; + uint offset; + uint unused; } insertionLookup; struct { quintptr _unused; quintptr _unused2; uint index; + uint unused; } indexedLookup; struct { Heap::InternalClass *ic; @@ -151,6 +151,19 @@ struct Lookup { quintptr reserved3; ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); } qmlContextGlobalLookup; + struct { + Heap::Object *qmlTypeWrapper; + quintptr unused2; + } qmlTypeLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + ReturnedValue encodedEnumValue; + } qmlEnumValueLookup; + struct { + Heap::InternalClass *ic; + Heap::Object *qmlScopedEnumWrapper; + } qmlScopedEnumWrapperLookup; }; uint nameIndex; @@ -187,7 +200,7 @@ struct Lookup { static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index d85b30a056..4f22dc7330 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -54,7 +54,6 @@ #include "qv4value_p.h" #include "qv4enginebase_p.h" #include <private/qv4heap_p.h> -#include <private/qv4writebarrier_p.h> #include <private/qv4vtable_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp index 68741e7677..90e1908a84 100644 --- a/src/qml/jsruntime/qv4mapobject.cpp +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -80,7 +80,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, if (!adder) return scope.engine->throwTypeError(); - ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true)); if (scope.hasException()) return Encode::undefined(); Q_ASSERT(iter); @@ -89,7 +89,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, Value *arguments = scope.alloc(2); ScopedValue done(scope); forever { - done = Runtime::method_iteratorNext(scope.engine, iter, obj); + done = Runtime::IteratorNext::call(scope.engine, iter, obj); if (scope.hasException()) break; if (done->toBoolean()) @@ -112,7 +112,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, break; } ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iter, falsey); + return Runtime::IteratorClose::call(scope.engine, iter, falsey); } } return a->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index a60a49a811..6632d69c27 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -52,6 +52,7 @@ #include <qglobal.h> +#include <private/qv4staticvalue_p.h> #include <QtCore/qnumeric.h> #include <QtCore/private/qnumeric_p.h> #include <cmath> @@ -66,43 +67,28 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(add_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(sub_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); - return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); - return Value::fromInt32(result).asReturnedValue(); + if (Q_UNLIKELY(mul_overflow(a, b, &result))) + return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp index 237ada8321..08a1900383 100644 --- a/src/qml/jsruntime/qv4module.cpp +++ b/src/qml/jsruntime/qv4module.cpp @@ -52,7 +52,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(Module); -void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit) +void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit) { Object::init(); @@ -106,7 +106,7 @@ void Module::evaluate() return; d()->evaluated = true; - CompiledData::CompilationUnit *unit = d()->unit; + ExecutableCompilationUnit *unit = d()->unit; unit->evaluateModuleRequests(); diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h index dca0678fe9..aabb2e005e 100644 --- a/src/qml/jsruntime/qv4module_p.h +++ b/src/qml/jsruntime/qv4module_p.h @@ -60,7 +60,7 @@ namespace QV4 { namespace Heap { #define ModuleMembers(class, Member) \ - Member(class, NoMark, CompiledData::CompilationUnit *, unit) \ + Member(class, NoMark, ExecutableCompilationUnit *, unit) \ Member(class, Pointer, CallContext *, scope) \ Member(class, HeapValue, HeapValue, self) \ Member(class, NoMark, bool, evaluated) @@ -68,7 +68,7 @@ namespace Heap { DECLARE_EXPORTED_HEAP_OBJECT(Module, Object) { DECLARE_MARKOBJECTS(Module) - void init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit); + void init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit); }; } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 02524b7da6..89161433ed 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -407,8 +407,8 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h { Heap::Object *o = d(); - uint index = id.asArrayIndex(); - if (index != UINT_MAX) { + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); Scope scope(this); PropertyAttributes attrs; ScopedProperty pd(scope); @@ -432,8 +432,6 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h break; } } else { - Q_ASSERT(!id.isArrayIndex()); - while (1) { auto idx = o->internalClass->findValueOrGetter(id); if (idx.isValid()) { @@ -471,14 +469,13 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) { // This object standard methods in the vtable, so we can take a shortcut // and avoid the calls to getOwnProperty and defineOwnProperty - uint index = id.asArrayIndex(); PropertyAttributes attrs; PropertyIndex propertyIndex{nullptr, nullptr}; - if (index != UINT_MAX) { + if (id.isArrayIndex()) { if (arrayData()) - propertyIndex = arrayData()->getValueOrSetter(index, &attrs); + propertyIndex = arrayData()->getValueOrSetter(id.asArrayIndex(), &attrs); } else { auto member = internalClass()->findValueOrSetter(id); if (member.isValid()) { @@ -547,12 +544,11 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) { // standard object, we can avoid some more checks - uint index = id.asArrayIndex(); - if (index == UINT_MAX) { + if (id.isArrayIndex()) { + r->arraySet(id.asArrayIndex(), value); + } else { ScopedStringOrSymbol s(scope, id.asStringOrSymbol()); r->insertMember(s, value); - } else { - r->arraySet(index, value); } return true; } @@ -787,8 +783,15 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, return lookup->setter(lookup, engine, *object, value); } else if (idx.attrs.isData() && idx.attrs.isWritable()) { lookup->objectLookup.ic = object->internalClass(); - lookup->objectLookup.offset = idx.index; - lookup->setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; + lookup->objectLookup.index = idx.index; + const auto nInline = object->d()->vtable()->nInlineProperties; + if (idx.index < nInline) { + lookup->setter = Lookup::setter0Inline; + lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset; + } else { + lookup->setter = Lookup::setter0MemberData; + lookup->objectLookup.offset = idx.index - nInline; + } return lookup->setter(lookup, engine, *object, value); } else { // ### handle setter diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index bee4aadafe..f3375929a3 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -79,21 +79,21 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { } const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const { - Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties); + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); return reinterpret_cast<const Value *>(this) + indexWithOffset; } const Value *inlinePropertyData(uint index) const { Q_ASSERT(index < vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; } - void setInlineProperty(ExecutionEngine *e, uint index, Value v) { - Q_ASSERT(index < vtable()->nInlineProperties); - Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Value v) { + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); + Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset; WriteBarrier::write(e, this, prop->data_ptr(), v.asReturnedValue()); } - void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { - Q_ASSERT(index < vtable()->nInlineProperties); - Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Heap::Base *b) { + Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties)); + Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset; WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } @@ -115,7 +115,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setProperty(ExecutionEngine *e, uint index, Value v) { uint nInline = vtable()->nInlineProperties; if (index < nInline) { - setInlineProperty(e, index, v); + setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, v); return; } index -= nInline; @@ -124,7 +124,7 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) { uint nInline = vtable()->nInlineProperties; if (index < nInline) { - setInlineProperty(e, index, b); + setInlinePropertyWithOffset(e, index + vtable()->inlinePropertyOffset, b); return; } index -= nInline; @@ -410,7 +410,7 @@ private: friend struct ObjectPrototype; }; -struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator +struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator { uint arrayIndex = 0; uint memberIndex = 0; @@ -543,13 +543,11 @@ inline const ArrayObject *Value::as() const { return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; } -#ifndef V4_BOOTSTRAP template<> inline ReturnedValue value_convert<Object>(ExecutionEngine *e, const Value &v) { return v.toObject(e)->asReturnedValue(); } -#endif } diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 6b4c3ba71a..3d3b3f413f 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -422,7 +422,7 @@ ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value Scope scope(b); ScopedObject o(scope, a); - o->setInternalClass(o->internalClass()->sealed()); + o->setInternalClass(o->internalClass()->canned()); if (o->arrayData()) { ArrayData::ensureAttributes(o); @@ -448,7 +448,7 @@ ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Valu if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); - o->setInternalClass(o->internalClass()->frozen()); + o->setInternalClass(o->internalClass()->cryopreserved()); if (o->arrayData()) { ArrayData::ensureAttributes(o); @@ -489,7 +489,7 @@ ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Va if (o->isExtensible()) return Encode(false); - if (o->internalClass() != o->internalClass()->sealed()) + if (o->internalClass() != o->internalClass()->canned()) return Encode(false); if (!o->arrayData() || !o->arrayData()->length()) @@ -521,7 +521,7 @@ ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Va if (o->isExtensible()) return Encode(false); - if (o->internalClass() != o->internalClass()->frozen()) + if (!o->internalClass()->isImplicitlyFrozen()) return Encode(false); if (!o->arrayData() || !o->arrayData()->length()) diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index e9515b5b68..8707305dc2 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -74,7 +74,7 @@ struct ObjectCtor: FunctionObject static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; -struct ObjectPrototype: Object +struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index b337243204..26e1074fe3 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -49,7 +49,7 @@ namespace Profiling { FunctionLocation FunctionCall::resolveLocation() const { return FunctionLocation(m_function->name()->toQString(), - m_function->compilationUnit->fileName(), + m_function->executableCompilationUnit()->fileName(), m_function->compiledFunction->location.line, m_function->compiledFunction->location.column); } diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 8461384e9a..ccf7c9210d 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -144,19 +144,19 @@ public: FunctionCall(Function *function, qint64 start, qint64 end) : m_function(function), m_start(start), m_end(end) - { m_function->compilationUnit->addref(); } + { m_function->executableCompilationUnit()->addref(); } FunctionCall(const FunctionCall &other) : m_function(other.m_function), m_start(other.m_start), m_end(other.m_end) - { m_function->compilationUnit->addref(); } + { m_function->executableCompilationUnit()->addref(); } ~FunctionCall() - { m_function->compilationUnit->release(); } + { m_function->executableCompilationUnit()->release(); } FunctionCall &operator=(const FunctionCall &other) { if (&other != this) { - other.m_function->compilationUnit->addref(); - m_function->compilationUnit->release(); + other.m_function->executableCompilationUnit()->addref(); + m_function->executableCompilationUnit()->release(); m_function = other.m_function; m_start = other.m_start; m_end = other.m_end; @@ -189,22 +189,22 @@ public: SentMarker(const SentMarker &other) : m_function(other.m_function) { if (m_function) - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } ~SentMarker() { if (m_function) - m_function->compilationUnit->release(); + m_function->executableCompilationUnit()->release(); } SentMarker &operator=(const SentMarker &other) { if (&other != this) { if (m_function) - m_function->compilationUnit->release(); + m_function->executableCompilationUnit()->release(); m_function = other.m_function; - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } return *this; } @@ -213,7 +213,7 @@ public: { Q_ASSERT(m_function == nullptr); m_function = function; - m_function->compilationUnit->addref(); + m_function->executableCompilationUnit()->addref(); } bool isValid() const diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp index 851fee7bd8..17d218a6eb 100644 --- a/src/qml/jsruntime/qv4promiseobject.cpp +++ b/src/qml/jsruntime/qv4promiseobject.cpp @@ -558,7 +558,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this ScopedFunctionObject reject(scope, capability->d()->reject); ScopedObject itemsObject(scope, argv); - ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true)); + ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); if (!iteratorObject || scope.hasException()) { ScopedObject error(scope); if (scope.hasException()) { @@ -583,7 +583,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this for (;;) { Scope scope(e); ScopedValue nextValue(scope); - doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue)); + doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue)); if (doneValue->toBoolean()) break; @@ -611,7 +611,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -619,7 +619,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -641,7 +641,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -660,7 +660,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -708,7 +708,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi ScopedFunctionObject reject(scope, capability->d()->reject); ScopedObject itemsObject(scope, argv); - ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true)); + ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); if (!iteratorObject) { ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error"))); reject->call(newPromise, error, 1); @@ -719,10 +719,10 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi for (;;) { Scope scope(e); ScopedValue nextValue(scope); - doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue)); + doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue)); if (scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -757,7 +757,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -765,7 +765,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue)); + ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -785,7 +785,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -804,7 +804,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h index 47867765db..b2a2ec3dea 100644 --- a/src/qml/jsruntime/qv4propertykey_p.h +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -113,8 +113,8 @@ public: static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; } static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; } bool isStringOrSymbol() const { return isManaged() && val != 0; } - uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); } - uint isArrayIndex() const { return !isManaged() && val != 0 && static_cast<uint>(val & 0xffffffff) != std::numeric_limits<uint>::max(); } + uint asArrayIndex() const { Q_ASSERT(isArrayIndex()); return static_cast<uint>(val & 0xffffffff); } + uint isArrayIndex() const { return !isManaged() && val != 0; } bool isValid() const { return val != 0; } static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b) { PropertyKey key; key.setM(b); return key; } @@ -124,7 +124,7 @@ public: return m(); } - bool isString() const; + Q_QML_EXPORT bool isString() const; bool isSymbol() const; bool isCanonicalNumericIndexString() const; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 969d44ecf6..e2d3b98ff6 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qv4qmlcontext_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlcontext_p.h> @@ -232,28 +231,32 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } else if (r.type.isValid()) { if (lookup) { if (r.type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); - siinfo->init(e); - if (siinfo->qobjectApi(e)) { + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) { + e->singletonInstance<QObject*>(r.type); lookup->qmlContextSingletonLookup.singleton = static_cast<Heap::Object*>( Value::fromReturnedValue( QQmlTypeWrapper::create(v4, nullptr, r.type) ).heapObject()); } else { - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + QJSValue singleton = e->singletonInstance<QJSValue>(r.type); + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton)); lookup->qmlContextSingletonLookup.singleton = o->d(); } lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; return lookup->qmlContextPropertyGetter(lookup, v4, base); } } - return QQmlTypeWrapper::create(v4, scopeObject, r.type); + result = QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } - Q_ASSERT(!"Unreachable"); + if (lookup) { + lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject()); + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType; + } + return result->asReturnedValue(); } // Fall through @@ -660,6 +663,27 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec return Encode::undefined(); } +ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QObject *scopeObject = qmlContext->qmlScope(); + if (scopeObject && QQmlData::wasDeleted(scopeObject)) + return QV4::Encode::undefined(); + + Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper; + if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) { + l->qmlTypeLookup.qmlTypeWrapper = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + } + + return Value::fromHeapObject(heapObject).asReturnedValue(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 4c8287ef2f..e3e7239fe5 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 63b6435112..c36da3815d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -39,7 +39,7 @@ #include "qv4qobjectwrapper_p.h" -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlstaticmetaobject_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlbinding_p.h> @@ -50,7 +50,6 @@ #include <private/qqmlvaluetypewrapper_p.h> #include <private/qqmllistwrapper_p.h> #include <private/qqmlbuiltinfunctions_p.h> -#include <private/qv8engine_p.h> #include <private/qv4arraybuffer_p.h> #include <private/qv4functionobject_p.h> @@ -58,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> @@ -81,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> @@ -165,10 +167,6 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object double v = 0; property.readProperty(object, &v); return QV4::Encode(v); - } else if (property.isV4Handle()) { - QQmlV4Handle handle; - property.readProperty(object, &handle); - return handle; } else if (property.propType() == qMetaTypeId<QJSValue>()) { QJSValue v; property.readProperty(object, &v); @@ -768,6 +766,8 @@ struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator ~QObjectWrapperOwnPropertyKeyIterator() override = default; PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; +private: + QSet<QByteArray> m_alreadySeen; }; PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) @@ -808,6 +808,11 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro ++propertyIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; + // filter out duplicates due to overloads: + if (m_alreadySeen.contains(method.name())) + continue; + else + m_alreadySeen.insert(method.name()); ExecutionEngine *thatEngine = that->engine(); Scope scope(thatEngine); ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name()))); @@ -1193,15 +1198,14 @@ DEFINE_OBJECT_VTABLE(QObjectWrapper); namespace { -template<typename A, typename B, typename C, typename D, typename E, - typename F, typename G, typename H> -class MaxSizeOf8 { +template<typename A, typename B, typename C, typename D, typename E, typename F, typename G> +class MaxSizeOf7 { template<typename Z, typename X> struct SMax { char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)]; }; public: - static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >); + static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >); }; struct CallArgument { @@ -1232,16 +1236,17 @@ 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[MaxSizeOf8<QVariant, - QString, - QList<QObject *>, - QJSValue, - QQmlV4Handle, - QJsonArray, - QJsonObject, - QJsonValue>::Size]; + char allocData[MaxSizeOf7<QVariant, + QString, + QList<QObject *>, + QJSValue, + QJsonArray, + QJsonObject, + QJsonValue>::Size]; qint64 q_for_alignment; }; @@ -1252,7 +1257,6 @@ private: QVariant *qvariantPtr; QList<QObject *> *qlistPtr; QJSValue *qjsValuePtr; - QQmlV4Handle *handlePtr; QJsonArray *jsonArrayPtr; QJsonObject *jsonObjectPtr; QJsonValue *jsonValuePtr; @@ -1271,7 +1275,8 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index QVarLengthArray<CallArgument, 9> args(argCount + 1); args[0].initAsType(returnType); for (int ii = 0; ii < argCount; ++ii) { - if (!args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii])) { + if (!args[ii + 1].fromValue(argTypes[ii], engine, + callArgs->args[ii].asValue<QV4::Value>())) { qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii); const StackTrace stack = engine->stackTrace(); for (const StackFrame &frame : stack) { @@ -1383,6 +1388,9 @@ static int MatchScore(const QV4::Value &actual, int conversionType) } else if (actual.as<QV4::RegExpObject>()) { switch (conversionType) { case QMetaType::QRegExp: +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: +#endif return 0; default: return 10; @@ -1613,8 +1621,10 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = *attempt; @@ -1688,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; @@ -1721,9 +1733,6 @@ void CallArgument::initAsType(int callType) } else if (callType == qMetaTypeId<QList<QObject *> >()) { type = callType; qlistPtr = new (&allocData) QList<QObject *>(); - } else if (callType == qMetaTypeId<QQmlV4Handle>()) { - type = callType; - handlePtr = new (&allocData) QQmlV4Handle; } else if (callType == QMetaType::QJsonArray) { type = callType; jsonArrayPtr = new (&allocData) QJsonArray(); @@ -1824,9 +1833,6 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q return false; } } - } else if (callType == qMetaTypeId<QQmlV4Handle>()) { - handlePtr = new (&allocData) QQmlV4Handle(value.asReturnedValue()); - type = callType; } else if (callType == QMetaType::QJsonArray) { QV4::ScopedArrayObject a(scope, value); jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a)); @@ -1846,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>>()) { @@ -1864,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) @@ -1951,8 +1962,6 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii)))); array->setArrayLengthUnchecked(list.count()); return array.asReturnedValue(); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - return *handlePtr; } else if (type == QMetaType::QJsonArray) { return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr); } else if (type == QMetaType::QJsonObject) { @@ -2258,8 +2267,10 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = attempt; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 5543c4d5a6..ac9cad2bdb 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -55,9 +55,7 @@ #include <QtCore/qmetatype.h> #include <QtCore/qpair.h> #include <QtCore/qhash.h> -#include <private/qhashedstring_p.h> #include <private/qqmldata_p.h> -#include <private/qqmlpropertycache_p.h> #include <private/qintrusivelist_p.h> #include <private/qv4value_p.h> diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 15dcb602eb..0772770d63 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -148,7 +148,7 @@ ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Valu if (!argc || !argv[0].isObject()) return e->throwTypeError(); - bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); + bool result = Runtime::DeleteProperty_NoThrow::call(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); return Encode(result); } diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 39a2e96b45..c1a42c4afa 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -50,10 +50,13 @@ #include <QtCore/QDebug> #include <QtCore/qregexp.h> +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif #include <cassert> #include <typeinfo> #include <iostream> -#include "qv4alloca_p.h" +#include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE @@ -134,6 +137,25 @@ void Heap::RegExpObject::init(const QRegExp &re) o->initProperties(); } +#if QT_CONFIG(regularexpression) +// Converts a QRegularExpression to a JS RegExp. +// The conversion is not 100% exact since ECMA regexp and QRegularExpression +// have different semantics/flags, but we try to do our best. +void Heap::RegExpObject::init(const QRegularExpression &re) +{ + Object::init(); + + Scope scope(internalClass->engine); + Scoped<QV4::RegExpObject> o(scope, this); + + const uint flags = (re.patternOptions() & QRegularExpression::CaseInsensitiveOption) + ? CompiledData::RegExp::RegExp_IgnoreCase + : CompiledData::RegExp::RegExp_NoFlags; + o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, re.pattern(), flags)); + o->initProperties(); +} +#endif + void RegExpObject::initProperties() { setProperty(Index_LastIndex, Value::fromInt32(0)); @@ -150,6 +172,20 @@ QRegExp RegExpObject::toQRegExp() const return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2); } +#if QT_CONFIG(regularexpression) +// Converts a JS RegExp to a QRegularExpression. +// The conversion is not 100% exact since ECMA regexp and QRegularExpression +// have different semantics/flags, but we try to do our best. +QRegularExpression RegExpObject::toQRegularExpression() const +{ + QRegularExpression::PatternOptions caseSensitivity + = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) + ? QRegularExpression::CaseInsensitiveOption + : QRegularExpression::NoPatternOption; + return QRegularExpression(*value()->pattern, caseSensitivity); +} +#endif + QString RegExpObject::toString() const { QString p = *value()->pattern; @@ -162,13 +198,6 @@ QString RegExpObject::toString() const return p; } -QString RegExpObject::source() const -{ - Scope scope(engine()); - ScopedValue s(scope, get(scope.engine->id_source())); - return s->toQString(); -} - ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str) { QString s = str->toQString(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index a584404c0b..b94889e9f0 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -81,6 +81,9 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { void init(); void init(QV4::RegExp *value); void init(const QRegExp &re); +#if QT_CONFIG(regularexpression) + void init(const QRegularExpression &re); +#endif }; #define RegExpCtorMembers(class, Member) \ @@ -98,7 +101,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { } -struct RegExpObject: Object { +struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) V4_INTERNALCLASS(RegExpObject) @@ -138,8 +141,16 @@ struct RegExpObject: Object { } QRegExp toQRegExp() const; +#if QT_CONFIG(regularexpression) + QRegularExpression toQRegularExpression() const; +#endif QString toString() const; - QString source() const; + QString source() const + { + Scope scope(engine()); + ScopedValue s(scope, get(scope.engine->id_source())); + return s->toQString(); + } Heap::RegExp *value() const { return d()->value; } uint flags() const { return d()->value->flags; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index f7c339dc26..aaa198c62a 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -38,9 +38,8 @@ ****************************************************************************/ #include "qv4global_p.h" -#include "qv4engine_p.h" #include "qv4runtime_p.h" -#ifndef V4_BOOTSTRAP +#include "qv4engine_p.h" #include "qv4object_p.h" #include "qv4objectproto_p.h" #include "qv4globalobject_p.h" @@ -60,11 +59,10 @@ #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" -#include <private/qv8engine_p.h> -#endif #include <QtCore/QDebug> #include <cassert> @@ -223,13 +221,9 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2) #endif // QV4_COUNT_RUNTIME_FUNCTIONS -#ifndef V4_BOOTSTRAP - -Runtime::Runtime() +static QV4::Lookup *runtimeLookup(Function *f, uint i) { -#define INIT_METHOD(returnvalue, name, args) runtimeMethods[name] = reinterpret_cast<void*>(&method_##name); -FOR_EACH_RUNTIME_METHOD(INIT_METHOD) -#undef INIT_METHOD + return f->executableCompilationUnit()->runtimeLookups + i; } void RuntimeHelpers::numberToString(QString *result, double num, int radix) @@ -320,9 +314,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->prepend(QLatin1Char('-')); } -ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) +ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); if (clos->isGenerator()) @@ -330,7 +325,7 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index) +Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedObject o(scope, base.toObject(engine)); @@ -344,14 +339,36 @@ bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, return o->deleteProperty(key); } -bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::DeleteProperty::call(ExecutionEngine *engine, QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + if (!Runtime::DeleteProperty_NoThrow::call(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) +ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name) +{ + if (!Runtime::DeleteName_NoThrow::call(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) { // 11.8.6, 5: rval must be an Object const Object *rhs = rval.as<Object>(); @@ -376,7 +393,7 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val return Encode(result->toBoolean()); } -QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right) { Object *ro = right.objectValue(); if (!ro) @@ -604,13 +621,12 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -ReturnedValue RuntimeHelpers::getTemplateObject(Function *function, int index) +ReturnedValue Runtime::GetTemplateObject::call(Function *function, int index) { - return function->compilationUnit->templateObjectAt(index)->asReturnedValue(); + return function->executableCompilationUnit()->templateObjectAt(index)->asReturnedValue(); } - -void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::StoreProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); QV4::Function *v4Function = engine->currentStackFrame->v4Function; @@ -683,7 +699,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, return o->get(name); } -ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &object, const Value &index) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -704,30 +720,6 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value & return getElementFallback(engine, object, index); } -ReturnedValue Runtime::method_loadElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementIntFallback(engine, object, idx); - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementFallback(engine, object, index); -} - static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); @@ -761,7 +753,7 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return o->put(name, value); } -void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -783,31 +775,7 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, engine->throwTypeError(); } -void Runtime::method_storeElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - } - } - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) - engine->throwTypeError(); -} - -ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) +ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); @@ -830,7 +798,7 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value & return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value) +ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value &iterator, Value *value) { // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true // and the stack unwinding won't close the iterator @@ -866,7 +834,7 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value return Encode(false); } -ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) +ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) { // the return value encodes how to continue the yield* iteration. // true implies iteration is done, false for iteration to continue @@ -903,7 +871,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, if (t->isUndefined()) { // no throw method on the iterator ScopedValue done(scope, Encode(false)); - method_iteratorClose(engine, iterator, done); + IteratorClose::call(engine, iterator, done); if (engine->hasException) return Encode::undefined(); return engine->throwTypeError(); @@ -938,7 +906,7 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, return Encode(false); } -ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) +ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator, const Value &done) { Q_ASSERT(iterator.isObject()); Q_ASSERT(done.isBoolean()); @@ -978,7 +946,7 @@ ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value return originalCompletion(); } -ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator) +ReturnedValue Runtime::DestructureRestElement::call(ExecutionEngine *engine, const Value &iterator) { Q_ASSERT(iterator.isObject()); @@ -988,7 +956,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co uint index = 0; while (1) { ScopedValue n(scope); - ScopedValue done(scope, method_iteratorNext(engine, iterator, n)); + ScopedValue done(scope, IteratorNext::call(engine, iterator, n)); if (engine->hasException) return Encode::undefined(); Q_ASSERT(done->isBoolean()); @@ -1000,7 +968,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co return array->asReturnedValue(); } -void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1010,7 +978,7 @@ void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, con engine->globalObject->put(name, value); } -void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1021,7 +989,7 @@ void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, con engine->throwReferenceError(name); } -ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::LoadProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1041,7 +1009,7 @@ ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value return o->get(name); } -ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -1055,7 +1023,8 @@ static Object *getSuperBase(Scope &scope) return nullptr; } - ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function); + ScopedFunctionObject f( + scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function)); ScopedObject homeObject(scope, f->getHomeObject()); if (!homeObject) { ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); @@ -1084,7 +1053,7 @@ static Object *getSuperBase(Scope &scope) return proto; } -ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) +ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Value &property) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1093,10 +1062,11 @@ ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const V ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return base->get(key, &engine->currentStackFrame->jsFrame->thisObject); + return base->get( + key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); } -void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) +void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1105,12 +1075,46 @@ void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &pr ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject); + bool result = base->put( + key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } -ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t) +ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function *f, int index) +{ + Lookup *l = runtimeLookup(f, index); + return l->globalGetter(l, engine); +} + +ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index) +{ + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); + return l->qmlContextPropertyGetter(l, engine, nullptr); +} + +ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index) +{ + Lookup *l = runtimeLookup(f, index); + return l->getter(l, engine, base); +} + +void Runtime::SetLookupSloppy::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + l->setter(l, engine, const_cast<Value &>(base), value); +} + +void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + if (!l->setter(l, engine, const_cast<Value &>(base), value)) + engine->throwTypeError(); +} + +ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t) { if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) { return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number @@ -1124,8 +1128,6 @@ ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, cons return c->asReturnedValue(); } -#endif // V4_BOOTSTRAP - uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) { Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString()))); @@ -1143,25 +1145,21 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) double dx = RuntimeHelpers::toNumber(x); return dx == y.asDouble(); } else if (x.isBoolean()) { - return Runtime::method_compareEqual(Value::fromDouble((double) x.booleanValue()), y); + return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y); } else if (y.isBoolean()) { - return Runtime::method_compareEqual(x, Value::fromDouble((double) y.booleanValue())); + return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue())); } else { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else Object *xo = x.objectValue(); Object *yo = y.objectValue(); if (yo && (x.isNumber() || x.isString())) { Scope scope(yo->engine()); ScopedValue py(scope, RuntimeHelpers::objectDefaultValue(yo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(x, py); + return Runtime::CompareEqual::call(x, py); } else if (xo && (y.isNumber() || y.isString())) { Scope scope(xo->engine()); ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(xo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(px, y); + return Runtime::CompareEqual::call(px, y); } -#endif } return false; @@ -1177,12 +1175,13 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y) if (x.isNumber()) return y.isNumber() && x.asDouble() == y.asDouble(); - if (x.isManaged()) + if (x.isManaged()) { return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>()); + } return false; } -QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1192,26 +1191,17 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterThan(pl, pr); -#endif + return Runtime::CompareGreaterThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1219,7 +1209,7 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) return dl > dr; } -QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1229,26 +1219,17 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessThan(pl, pr); -#endif + return Runtime::CompareLessThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1256,7 +1237,7 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) return dl < dr; } -QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1266,26 +1247,17 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterEqual(pl, pr); -#endif + return Runtime::CompareGreaterEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1293,7 +1265,7 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) return dl >= dr; } -QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1303,26 +1275,17 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessEqual(pl, pr); -#endif + return Runtime::CompareLessEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1330,22 +1293,21 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) return dl <= dr; } -#ifndef V4_BOOTSTRAP -Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right) +Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_instanceof(engine, left, right)); + ScopedValue v(scope, Instanceof::call(engine, left, right)); return v->booleanValue(); } -uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right) +uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_in(engine, left, right)); + ScopedValue v(scope, In::call(engine, left, right)); return v->booleanValue(); } @@ -1359,33 +1321,36 @@ static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engin return engine->throwTypeError(msg); } -ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc) { Scope scope(engine); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); Value thisObject = Value::undefinedValue(); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, &thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); } -ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index, + Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject)); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } return static_cast<FunctionObject &>(function).call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) +ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); @@ -1404,7 +1369,7 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va return function->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); @@ -1415,17 +1380,22 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V if (engine->hasException) return Encode::undefined(); - if (!f) - return throwPropertyIsNotAFunctionTypeError(engine, thisObject, - engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString()); + if (!f) { + return throwPropertyIsNotAFunctionTypeError( + engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[nameIndex]->toQString()); + } return f->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc) { + const Value *base = &baseRef; Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name( + scope, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject lookupObject(scope, base); if (!lookupObject) { @@ -1437,7 +1407,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, } if (base->isManaged()) { - Managed *m = static_cast<Managed *>(base); + const Managed *m = static_cast<const Managed *>(base); lookupObject = m->internalClass()->prototype; Q_ASSERT(m->internalClass()->prototype); } else { @@ -1461,20 +1431,21 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc) { - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); // ok to have the value on the stack here - Value f = Value::fromReturnedValue(l->getter(l, engine, *base)); + Value f = Value::fromReturnedValue(l->getter(l, engine, base)); if (!f.isFunctionObject()) return engine->throwTypeError(); - return static_cast<FunctionObject &>(f).call(base, argv, argc); + return static_cast<FunctionObject &>(f).call(&base, argv, argc); } -ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc) +ReturnedValue Runtime::CallElement::call(ExecutionEngine *engine, const Value &baseRef, const Value &index, Value *argv, int argc) { + const Value *base = &baseRef; Scope scope(engine); ScopedValue thisObject(scope, base->toObject(engine)); base = thisObject; @@ -1483,14 +1454,14 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, if (engine->hasException) return Encode::undefined(); - ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str)); + ScopedFunctionObject f(scope, static_cast<const Object *>(base)->get(str)); if (!f) return engine->throwTypeError(); return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc) +ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); @@ -1498,11 +1469,12 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(func).call(&undef, argv, argc); } -ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func, + const Value &thisObject, Value argv[], int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return static_cast<const FunctionObject &>(func).call(thisObject, argv, argc); + return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc); } struct CallArgs { @@ -1528,11 +1500,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) } // spread element ++i; - it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1); + it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1); if (scope.engine->hasException) return { nullptr, 0 }; while (1) { - done = Runtime::method_iteratorNext(scope.engine, it, v); + done = Runtime::IteratorNext::call(scope.engine, it, v); if (scope.engine->hasException) return { nullptr, 0 }; Q_ASSERT(done->isBoolean()); @@ -1545,7 +1517,7 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) return { arguments, argCount }; } -ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) { Q_ASSERT(argc >= 1); if (!function.isFunctionObject()) @@ -1559,7 +1531,7 @@ ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Valu return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); } -ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1567,7 +1539,7 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget); } -ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1580,7 +1552,7 @@ ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } -ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *engine) +ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *engine) { // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than // the jitted function, so it can safely do a tail call. @@ -1603,20 +1575,21 @@ ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *en } memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall); + frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall); frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); frame->pendingTailCall = true; return Encode::undefined(); } -void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) +void Runtime::ThrowException::call(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) engine->throwError(value); } -ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value) +ReturnedValue Runtime::TypeofValue::call(ExecutionEngine *engine, const Value &value) { Scope scope(engine); ScopedString res(scope); @@ -1647,83 +1620,110 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & return res.asReturnedValue(); } -QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; - return method_typeofValue(engine, prop); + return TypeofValue::call(engine, prop); } -ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame) +void Runtime::PushCallContext::call(CppStackFrame *frame) { - QV4::Value &accumulator = jsStackFrame[CallData::Accumulator]; - accumulator = accumulator.toObject(engine); - if (engine->hasException) - return Encode::undefined(); - Q_ASSERT(accumulator.isObject()); - const Object &obj = static_cast<const Object &>(accumulator); - ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context); - return context->newWithContext(obj.d())->asReturnedValue(); + frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue(); } -ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) +ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc) { - ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame, blockIndex, - e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); + CallData *jsFrame = engine->currentStackFrame->jsFrame; + Value &newAcc = jsFrame->accumulator.asValue<Value>(); + newAcc = Value::fromHeapObject(acc.toObject(engine)); + if (!engine->hasException) { + Q_ASSERT(newAcc.isObject()); + const Object &obj = static_cast<const Object &>(newAcc); + Value &context = jsFrame->context.asValue<Value>(); + auto ec = static_cast<const ExecutionContext *>(&context); + context = ec->newWithContext(obj.d())->asReturnedValue(); + } + return newAcc.asReturnedValue(); } -ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) +void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex) { - ExecutionEngine *e = parent->engine(); - return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue(); + auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex]; + engine->currentStackFrame->jsFrame->context = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue(); } -ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous) +void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index) { - return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue(); + engine->currentStackFrame->jsFrame->context = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); } +void Runtime::CloneBlockContext::call(ExecutionEngine *engine) +{ + auto frame = engine->currentStackFrame; + auto context = static_cast<Heap::CallContext *>( + Value::fromStaticValue(frame->jsFrame->context).m()); + frame->jsFrame->context = + ExecutionContext::cloneBlockContext(engine, context)->asReturnedValue(); +} -ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index) +void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index) { Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); engine->setScriptContext(c); - return c; + engine->currentStackFrame->jsFrame->context = c; } -ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine) +void Runtime::PopScriptContext::call(ExecutionEngine *engine) { ReturnedValue root = engine->rootContext()->asReturnedValue(); engine->setScriptContext(root); - return root; + engine->currentStackFrame->jsFrame->context = root; } -void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex) +void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); engine->throwReferenceError(name); } -void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) +void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value &v) +{ + if (v.isNullOrUndefined()) + engine->throwTypeError(); +} + +ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t) +{ + if (!t.isObject()) { + if (t.isNullOrUndefined()) { + return engine->globalObject->asReturnedValue(); + } else { + return t.toObject(engine)->asReturnedValue(); + } + } + return t.asReturnedValue(); +} + +void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); } -ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) +ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length) { return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, const QV4::Value *args, int argc) +ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, QV4::Value args[], int argc) { Scope scope(engine); Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]); @@ -1755,7 +1755,8 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId if (arg != ObjectLiteralArgument::Value) { Q_ASSERT(args[2].isInteger()); int functionId = args[2].integerValue(); - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); PropertyKey::FunctionNamePrefix prefix = PropertyKey::None; @@ -1796,9 +1797,11 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId return o.asReturnedValue(); } -ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames) +ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex, + const Value &superClass, Value computedNames[]) { - const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::ExecutableCompilationUnit *unit + = engine->currentStackFrame->v4Function->executableCompilationUnit(); const QV4::CompiledData::Class *cls = unit->unitData()->classAt(classIndex); Scope scope(engine); @@ -1898,20 +1901,20 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde return constructor->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject); return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine) { Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex) +QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex) { const Value *values = engine->currentStackFrame->originalArguments + argIndex; int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; @@ -1920,14 +1923,33 @@ QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, return engine->newArrayObject(values, nValues)->asReturnedValue(); } -ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) +ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); + const auto val + = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id]; + Heap::RegExpObject *ro = engine->newRegExpObject(Value::fromStaticValue(val).as<RegExp>()); return ro->asReturnedValue(); } -#endif // V4_BOOTSTRAP -ReturnedValue Runtime::method_uMinus(const Value &value) +ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj) +{ + if (obj.isObject()) + return obj.asReturnedValue(); + + return obj.toObject(engine)->asReturnedValue(); +} + +Bool Runtime::ToBoolean::call(const Value &obj) +{ + return obj.toBoolean(); +} + +ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v) +{ + return Encode(v.toNumber()); +} + +ReturnedValue Runtime::UMinus::call(const Value &value) { TRACE1(value); @@ -1943,8 +1965,7 @@ ReturnedValue Runtime::method_uMinus(const Value &value) // binary operators -#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) +ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); @@ -1956,7 +1977,7 @@ ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, co return RuntimeHelpers::addHelper(engine, left, right); } -ReturnedValue Runtime::method_sub(const Value &left, const Value &right) +ReturnedValue Runtime::Sub::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1969,7 +1990,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right) return Value::fromDouble(lval - rval).asReturnedValue(); } -ReturnedValue Runtime::method_mul(const Value &left, const Value &right) +ReturnedValue Runtime::Mul::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1982,7 +2003,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right) return Value::fromDouble(lval * rval).asReturnedValue(); } -ReturnedValue Runtime::method_div(const Value &left, const Value &right) +ReturnedValue Runtime::Div::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2003,7 +2024,7 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right) return Value::fromDouble(lval / rval).asReturnedValue(); } -ReturnedValue Runtime::method_mod(const Value &left, const Value &right) +ReturnedValue Runtime::Mod::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2024,7 +2045,43 @@ ReturnedValue Runtime::method_mod(const Value &left, const Value &right) return Value::fromDouble(std::fmod(lval, rval)).asReturnedValue(); } -ReturnedValue Runtime::method_shl(const Value &left, const Value &right) +ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp) +{ + double b = base.toNumber(); + double e = exp.toNumber(); + if (qt_is_inf(e) && (b == 1 || b == -1)) + return Encode(qt_qnan()); + return Encode(pow(b,e)); +} + +ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval & rval)); +} + +ReturnedValue Runtime::BitOr::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval | rval)); +} + +ReturnedValue Runtime::BitXor::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval ^ rval)); +} + +ReturnedValue Runtime::Shl::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2033,7 +2090,7 @@ ReturnedValue Runtime::method_shl(const Value &left, const Value &right) return Encode((int)(lval << rval)); } -ReturnedValue Runtime::method_shr(const Value &left, const Value &right) +ReturnedValue Runtime::Shr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2042,7 +2099,7 @@ ReturnedValue Runtime::method_shr(const Value &left, const Value &right) return Encode((int)(lval >> rval)); } -ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) +ReturnedValue Runtime::UShr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2053,37 +2110,35 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) return Encode(res); } -#endif // V4_BOOTSTRAP - -ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterThan(left, right); + bool r = CompareGreaterThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right) +ReturnedValue Runtime::LessThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessThan(left, right); + bool r = CompareLessThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterEqual(left, right); + bool r = CompareGreaterEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) +ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessEqual(left, right); + bool r = CompareLessEqual::call(left, right); return Encode(r); } @@ -2107,18 +2162,16 @@ struct LazyScope } }; -Bool Runtime::method_compareEqual(const Value &left, const Value &right) +Bool Runtime::CompareEqual::call(const Value &left, const Value &right) { TRACE2(left, right); Value lhs = left; Value rhs = right; -#ifndef V4_BOOTSTRAP LazyScope scope; Value *lhsGuard = nullptr; Value *rhsGuard = nullptr; -#endif redo: if (lhs.asReturnedValue() == rhs.asReturnedValue()) @@ -2148,7 +2201,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) case QV4::Value::QT_ManagedOrUndefined1: case QV4::Value::QT_ManagedOrUndefined2: case QV4::Value::QT_ManagedOrUndefined3: { -#ifndef V4_BOOTSTRAP // RHS: Managed Heap::Base *l = lhs.m(); Heap::Base *r = rhs.m(); @@ -2166,7 +2218,6 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) lhs = lhsGuard->asReturnedValue(); break; } -#endif return false; } case QV4::Value::QT_Empty: @@ -2178,16 +2229,12 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) rhs = Value::fromDouble(rhs.int_32()); // fall through default: // double -#ifndef V4_BOOTSTRAP if (lhs.m()->internalClass->vtable->isStringOrSymbol) { return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; } else { scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); lhs = lhsGuard->asReturnedValue(); } -#else - Q_UNIMPLEMENTED(); -#endif } goto redo; case QV4::Value::QT_Empty: @@ -2216,23 +2263,23 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) } } -ReturnedValue Runtime::method_equal(const Value &left, const Value &right) +ReturnedValue Runtime::Equal::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareEqual(left, right); + bool r = CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right) +ReturnedValue Runtime::NotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = !method_compareEqual(left, right); + bool r = !CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2240,7 +2287,7 @@ ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) return Encode(r); } -ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2248,21 +2295,21 @@ ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &rig return Encode(r); } -Bool Runtime::method_compareNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - return !Runtime::method_compareEqual(left, right); + return !Runtime::CompareEqual::call(left, right); } -Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); return RuntimeHelpers::strictEqual(left, right); } -Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 2be3ebf012..cb89b7e6c0 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -52,8 +52,6 @@ #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4context_p.h" -#include "qv4engine_p.h" #include "qv4math_p.h" #include "qv4runtimeapi_p.h" #include <QtCore/qnumeric.h> @@ -114,20 +112,16 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static Bool strictEqual(const Value &x, const Value &y); static ReturnedValue addHelper(ExecutionEngine *engine, const Value &left, const Value &right); - - static ReturnedValue getTemplateObject(Function *function, int index); }; // type conversion and testing -#ifndef V4_BOOTSTRAP inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint) { if (!value.isObject()) return value.asReturnedValue(); return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint); } -#endif inline double RuntimeHelpers::toNumber(const Value &value) { diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index ceec13a3bb..05ffb84d58 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -51,179 +51,449 @@ // #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE namespace QV4 { typedef uint Bool; -struct NoThrowEngine; -namespace { -template <typename T> -struct ExceptionCheck { - enum { NeedsCheck = 1 }; -}; -// push_catch and pop context methods shouldn't check for exceptions -template <> -struct ExceptionCheck<void (*)(QV4::NoThrowEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> { - enum { NeedsCheck = 0 }; -}; -template <> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> { - enum { NeedsCheck = 0 }; -}; -template <typename A> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B> -struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> { - enum { NeedsCheck = 0 }; -}; -template <typename A, typename B, typename C> -struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { - enum { NeedsCheck = 0 }; -}; -} // anonymous namespace - -#define FOR_EACH_RUNTIME_METHOD(F) \ - /* call */ \ - F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callQmlContextPropertyLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \ - F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \ - F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \ - F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \ - F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ - F(ReturnedValue, callWithReceiver, (ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)) \ - F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ - F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ - F(ReturnedValue, tailCall, (CppStackFrame *frame, ExecutionEngine *engine)) \ - \ - /* construct */ \ - F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ - F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ - \ - /* load & store */ \ - F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ - F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \ - F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ - F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ - F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \ - F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ - F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ - F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \ - \ - /* typeof */ \ - F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ - F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* delete */ \ - F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* exceptions & scopes */ \ - F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \ - F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \ - F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \ - F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \ - F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \ - F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \ - F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \ - \ - /* closures */ \ - F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ - \ - /* function header */ \ - F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ - F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ - F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ - F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \ - \ - /* literals */ \ - F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ - F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, const Value *args, int argc)) \ - F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \ - \ - /* for-in, for-of and array destructuring */ \ - F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ - F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \ - F(ReturnedValue, iteratorNextForYieldStar, (ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)) \ - F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \ - F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \ - \ - /* unary operators */ \ - F(ReturnedValue, uMinus, (const Value &value)) \ - \ - /* binary operators */ \ - F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, sub, (const Value &left, const Value &right)) \ - F(ReturnedValue, mul, (const Value &left, const Value &right)) \ - F(ReturnedValue, div, (const Value &left, const Value &right)) \ - F(ReturnedValue, mod, (const Value &left, const Value &right)) \ - F(ReturnedValue, shl, (const Value &left, const Value &right)) \ - F(ReturnedValue, shr, (const Value &left, const Value &right)) \ - F(ReturnedValue, ushr, (const Value &left, const Value &right)) \ - F(ReturnedValue, greaterThan, (const Value &left, const Value &right)) \ - F(ReturnedValue, lessThan, (const Value &left, const Value &right)) \ - F(ReturnedValue, greaterEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, lessEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, equal, (const Value &left, const Value &right)) \ - F(ReturnedValue, notEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, strictEqual, (const Value &left, const Value &right)) \ - F(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)) \ - \ - /* comparisons */ \ - F(Bool, compareGreaterThan, (const Value &l, const Value &r)) \ - F(Bool, compareLessThan, (const Value &l, const Value &r)) \ - F(Bool, compareGreaterEqual, (const Value &l, const Value &r)) \ - F(Bool, compareLessEqual, (const Value &l, const Value &r)) \ - F(Bool, compareEqual, (const Value &left, const Value &right)) \ - F(Bool, compareNotEqual, (const Value &left, const Value &right)) \ - F(Bool, compareStrictEqual, (const Value &left, const Value &right)) \ - F(Bool, compareStrictNotEqual, (const Value &left, const Value &right)) \ - \ - F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - \ - F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) struct Q_QML_PRIVATE_EXPORT Runtime { - Runtime(); - typedef ReturnedValue (*UnaryOperation)(const Value &value); typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); - typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); + typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *, const Value &left, const Value &right); -#define DEFINE_RUNTIME_METHOD_ENUM(returnvalue, name, args) name, - enum RuntimeMethods { - FOR_EACH_RUNTIME_METHOD(DEFINE_RUNTIME_METHOD_ENUM) - RuntimeMethodCount, - InvalidRuntimeMethod = RuntimeMethodCount + enum class Throws { No, Yes }; + enum class ChangesContext { No, Yes }; + enum class Pure { No, Yes }; + enum class LastArgumentIsOutputValue { No, Yes }; + + template<Throws t, ChangesContext c = ChangesContext::No, Pure p = Pure::No, + LastArgumentIsOutputValue out = LastArgumentIsOutputValue::No> + struct Method + { + static constexpr bool throws = t == Throws::Yes; + static constexpr bool changesContext = c == ChangesContext::Yes; + static constexpr bool pure = p == Pure::Yes; + static constexpr bool lastArgumentIsOutputValue = out == LastArgumentIsOutputValue::Yes; + }; + using PureMethod = Method<Throws::No, ChangesContext::No, Pure::Yes>; + using IteratorMethod = Method<Throws::Yes, ChangesContext::No, Pure::No, + LastArgumentIsOutputValue::Yes>; + + /* call */ + struct Q_QML_PRIVATE_EXPORT CallGlobalLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, uint, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallValue : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallWithReceiver : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallPossiblyDirectEval : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CallWithSpread : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes> + { + static ReturnedValue call(CppStackFrame *, ExecutionEngine *); }; -#undef DEFINE_RUNTIME_METHOD_ENUM - void *runtimeMethods[RuntimeMethodCount]; + /* construct */ + struct Q_QML_PRIVATE_EXPORT Construct : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT ConstructWithSpread : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); + }; - static uint runtimeMethodOffset(RuntimeMethods method) { return method*QT_POINTER_SIZE; } + /* load & store */ + struct Q_QML_PRIVATE_EXPORT StoreNameStrict : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreNameSloppy : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreProperty : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreElement : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StoreSuperProperty : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadSuperConstructor : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LoadGlobalLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, int); + }; + struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, uint); + }; + struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT SetLookupStrict : Method<Throws::Yes> + { + static void call(Function *, const Value &, int, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT SetLookupSloppy : Method<Throws::Yes> + { + static void call(Function *, const Value &, int, const Value &); + }; + + /* typeof */ + struct Q_QML_PRIVATE_EXPORT TypeofValue : PureMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT TypeofName : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *, int); + }; + + /* delete */ + struct Q_QML_PRIVATE_EXPORT DeleteProperty_NoThrow : Method<Throws::No> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeleteProperty : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeleteName_NoThrow : Method<Throws::No> + { + static Bool call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT DeleteName : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Function *, int); + }; + + /* exceptions & scopes */ + struct Q_QML_PRIVATE_EXPORT ThrowException : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(CppStackFrame *); + }; + struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int, int); + }; + struct Q_QML_PRIVATE_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes> + { + static void call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT ThrowReferenceError : Method<Throws::Yes> + { + static void call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes> + { + static void call(ExecutionEngine *, const Value &); + }; + + /* closures */ + struct Q_QML_PRIVATE_EXPORT Closure : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *, int); + }; -#define RUNTIME_METHOD(returnvalue, name, args) \ - typedef returnvalue (*Method_##name)args; \ - enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \ - static returnvalue method_##name args; - FOR_EACH_RUNTIME_METHOD(RUNTIME_METHOD) -#undef RUNTIME_METHOD + /* Function header */ + struct Q_QML_PRIVATE_EXPORT ConvertThisToObject : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DeclareVar : Method<Throws::Yes> + { + static void call(ExecutionEngine *, Bool, int); + }; + struct Q_QML_PRIVATE_EXPORT CreateMappedArgumentsObject : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No> + { + static ReturnedValue call(ExecutionEngine *); + }; + struct Q_QML_PRIVATE_EXPORT CreateRestParameter : PureMethod + { + static ReturnedValue call(ExecutionEngine *, int); + }; + + /* literals */ + struct Q_QML_PRIVATE_EXPORT ArrayLiteral : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, Value[], uint); + }; + struct Q_QML_PRIVATE_EXPORT ObjectLiteral : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, Value[], int); + }; + struct Q_QML_PRIVATE_EXPORT CreateClass : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, int, const Value &, Value[]); + }; + + /* for-in, for-of and array destructuring */ + struct Q_QML_PRIVATE_EXPORT GetIterator : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, int); + }; + struct Q_QML_PRIVATE_EXPORT IteratorNext : IteratorMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &, Value *); + }; + struct Q_QML_PRIVATE_EXPORT IteratorNextForYieldStar : IteratorMethod + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value *); + }; + struct Q_QML_PRIVATE_EXPORT IteratorClose : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT DestructureRestElement : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + + /* conversions */ + struct Q_QML_PRIVATE_EXPORT ToObject : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT ToBoolean : PureMethod + { + static Bool call(const Value &); + }; + struct Q_QML_PRIVATE_EXPORT ToNumber : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &); + }; + /* unary operators */ + struct Q_QML_PRIVATE_EXPORT UMinus : Method<Throws::Yes> + { + static ReturnedValue call(const Value &); + }; + + /* binary operators */ + struct Q_QML_PRIVATE_EXPORT Instanceof : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT In : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Add : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Sub : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Mul : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Div : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Mod : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Exp : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitAnd : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitOr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT BitXor : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Shl : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Shr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT UShr : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT GreaterThan : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LessThan : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT GreaterEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT LessEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT Equal : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT NotEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StrictEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT StrictNotEqual : Method<Throws::Yes> + { + static ReturnedValue call(const Value &, const Value &); + }; + + /* comparisons */ + struct Q_QML_PRIVATE_EXPORT CompareGreaterThan : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareLessThan : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareGreaterEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareLessEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareNotEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareStrictEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareStrictNotEqual : Method<Throws::Yes> + { + static Bool call(const Value &, const Value &); + }; + + struct Q_QML_PRIVATE_EXPORT CompareInstanceof : Method<Throws::Yes> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_PRIVATE_EXPORT CompareIn : Method<Throws::Yes> + { + static Bool call(ExecutionEngine *, const Value &, const Value &); + }; + + struct Q_QML_PRIVATE_EXPORT RegexpLiteral : PureMethod + { + static ReturnedValue call(ExecutionEngine *, int); + }; + struct Q_QML_PRIVATE_EXPORT GetTemplateObject : PureMethod + { + static ReturnedValue call(Function *, int); + }; struct StackOffsets { static const int tailCall_function = -1; @@ -234,7 +504,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { }; static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof"); -static_assert(offsetof(Runtime, runtimeMethods) == 0, "JIT expects this to be the first member"); static_assert(sizeof(Runtime::BinaryOperation) == sizeof(void*), "JIT expects a function pointer to fit into a regular pointer, for cross-compilation offset translation"); } // namespace QV4 diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index 9866966936..162d75db63 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -37,10 +37,12 @@ ** ****************************************************************************/ +#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, @@ -58,7 +60,7 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, scan(ast); scan.leaveEnvironment(); - if (hasError) + if (hasError()) return; int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); @@ -67,17 +69,19 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; + + Codegen::throwSyntaxError(loc, detail); engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); } void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) { - if (hasError) + if (hasError()) return; - hasError = true; + + Codegen::throwReferenceError(loc, detail); engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); } 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 6cb2e95cdc..2fab9e4b7b 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -60,8 +60,9 @@ #include <QScopedValueRollback> using namespace QV4; +using namespace QQmlJS; -Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit) +Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true) { @@ -108,10 +109,10 @@ void Script::parse() const auto diagnosticMessages = parser.diagnosticMessages(); for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isError()) { - valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn); + valueScope.engine->throwSyntaxError(m.message, sourceFile, m.line, m.column); return; } else { - qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn + qWarning() << sourceFile << ':' << m.line << ':' << m.column << ": warning: " << m.message; } } @@ -133,7 +134,7 @@ void Script::parse() if (v4->hasException) return; - compilationUnit = cg.generateCompilationUnit(); + compilationUnit = QV4::ExecutableCompilationUnit::create(cg.generateCompilationUnit()); vmFunction = compilationUnit->linkToEngine(v4); } @@ -172,10 +173,11 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, - const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors, - QV4::Compiler::ContextType contextType) +QV4::CompiledData::CompilationUnit Script::precompile( + QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, + Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, + const QString &source, QList<QQmlError> *reportedErrors, + QV4::Compiler::ContextType contextType) { using namespace QV4::Compiler; using namespace QQmlJS::AST; @@ -202,10 +204,16 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi Codegen cg(unitGenerator, /*strict mode*/false); cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType); - errors = cg.qmlErrors(); - if (!errors.isEmpty()) { - if (reportedErrors) - *reportedErrors << errors; + if (cg.hasError()) { + if (reportedErrors) { + const auto v4Error = cg.error(); + QQmlError error; + error.setUrl(cg.url()); + error.setLine(v4Error.line); + error.setColumn(v4Error.column); + error.setDescription(v4Error.message); + reportedErrors->append(error); + } return nullptr; } @@ -219,8 +227,9 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl, &cacheError)) { - QQmlRefPointer<QV4::CompiledData::CompilationUnit> jsUnit; - jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit)); + QQmlRefPointer<QV4::ExecutableCompilationUnit> jsUnit + = QV4::ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(cachedUnit)); return new QV4::Script(engine, qmlContext, jsUnit); } @@ -237,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/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index a1e9b83a8b..aecedea701 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -80,7 +80,7 @@ struct Q_QML_EXPORT Script { if (qml) qmlContext.set(engine, *qml); } - Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit); + Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit); ~Script(); QString sourceFile; int line; @@ -92,7 +92,7 @@ struct Q_QML_EXPORT Script { bool parsed; QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval; QV4::PersistentValue qmlContext; - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<ExecutableCompilationUnit> compilationUnit; Function *vmFunction; bool parseAsBinding; @@ -101,8 +101,10 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, - const QString &fileName, const QString &finalUrl, const QString &source, + static QV4::CompiledData::CompilationUnit precompile( + QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, + Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, + const QString &finalUrl, const QString &source, QList<QQmlError> *reportedErrors = nullptr, QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Global); static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); 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/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index da71215bed..886dfaa521 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct SequencePrototype : public QV4::Object +struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object { V4_PROTOTYPE(arrayPrototype) void init(); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp deleted file mode 100644 index 50871a4d87..0000000000 --- a/src/qml/jsruntime/qv4serialize.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/**************************************************************************** -** -** 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 "qv4serialize_p.h" - -#include <private/qv8engine_p.h> -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#include <private/qqmllistmodelworkeragent_p.h> -#endif - -#include <private/qv4value_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4regexpobject_p.h> -#if QT_CONFIG(qml_sequence_object) -#include <private/qv4sequenceobject_p.h> -#endif -#include <private/qv4objectproto_p.h> -#include <private/qv4qobjectwrapper_p.h> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -// We allow the following JavaScript types to be passed between the main and -// the secondary thread: -// + undefined -// + null -// + Boolean -// + String -// + Function -// + Array -// + "Simple" Objects -// + Number -// + Date -// + RegExp -// <quint8 type><quint24 size><data> - -enum Type { - WorkerUndefined, - WorkerNull, - WorkerTrue, - WorkerFalse, - WorkerString, - WorkerFunction, - WorkerArray, - WorkerObject, - WorkerInt32, - WorkerUint32, - WorkerNumber, - WorkerDate, - WorkerRegexp, -#if QT_CONFIG(qml_list_model) - WorkerListModel, -#endif -#if QT_CONFIG(qml_sequence_object) - WorkerSequence -#endif -}; - -static inline quint32 valueheader(Type type, quint32 size = 0) -{ - return quint8(type) << 24 | (size & 0xFFFFFF); -} - -static inline Type headertype(quint32 header) -{ - return (Type)(header >> 24); -} - -static inline quint32 headersize(quint32 header) -{ - return header & 0xFFFFFF; -} - -static inline void push(QByteArray &data, quint32 value) -{ - data.append((const char *)&value, sizeof(quint32)); -} - -static inline void push(QByteArray &data, double value) -{ - data.append((const char *)&value, sizeof(double)); -} - -static inline void push(QByteArray &data, void *ptr) -{ - data.append((const char *)&ptr, sizeof(void *)); -} - -static inline void reserve(QByteArray &data, int extra) -{ - data.reserve(data.size() + extra); -} - -static inline quint32 popUint32(const char *&data) -{ - quint32 rv = *((const quint32 *)data); - data += sizeof(quint32); - return rv; -} - -static inline double popDouble(const char *&data) -{ - double rv = *((const double *)data); - data += sizeof(double); - return rv; -} - -static inline void *popPtr(const char *&data) -{ - void *rv = *((void *const *)data); - data += sizeof(void *); - return rv; -} - -// XXX TODO: Check that worker script is exception safe in the case of -// serialization/deserialization failures - -#define ALIGN(size) (((size) + 3) & ~3) -void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine) -{ - QV4::Scope scope(engine); - - if (v.isEmpty()) { - Q_ASSERT(!"Serialize: got empty value"); - } else if (v.isUndefined()) { - push(data, valueheader(WorkerUndefined)); - } else if (v.isNull()) { - push(data, valueheader(WorkerNull)); - } else if (v.isBoolean()) { - push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); - } else if (v.isString()) { - const QString &qstr = v.toQString(); - int length = qstr.length(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, utf16size + sizeof(quint32)); - push(data, valueheader(WorkerString, length)); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.as<FunctionObject>()) { - // XXX TODO: Implement passing function objects between the main and - // worker scripts - push(data, valueheader(WorkerUndefined)); - } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) { - uint length = array->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerArray, length)); - ScopedValue val(scope); - for (uint ii = 0; ii < length; ++ii) - serialize(data, (val = array->get(ii)), engine); - } else if (v.isInteger()) { - reserve(data, 2 * sizeof(quint32)); - push(data, valueheader(WorkerInt32)); - push(data, (quint32)v.integerValue()); -// } else if (v.IsUint32()) { -// reserve(data, 2 * sizeof(quint32)); -// push(data, valueheader(WorkerUint32)); -// push(data, v.Uint32Value()); - } else if (v.isNumber()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerNumber)); - push(data, v.asDouble()); - } else if (const QV4::DateObject *d = v.as<DateObject>()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerDate)); - push(data, d->date()); - } else if (const RegExpObject *re = v.as<RegExpObject>()) { - quint32 flags = re->flags(); - QString pattern = re->source(); - int length = pattern.length() + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, sizeof(quint32) + utf16size); - push(data, valueheader(WorkerRegexp, flags)); - push(data, (quint32)length); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { - // XXX TODO: Generalize passing objects between the main thread and worker scripts so - // that others can trivially plug in their elements. -#if QT_CONFIG(qml_list_model) - QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); - if (lm && lm->agent()) { - QQmlListModelWorkerAgent *agent = lm->agent(); - agent->addref(); - push(data, valueheader(WorkerListModel)); - push(data, (void *)agent); - return; - } -#else - Q_UNUSED(qobjectWrapper); -#endif - // No other QObject's are allowed to be sent - push(data, valueheader(WorkerUndefined)); - } else if (const Object *o = v.as<Object>()) { -#if QT_CONFIG(qml_sequence_object) - if (o->isListType()) { - // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); - uint length = seqLength + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerSequence, length)); - serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type - ScopedValue val(scope); - for (uint ii = 0; ii < seqLength; ++ii) - serialize(data, (val = o->get(ii)), engine); // sequence elements - - return; - } -#endif - - // regular object - QV4::ScopedValue val(scope, v); - QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val)); - quint32 length = properties->getLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - push(data, valueheader(WorkerObject, length)); - - QV4::ScopedValue s(scope); - for (quint32 ii = 0; ii < length; ++ii) { - s = properties->get(ii); - serialize(data, s, engine); - - QV4::String *str = s->as<String>(); - val = o->get(str); - if (scope.hasException()) - scope.engine->catchException(); - - serialize(data, val, engine); - } - return; - } else { - push(data, valueheader(WorkerUndefined)); - } -} - -ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) -{ - quint32 header = popUint32(data); - Type type = headertype(header); - - Scope scope(engine); - - switch (type) { - case WorkerUndefined: - return QV4::Encode::undefined(); - case WorkerNull: - return QV4::Encode::null(); - case WorkerTrue: - return QV4::Encode(true); - case WorkerFalse: - return QV4::Encode(false); - case WorkerString: - { - quint32 size = headersize(header); - QString qstr((const QChar *)data, size); - data += ALIGN(size * sizeof(quint16)); - return QV4::Encode(engine->newString(qstr)); - } - case WorkerFunction: - Q_ASSERT(!"Unreachable"); - break; - case WorkerArray: - { - quint32 size = headersize(header); - ScopedArrayObject a(scope, engine->newArrayObject()); - ScopedValue v(scope); - for (quint32 ii = 0; ii < size; ++ii) { - v = deserialize(data, engine); - a->put(ii, v); - } - return a.asReturnedValue(); - } - case WorkerObject: - { - quint32 size = headersize(header); - ScopedObject o(scope, engine->newObject()); - ScopedValue name(scope); - ScopedString n(scope); - ScopedValue value(scope); - for (quint32 ii = 0; ii < size; ++ii) { - name = deserialize(data, engine); - value = deserialize(data, engine); - n = name->asReturnedValue(); - o->put(n, value); - } - return o.asReturnedValue(); - } - case WorkerInt32: - return QV4::Encode((qint32)popUint32(data)); - case WorkerUint32: - return QV4::Encode(popUint32(data)); - case WorkerNumber: - return QV4::Encode(popDouble(data)); - case WorkerDate: - return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data)))); - case WorkerRegexp: - { - quint32 flags = headersize(header); - quint32 length = popUint32(data); - QString pattern = QString((const QChar *)data, length - 1); - data += ALIGN(length * sizeof(quint16)); - return Encode(engine->newRegExpObject(pattern, flags)); - } -#if QT_CONFIG(qml_list_model) - case WorkerListModel: - { - void *ptr = popPtr(data); - QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; - QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent)); - // ### Find a better solution then the ugly property - QQmlListModelWorkerAgent::VariantRef ref(agent); - QVariant var = qVariantFromValue(ref); - QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); - QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); - rv->as<Object>()->defineReadonlyProperty(s, v); - - agent->release(); - agent->setEngine(engine); - return rv->asReturnedValue(); - } -#endif -#if QT_CONFIG(qml_sequence_object) - case WorkerSequence: - { - ScopedValue value(scope); - bool succeeded = false; - quint32 length = headersize(header); - quint32 seqLength = length - 1; - value = deserialize(data, engine); - int sequenceType = value->integerValue(); - ScopedArrayObject array(scope, engine->newArrayObject()); - array->arrayReserve(seqLength); - for (quint32 ii = 0; ii < seqLength; ++ii) { - value = deserialize(data, engine); - array->arrayPut(ii, value); - } - array->setArrayLengthUnchecked(seqLength); - QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); - return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); - } -#endif - } - Q_ASSERT(!"Unreachable"); - return QV4::Encode::undefined(); -} - -QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine) -{ - QByteArray rv; - serialize(rv, value, engine); - return rv; -} - -ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine) -{ - const char *stream = data.constData(); - return deserialize(stream, engine); -} - -QT_END_NAMESPACE - diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp index 088ecbe30d..1664d1bd71 100644 --- a/src/qml/jsruntime/qv4setobject.cpp +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -76,7 +76,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add"))))); if (!adder) return scope.engine->throwTypeError(); - ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true)); CHECK_EXCEPTION(); if (!iter) return a.asReturnedValue(); @@ -84,7 +84,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, Value *nextValue = scope.alloc(1); ScopedValue done(scope); forever { - done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + done = Runtime::IteratorNext::call(scope.engine, iter, nextValue); CHECK_EXCEPTION(); if (done->toBoolean()) return a.asReturnedValue(); @@ -92,7 +92,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, adder->call(a, nextValue, 1); if (scope.engine->hasException) { ScopedValue falsey(scope, Encode(false)); - return Runtime::method_iteratorClose(scope.engine, iter, falsey); + return Runtime::IteratorClose::call(scope.engine, iter, falsey); } } } diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index 44cfef9173..bf689a74bc 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -52,63 +52,13 @@ #include <private/qv4context_p.h> #include <private/qv4enginebase_p.h> -#ifndef V4_BOOTSTRAP +#include <private/qv4calldata_p.h> #include <private/qv4function_p.h> -#endif QT_BEGIN_NAMESPACE namespace QV4 { -struct CallData -{ - enum Offsets { - Function = 0, - Context = 1, - Accumulator = 2, - This = 3, - NewTarget = 4, - Argc = 5, - - LastOffset = Argc, - OffsetCount = LastOffset + 1 - }; - - Value function; - Value context; - Value accumulator; - Value thisObject; - Value newTarget; - Value _argc; - - int argc() const { - Q_ASSERT(_argc.isInteger()); - return _argc.int_32(); - } - - void setArgc(int argc) { - Q_ASSERT(argc >= 0); - _argc.setInt_32(argc); - } - - inline ReturnedValue argument(int i) const { - return i < argc() ? args[i].asReturnedValue() : Value::undefinedValue().asReturnedValue(); - } - - Value args[1]; - - static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value)); - struct Q_QML_EXPORT CppStackFrame { EngineBase *engine; Value *savedStackTop; @@ -155,7 +105,6 @@ struct Q_QML_EXPORT CppStackFrame { engine->jsStackTop = savedStackTop; } -#ifndef V4_BOOTSTRAP static uint requiredJSStackFrameSize(uint nRegisters) { return CallData::HeaderSize() + nRegisters; } @@ -198,7 +147,6 @@ struct Q_QML_EXPORT CppStackFrame { *v = Value::emptyValue().asReturnedValue(); } } -#endif QString source() const; QString function() const; diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 68d65f2e24..223f4a4769 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -39,19 +39,15 @@ #include "qv4string_p.h" #include "qv4value_p.h" -#ifndef V4_BOOTSTRAP #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" -#endif #include <QtCore/QHash> #include <QtCore/private/qnumeric_p.h> using namespace QV4; -#ifndef V4_BOOTSTRAP - void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) { StringOrSymbol *s = static_cast<StringOrSymbol *>(that); @@ -254,10 +250,3 @@ qint64 String::virtualGetLength(const Managed *m) { return static_cast<const String *>(m)->d()->length(); } - -#endif // V4_BOOTSTRAP - -uint String::toArrayIndex(const QString &str) -{ - return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); -} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index fbd4f5f550..52fe09cd72 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -54,6 +54,7 @@ #include "qv4managed_p.h" #include <QtCore/private/qnumeric_p.h> #include "qv4enginebase_p.h" +#include <private/qv4stringtoarrayindex_p.h> QT_BEGIN_NAMESPACE @@ -104,7 +105,6 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { static void markObjects(Heap::Base *that, MarkStack *markStack); -#ifndef V4_BOOTSTRAP const VTable *vtable() const { return internalClass->vtable; } @@ -140,11 +140,9 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { private: static void append(const String *data, QChar *ch); -#endif }; Q_STATIC_ASSERT(std::is_trivial< String >::value); -#ifndef V4_BOOTSTRAP struct ComplexString : String { void init(String *l, String *n); void init(String *ref, int from, int len); @@ -162,12 +160,10 @@ inline int String::length() const { return text ? text->size : static_cast<const ComplexString *>(this)->len; } -#endif } struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { -#ifndef V4_BOOTSTRAP V4_MANAGED(StringOrSymbol, Managed) V4_NEEDS_DESTROY enum { @@ -184,11 +180,9 @@ public: inline QString toQString() const { return d()->toQString(); } -#endif }; struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { -#ifndef V4_BOOTSTRAP V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) @@ -239,43 +233,13 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static qint64 virtualGetLength(const Managed *m); -#endif - -public: - static uint toArrayIndex(const QString &str); - -private: - static inline uint toUInt(const QChar *ch) { return ch->unicode(); } - static inline uint toUInt(const char *ch) { return static_cast<unsigned char>(*ch); } - - template <typename T> - static inline uint toArrayIndex(const T *ch, const T *end) - { - uint i = toUInt(ch) - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = toUInt(ch) - '0'; - if (x > 9) - return UINT_MAX; - if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x - return UINT_MAX; - ++ch; - } - return i; - } public: template <typename T> static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) { // array indices get their number as hash value - uint h = toArrayIndex(ch, end); + uint h = stringToArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; @@ -283,17 +247,16 @@ public: } while (ch < end) { - h = 31 * h + toUInt(ch); + h = 31 * h + charToUInt(ch); ++ch; } if (subtype) - *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; + *subtype = (charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; -#ifndef V4_BOOTSTRAP struct ComplexString : String { typedef QV4::Heap::ComplexString Data; QV4::Heap::ComplexString *d_unchecked() const { return static_cast<QV4::Heap::ComplexString *>(m()); } @@ -332,8 +295,6 @@ inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) { return v.toString(e)->asReturnedValue(); } -#endif - } diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 6afa6d36d6..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> @@ -152,13 +152,14 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert if (attributes != Attr_Invalid) return attributes; - const StringObject *s = static_cast<const StringObject *>(m); - uint slen = s->d()->string->toQString().length(); - uint index = id.asArrayIndex(); - if (index < slen) { - if (p) - p->value = s->getIndex(index); - return Attr_NotConfigurable|Attr_NotWritable; + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + const auto s = static_cast<const StringObject *>(m); + if (index < uint(s->d()->string->toQString().length())) { + if (p) + p->value = s->getIndex(index); + return Attr_NotConfigurable|Attr_NotWritable; + } } return Object::virtualGetOwnProperty(m, id, p); } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index faf7934c06..7d33167762 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -214,7 +214,7 @@ template <typename T> ReturnedValue atomicLoad(char *data) { typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data); - T val = QAtomicOps<T>::load(*mem); + T val = QAtomicOps<T>::loadRelaxed(*mem); return typeToValue(val); } @@ -223,7 +223,7 @@ ReturnedValue atomicStore(char *data, Value v) { T value = valueToType<T>(v); typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data); - QAtomicOps<T>::store(*mem, value); + QAtomicOps<T>::storeRelaxed(*mem, value); return typeToValue(value); } @@ -459,24 +459,23 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualGet(m, id, receiver, hasProperty); - // fall through, with index == UINT_MAX it'll do the right thing. Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); if (a->d()->buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); - if (index >= a->length()) { + if (!isArrayIndex || id.asArrayIndex() >= a->length()) { if (hasProperty) *hasProperty = false; return Encode::undefined(); } uint bytesPerElement = a->d()->type->bytesPerElement; - uint byteOffset = a->d()->byteOffset + index * bytesPerElement; + uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement; Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength()); if (hasProperty) @@ -486,27 +485,22 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualHasProperty(m, id); - // fall through, with index == UINT_MAX it'll do the right thing. const TypedArray *a = static_cast<const TypedArray *>(m); if (a->d()->buffer->isDetachedBuffer()) { a->engine()->throwTypeError(); return false; } - if (index >= a->length()) - return false; - return true; + return isArrayIndex && id.asArrayIndex() < a->length(); } PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString()) return Object::virtualGetOwnProperty(m, id, p); - // fall through, with index == UINT_MAX it'll do the right thing. bool hasProperty = false; ReturnedValue v = virtualGet(m, id, m, &hasProperty); @@ -517,10 +511,9 @@ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyK bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) + const bool isArrayIndex = id.isArrayIndex(); + if (!isArrayIndex && !id.isCanonicalNumericIndexString()) return Object::virtualPut(m, id, value, receiver); - // fall through, with index == UINT_MAX it'll do the right thing. ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) @@ -531,6 +524,10 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu if (a->d()->buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); + if (!isArrayIndex) + return false; + + const uint index = id.asArrayIndex(); if (index >= a->length()) return false; @@ -547,11 +544,12 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) { - uint index = id.asArrayIndex(); - if (index == UINT_MAX && !id.isCanonicalNumericIndexString()) - return Object::virtualDefineOwnProperty(m, id, p, attrs); - // fall through, with index == UINT_MAX it'll do the right thing. + if (!id.isArrayIndex()) { + return !id.isCanonicalNumericIndexString() + && Object::virtualDefineOwnProperty(m, id, p, attrs); + } + const uint index = id.asArrayIndex(); TypedArray *a = static_cast<TypedArray *>(m); if (index >= a->length() || attrs.isAccessor()) return false; @@ -1595,7 +1593,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function R += separator; v = instance->get(k); - v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index cbc153bb86..d29b060b9e 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -36,16 +36,14 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <qv4engine_p.h> + #include <qv4runtime_p.h> -#include <qv4string_p.h> #include <qv4propertykey_p.h> -#ifndef V4_BOOTSTRAP +#include <qv4string_p.h> #include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> -#endif #include <wtf/MathExtras.h> @@ -83,12 +81,8 @@ bool Value::toBooleanImpl(Value val) Heap::Base *b = val.m(); if (!b) return false; -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else if (b->internalClass->vtable->isString) return static_cast<Heap::String *>(b)->length() > 0; -#endif return true; } @@ -103,10 +97,6 @@ double Value::toNumberImpl(Value val) case QV4::Value::Undefined_Type: return std::numeric_limits<double>::quiet_NaN(); case QV4::Value::Managed_Type: -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - Q_FALLTHROUGH(); -#else if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); if (val.isSymbol()) { @@ -114,16 +104,15 @@ double Value::toNumberImpl(Value val) m.engine()->throwTypeError(); return 0; } - { - Q_ASSERT(val.isObject()); - Scope scope(val.objectValue()->engine()); - ScopedValue protectThis(scope, val); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); + { + Q_ASSERT(val.isObject()); + Scope scope(val.objectValue()->engine()); + ScopedValue protectThis(scope, val); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); } -#endif case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: @@ -133,7 +122,6 @@ double Value::toNumberImpl(Value val) } } -#ifndef V4_BOOTSTRAP QString Value::toQStringNoThrow() const { switch (type()) { @@ -248,7 +236,6 @@ QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const ScopedStringOrSymbol s(scope, v); return s->toPropertyKey(); } -#endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { if (_val == other._val) @@ -285,7 +272,6 @@ bool Value::sameValueZero(Value other) const { return false; } -#ifndef V4_BOOTSTRAP Heap::String *Value::toString(ExecutionEngine *e, Value val) { return RuntimeHelpers::convertToString(e, val); @@ -327,4 +313,3 @@ uint Value::asArrayLength(bool *ok) const } return idx; } -#endif // V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index ce85e48b72..4e901721cb 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -57,8 +57,10 @@ #include "qv4global_p.h" #include <private/qv4heap_p.h> #include <private/qv4internalclass_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qnumeric_p.h> +#include <private/qv4calldata_p.h> QT_BEGIN_NAMESPACE @@ -68,84 +70,23 @@ namespace Heap { struct Base; } -struct Q_QML_PRIVATE_EXPORT Value +struct Q_QML_PRIVATE_EXPORT Value : public StaticValue { - /* - We use 8 bytes for a value and a different variant of NaN boxing. A Double - NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a - signalling NaN it is the top 14 bits. The other values are usually set to 0 by the - processor, and are thus free for us to store other data. We keep pointers in there for - managed objects, and encode the other types using the free space given to use by the unused - bits for NaN values. This also works for pointers on 64 bit systems, as they all currently - only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for - pointers.) - - We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will - get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between - managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave - the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is - set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are - used to encode Null/Int/Bool. - - Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. - - Specific bit-sequences: - 0 = always 0 - 1 = always 1 - x = stored value - a,b,c,d = specific bit values, see notes - - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | - 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value - ------------------------------------------------------------------------+-------------- - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined - 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) - a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf - dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double - 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool - 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int - - Notes: - - a: xor-ed signbit, always 1 for NaN - - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value - - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 - - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ - and JS - - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 - - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, - so: (val >> (64-15)) == 1 - - Null, Bool, and Int have bit 48 set, indicating integer-convertible - - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where - any non double results in a NaN - - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to - 63) are zero. No need to shift. - */ - - 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; } - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - static inline int valueOffset() { return 0; } - static inline int tagOffset() { return 4; } -#else // !Q_LITTLE_ENDIAN - static inline int valueOffset() { return 4; } - 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()); } + using HeapBasePtr = Heap::Base *; + using ManagedPtr = Managed *; + + Value() = default; + constexpr Value(quint64 val) : StaticValue(val) {} + + static constexpr Value fromStaticValue(StaticValue staticValue) + { + return {staticValue._val}; + } #if QT_POINTER_SIZE == 8 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Heap::Base *b; + HeapBasePtr b; #ifdef __ia64 // Restore bits 49-47 to bits 63-61, undoing the workaround explained in // setM below. @@ -159,7 +100,7 @@ struct Q_QML_PRIVATE_EXPORT Value #endif return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { memcpy(&_val, &b, 8); #ifdef __ia64 @@ -171,15 +112,15 @@ struct Q_QML_PRIVATE_EXPORT Value #endif } #elif QT_POINTER_SIZE == 4 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); - Heap::Base *b; + Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32)); + HeapBasePtr b; quint32 v = value(); memcpy(&b, &v, 4); return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { quint32 v; memcpy(&v, &b, 4); @@ -189,190 +130,11 @@ struct Q_QML_PRIVATE_EXPORT Value # error "unsupported pointer size" #endif - QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const - { - return int(value()); - } - QML_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(); } - - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() - { - setTagValue(quint32(ValueTypeInternal::Empty), 0); - } - - // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible - // and use negative numbers here - enum QuickType { - QT_ManagedOrUndefined = 0, - QT_ManagedOrUndefined1 = 1, - QT_ManagedOrUndefined2 = 2, - QT_ManagedOrUndefined3 = 3, - QT_Empty = 4, - QT_Null = 5, - QT_Bool = 6, - QT_Int = 7 - // all other values are doubles - }; - - enum Type { - Undefined_Type = 0, - Managed_Type = 1, - Empty_Type = 4, - Null_Type = 5, - Boolean_Type = 6, - Integer_Type = 7, - Double_Type = 8 - }; - - inline Type type() const { - int t = quickType(); - if (t < QT_Empty) - return _val ? Managed_Type : Undefined_Type; - if (t > QT_Int) - return Double_Type; - return static_cast<Type>(t); - } - - // Shared between 32-bit and 64-bit encoding - enum { - Tag_Shift = 32 - }; - - // Used only by 64-bit encoding - static const quint64 NaNEncodeMask = 0xfffc000000000000ull; - enum { - IsDouble_Shift = 64-14, - IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-15, - IsIntegerOrBool_Shift = 64-16, - QuickType_Shift = 64 - 17, - IsPositiveIntShift = 31 - }; - - static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 - - enum class ValueTypeInternal_64 { - Empty = Immediate_Mask_64 | 0, - Null = Immediate_Mask_64 | 0x08000u, - Boolean = Immediate_Mask_64 | 0x10000u, - Integer = Immediate_Mask_64 | 0x18000u - }; - - // Used only by 32-bit encoding - enum Masks { - SilentNaNBit = 0x00040000, - NotDouble_Mask = 0x7ffa0000, - }; - static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; - - enum class ValueTypeInternal_32 { - Empty = Immediate_Mask_32 | 0, - Null = Immediate_Mask_32 | 0x08000u, - Boolean = Immediate_Mask_32 | 0x10000u, - Integer = Immediate_Mask_32 | 0x18000u - }; - - enum { - Managed_Type_Internal = 0 - }; - - using ValueTypeInternal = ValueTypeInternal_64; - - enum { - NaN_Mask = 0x7ff80000, - }; - - inline quint64 quickType() const { return (_val >> QuickType_Shift); } - - // used internally in property - inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } - inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } - inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } - inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } - inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } - inline bool isNumber() const { return quickType() >= QT_Int; } - - inline bool isUndefined() const { return _val == 0; } - inline bool isDouble() const { return (_val >> IsDouble_Shift); } - inline bool isManaged() const { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); } - inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); } - - inline bool isIntOrBool() const { - return (_val >> IsIntegerOrBool_Shift) == 3; - } - - inline bool integerCompatible() const { - Q_ASSERT(!isEmpty()); - return (_val >> IsIntegerConvertible_Shift) == 1; - } - static inline bool integerCompatible(Value a, Value b) { - return a.integerCompatible() && b.integerCompatible(); - } - static inline bool bothDouble(Value a, Value b) { - return a.isDouble() && b.isDouble(); - } - inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } - - inline bool isPositiveInt() const { -#if QT_POINTER_SIZE == 4 - return isInteger() && int_32() >= 0; -#else - return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); -#endif - } - - QML_NEARLY_ALWAYS_INLINE double doubleValue() const { - Q_ASSERT(isDouble()); - double d; - Value v = *this; - v._val ^= NaNEncodeMask; - memcpy(&d, &v._val, 8); - return d; - } - QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { - if (qt_is_nan(d)) - d = qt_qnan(); - memcpy(&_val, &d, 8); - _val ^= NaNEncodeMask; - Q_ASSERT(isDouble()); - } inline bool isString() const; inline bool isStringOrSymbol() const; inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; - inline bool isInt32() { - if (tag() == quint32(ValueTypeInternal::Integer)) - return true; - if (isDouble()) { - double d = doubleValue(); - if (isInt32(d)) { - setInt_32(int(d)); - return true; - } - } - return false; - } - QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { - int i = int(d); - return (i == d && !(d == 0 && std::signbit(d))); - } - double asDouble() const { - if (tag() == quint32(ValueTypeInternal::Integer)) - return int_32(); - return doubleValue(); - } - - bool booleanValue() const { - return int_32(); - } - int integerValue() const { - return int_32(); - } QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) @@ -394,16 +156,16 @@ struct Q_QML_PRIVATE_EXPORT Value return nullptr; return reinterpret_cast<Object*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Managed *managed() const { + QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const { if (!isManaged()) return nullptr; return reinterpret_cast<Managed*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Heap::Base *heapObject() const { + QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const { return isManagedOrUndefined() ? m() : nullptr; } - static inline Value fromHeapObject(Heap::Base *m) + static inline Value fromHeapObject(HeapBasePtr m) { Value v; v.setM(m); @@ -446,12 +208,6 @@ struct Q_QML_PRIVATE_EXPORT Value static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; - inline bool tryIntegerConversion() { - bool b = integerCompatible(); - if (b) - setTagValue(quint32(ValueTypeInternal::Integer), value()); - return b; - } template <typename T> const T *as() const { @@ -485,13 +241,12 @@ struct Q_QML_PRIVATE_EXPORT Value return static_cast<const T *>(managed()); } -#ifndef V4_BOOTSTRAP uint asArrayLength(bool *ok) const; -#endif - ReturnedValue *data_ptr() { return &_val; } - constexpr ReturnedValue asReturnedValue() const { return _val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } + static constexpr Value fromReturnedValue(ReturnedValue val) + { + return fromStaticValue(StaticValue::fromReturnedValue(val)); + } // As per ES specs bool sameValue(Value other) const; @@ -499,21 +254,45 @@ struct Q_QML_PRIVATE_EXPORT Value inline void mark(MarkStack *markStack); - inline static constexpr Value emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } - static inline constexpr Value fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } - static inline constexpr Value fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } - inline static constexpr Value undefinedValue() { return { 0 }; } - static inline constexpr Value nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } - static inline Value fromDouble(double d); - static inline Value fromUInt32(uint i); - - static double toInteger(double d); - static int toInt32(double d); - static unsigned int toUInt32(double d); + static double toInteger(double d) { return StaticValue::toInteger(d); } + static int toInt32(double d) { return StaticValue::toInt32(d); } + static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); } + inline static constexpr Value emptyValue() + { + return fromStaticValue(StaticValue::emptyValue()); + } + static inline constexpr Value fromBoolean(bool b) + { + return fromStaticValue(StaticValue::fromBoolean(b)); + } + static inline constexpr Value fromInt32(int i) + { + return fromStaticValue(StaticValue::fromInt32(i)); + } + inline static constexpr Value undefinedValue() + { + return fromStaticValue(StaticValue::undefinedValue()); + } + static inline constexpr Value nullValue() + { + return fromStaticValue(StaticValue::nullValue()); + } + static inline Value fromDouble(double d) + { + return fromStaticValue(StaticValue::fromDouble(d)); + } + static inline Value fromUInt32(uint i) + { + return fromStaticValue(StaticValue::fromUInt32(i)); + } Value &operator =(const ScopedValue &v); - Value &operator=(ReturnedValue v) { _val = v; return *this; } - Value &operator=(Managed *m) { + Value &operator=(ReturnedValue v) + { + StaticValue::operator=(v); + return *this; + } + Value &operator=(ManagedPtr m) { if (!m) { setM(nullptr); } else { @@ -521,7 +300,7 @@ struct Q_QML_PRIVATE_EXPORT Value } return *this; } - Value &operator=(Heap::Base *o) { + Value &operator=(HeapBasePtr o) { setM(o); return *this; } @@ -529,43 +308,88 @@ struct Q_QML_PRIVATE_EXPORT Value template<typename T> Value &operator=(const Scoped<T> &t); }; -Q_STATIC_ASSERT(std::is_trivial< Value >::value); +Q_STATIC_ASSERT(std::is_trivial<Value>::value); +Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue)); + +template<> +inline StaticValue &StaticValue::operator=<Value>(const Value &value) +{ + _val = value._val; + return *this; +} + +template<typename Managed> +inline StaticValue &StaticValue::operator=(const Managed &m) +{ + *static_cast<Value *>(this) = m; + return *this; +} + +template<> +inline Value &StaticValue::asValue<Value>() +{ + return *static_cast<Value *>(this); +} + +template<> +inline const Value &StaticValue::asValue<Value>() const +{ + return *static_cast<const Value *>(this); +} + +template<> +inline Value *CallData::argValues<Value>() +{ + return static_cast<Value *>(static_cast<StaticValue *>(args)); +} + +template<> +inline const Value *CallData::argValues<Value>() const +{ + return static_cast<const Value *>(static_cast<const StaticValue *>(args)); +} + +template<typename HeapBase> +inline Encode::Encode(HeapBase *o) +{ + val = Value::fromHeapObject(o).asReturnedValue(); +} inline void Value::mark(MarkStack *markStack) { - Heap::Base *o = heapObject(); + HeapBasePtr o = heapObject(); if (o) o->mark(markStack); } inline bool Value::isString() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isString; } bool Value::isStringOrSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol; } bool Value::isSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; } inline bool Value::isObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isFunctionObject; } @@ -595,151 +419,12 @@ inline ReturnedValue Value::convertedToNumber() const inline ReturnedValue Heap::Base::asReturnedValue() const { - return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.setDouble(d); - return v; -} - -inline Value Value::fromUInt32(uint i) -{ - Value v; - if (i < INT_MAX) { - v.setTagValue(quint32(ValueTypeInternal::Integer), i); - } else { - v.setDouble(i); - } - return v; -} - -struct Double { - quint64 d; - - Double(double dbl) { - memcpy(&d, &dbl, sizeof(double)); - } - - int sign() const { - return (d >> 63) ? -1 : 1; - } - - bool isDenormal() const { - return static_cast<int>((d << 1) >> 53) == 0; - } - - int exponent() const { - return static_cast<int>((d << 1) >> 53) - 1023; - } - - quint64 significant() const { - quint64 m = (d << 12) >> 12; - if (!isDenormal()) - m |= (static_cast<quint64>(1) << 52); - return m; - } - - static int toInt32(double d) { - int i = static_cast<int>(d); - if (i == d) - return i; - return Double(d).toInt32(); - } - - int toInt32() { - int e = exponent() - 52; - if (e < 0) { - if (e <= -53) - return 0; - return sign() * static_cast<int>(significant() >> -e); - } else { - if (e > 31) - return 0; - return sign() * (static_cast<int>(significant()) << e); - } - } -}; - -inline double Value::toInteger(double d) -{ - if (std::isnan(d)) - return +0; - else if (!d || std::isinf(d)) - return d; - return d >= 0 ? std::floor(d) : std::ceil(d); -} - -inline int Value::toInt32(double value) -{ - return Double::toInt32(value); -} - -inline unsigned int Value::toUInt32(double d) -{ - return static_cast<uint>(toInt32(d)); + return Value::fromHeapObject(const_cast<Value::HeapBasePtr>(this)).asReturnedValue(); } // For source compat with older code in other modules using Primitive = Value; -struct Encode { - static constexpr ReturnedValue undefined() { - return Value::undefinedValue().asReturnedValue(); - } - static constexpr ReturnedValue null() { - return Value::nullValue().asReturnedValue(); - } - - explicit constexpr Encode(bool b) - : val(Value::fromBoolean(b).asReturnedValue()) - { - } - explicit Encode(double d) { - val = Value::fromDouble(d).asReturnedValue(); - } - explicit constexpr Encode(int i) - : val(Value::fromInt32(i).asReturnedValue()) - { - } - explicit Encode(uint i) { - val = Value::fromUInt32(i).asReturnedValue(); - } - explicit constexpr Encode(ReturnedValue v) - : val(v) - { - } - constexpr Encode(Value v) - : val(v.asReturnedValue()) - { - } - - explicit Encode(Heap::Base *o) { - val = Value::fromHeapObject(o).asReturnedValue(); - } - - explicit Encode(Value *o) { - Q_ASSERT(o); - val = o->asReturnedValue(); - } - - static ReturnedValue smallestNumber(double d) { - if (Value::isInt32(d)) - return Encode(static_cast<int>(d)); - else - return Encode(d); - } - - constexpr operator ReturnedValue() const { - return val; - } - quint64 val; -private: - explicit Encode(void *); -}; - template<typename T> ReturnedValue value_convert(ExecutionEngine *e, const Value &v); @@ -796,8 +481,8 @@ inline double Value::toInteger() const template <size_t o> struct HeapValue : Value { static Q_CONSTEXPR size_t offset = o; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + HeapBasePtr base() { + HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -805,7 +490,7 @@ struct HeapValue : Value { void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue()); } - void set(EngineBase *e, Heap::Base *b) { + void set(EngineBase *e, HeapBasePtr b) { WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue()); } }; @@ -817,8 +502,9 @@ struct ValueArray { uint alloc; Value values[1]; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Value::HeapBasePtr base() { + Value::HeapBasePtr base = reinterpret_cast<Value::HeapBasePtr>(this) + - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -826,7 +512,7 @@ struct ValueArray { void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } - void set(EngineBase *e, uint index, Heap::Base *b) { + void set(EngineBase *e, uint index, Value::HeapBasePtr b) { WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { @@ -855,12 +541,12 @@ struct ValueArray { const Value *end = v + alloc; if (alloc > 32*1024) { // drain from time to time to avoid overflows in the js stack - Heap::Base **currentBase = markStack->top; + Value::HeapBasePtr *currentBase = markStack->top; while (v < end) { v->mark(markStack); ++v; if (markStack->top >= currentBase + 32*1024) { - Heap::Base **oldBase = markStack->base; + Value::HeapBasePtr *oldBase = markStack->base; markStack->base = currentBase; markStack->drain(); markStack->base = oldBase; diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index e4d8bcaafc..e117e509ab 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -41,7 +41,6 @@ #include "qv4functionobject_p.h" #include "qv4objectproto_p.h" #include <private/qqmlvaluetypewrapper_p.h> -#include <private/qv8engine_p.h> #include <private/qv4qobjectwrapper_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 4ce8d97ee5..27d518f5c6 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,17 +56,20 @@ #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 #include <qtqml_tracepoints_p.h> #undef COUNT_INSTRUCTIONS +enum { ShowWhenDeoptimiationHappens = 0 }; + extern "C" { // This is the interface to Qt Creator's (new) QML debugger. @@ -202,7 +205,7 @@ int qt_v4DebuggerHook(const char *json) return ProtocolVersion; // Version number. } - int version = ob.value(QLatin1Literal("version")).toString().toInt(); + int version = ob.value(QLatin1String("version")).toString().toInt(); if (version != ProtocolVersion) { return -WrongProtocol; } @@ -345,73 +348,9 @@ static struct InstrCount { #undef CHECK_EXCEPTION #endif #define CHECK_EXCEPTION \ - if (engine->hasException) \ + if (engine->hasException || engine->isInterrupted.loadAcquire()) \ goto handleUnwind -static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken) - : quint8(ObservedTraceValues::FalsePathTaken); -#else - Q_UNUSED(truePathTaken); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceValue(ReturnedValue acc, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - switch (Primitive::fromReturnedValue(acc).type()) { - case QV4::Value::Integer_Type: - *traceInfo |= quint8(ObservedTraceValues::Integer); - break; - case QV4::Value::Boolean_Type: - *traceInfo |= quint8(ObservedTraceValues::Boolean); - break; - case QV4::Value::Double_Type: - *traceInfo |= quint8(ObservedTraceValues::Double); - break; - default: - *traceInfo |= quint8(ObservedTraceValues::Other); - break; - } -#else - Q_UNUSED(acc); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceDoubleValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Double); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceOtherValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Other); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); @@ -425,7 +364,7 @@ static inline Heap::CallContext *getScope(QV4::Value *stack, int level) static inline const QV4::Value &constant(Function *function, int index) { - return function->compilationUnit->constants[index]; + return function->compilationUnit->constants[index].asValue<Value>(); } static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) @@ -491,7 +430,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) if (debugger == nullptr) { if (function->jittedCode == nullptr) { if (engine->canJIT(function)) @@ -499,16 +438,20 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) else ++function->interpreterCallCount; } - if (function->jittedCode != nullptr) - return function->jittedCode(frame, engine); } -#endif // V4_ENABLE_JIT +#endif // QT_CONFIG(qml_jit) // interpreter if (debugger) debugger->enteringFunction(); - ReturnedValue result = interpret(frame, engine, function->codeData); + ReturnedValue result; + if (function->jittedCode != nullptr && debugger == nullptr) { + result = function->jittedCode(frame, engine); + } else { + // interpreter + result = interpret(frame, engine, function->codeData); + } if (debugger) debugger->leavingFunction(result); @@ -519,15 +462,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) { QV4::Function *function = frame->v4Function; - QV4::Value &accumulator = frame->jsFrame->accumulator; + QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>(); QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); - if (function->tracingEnabled()) { - for (int i = 0; i < int(function->nFormals); ++i) - traceValue(frame->jsFrame->argument(i), function, i); - } - MOTH_JUMP_TABLE; for (;;) { @@ -586,7 +524,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -599,7 +536,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadScopedLocal) auto cc = getScope(stack, scope); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -613,88 +549,73 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(MoveRegExp) - STACK_VALUE(destReg) = Runtime::method_regexpLiteral(engine, regExpId); + STACK_VALUE(destReg) = Runtime::RegexpLiteral::call(engine, regExpId); MOTH_END_INSTR(MoveRegExp) MOTH_BEGIN_INSTR(LoadClosure) - acc = Runtime::method_closure(engine, value); + acc = Runtime::Closure::call(engine, value); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) STORE_IP(); - acc = Runtime::method_loadName(engine, name); + acc = Runtime::LoadName::call(engine, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) STORE_IP(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) STORE_IP(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; acc = l->qmlContextPropertyGetter(l, engine, nullptr); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameStrict) STORE_IP(); STORE_ACC(); - Runtime::method_storeNameStrict(engine, name, accumulator); + Runtime::StoreNameStrict::call(engine, name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(StoreNameSloppy) STORE_IP(); STORE_ACC(); - Runtime::method_storeNameSloppy(engine, name, accumulator); + Runtime::StoreNameSloppy::call(engine, name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreNameSloppy) MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); - traceValue(acc, function, traceSlot); -#else - Q_UNUSED(traceSlot); - acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); -#endif + acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - Runtime::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); -#else - Q_UNUSED(traceSlot); - Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); -#endif + Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) STORE_IP(); STORE_ACC(); - acc = Runtime::method_loadProperty(engine, accumulator, name); + acc = Runtime::LoadProperty::call(engine, accumulator, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); STORE_ACC(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; if (accumulator.isNullOrUndefined()) { QString message = QStringLiteral("Cannot read property '%1' of %2") @@ -706,20 +627,19 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) STORE_IP(); STORE_ACC(); - Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator); + Runtime::StoreProperty::call(engine, STACK_VALUE(base), name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) STORE_IP(); STORE_ACC(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict()) engine->throwTypeError(); CHECK_EXCEPTION; @@ -727,14 +647,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadSuperProperty) STORE_IP(); - acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property)); + acc = Runtime::LoadSuperProperty::call(engine, STACK_VALUE(property)); CHECK_EXCEPTION; MOTH_END_INSTR(LoadSuperProperty) MOTH_BEGIN_INSTR(StoreSuperProperty) STORE_IP(); STORE_ACC(); - Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator); + Runtime::StoreSuperProperty::call(engine, STACK_VALUE(property), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreSuperProperty) @@ -765,7 +685,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(IteratorNextForYieldStar) STORE_ACC(); - acc = Runtime::method_iteratorNextForYieldStar(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); + acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); CHECK_EXCEPTION; MOTH_END_INSTR(IteratorNextForYieldStar) @@ -779,7 +699,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, Value undef = Value::undefinedValue(); acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) @@ -791,19 +710,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); - acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc); + acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) STORE_IP(); - Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex; + Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex; if (stack[base].isNullOrUndefined()) { QString message = QStringLiteral("Cannot call method '%1' of %2") @@ -826,49 +743,42 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) STORE_IP(); - acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc); + acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) STORE_IP(); - acc = Runtime::method_callName(engine, name, stack + argv, argc); + acc = Runtime::CallName::call(engine, name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); - acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); + acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); - acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); + acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); - acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc); + acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); - acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); + acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -877,21 +787,21 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, *engine->jsAlloca(1) = Primitive::fromInt32(argv); *engine->jsAlloca(1) = STACK_VALUE(thisObject); *engine->jsAlloca(1) = STACK_VALUE(func); - return Runtime::method_tailCall(frame, engine); + return Runtime::TailCall::call(frame, engine); CHECK_EXCEPTION; MOTH_END_INSTR(TailCall) MOTH_BEGIN_INSTR(Construct) STORE_IP(); STORE_ACC(); - acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc); + acc = Runtime::Construct::call(engine, STACK_VALUE(func), ACC, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(ConstructWithSpread) STORE_IP(); STORE_ACC(); - acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc); + acc = Runtime::ConstructWithSpread::call(engine, STACK_VALUE(func), ACC, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(ConstructWithSpread) @@ -918,7 +828,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(DeadTemporalZoneCheck) if (ACC.isEmpty()) { STORE_IP(); - Runtime::method_throwReferenceError(engine, name); + Runtime::ThrowReferenceError::call(engine, name); goto handleUnwind; } MOTH_END_INSTR(DeadTemporalZoneCheck) @@ -926,7 +836,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(ThrowException) STORE_IP(); STORE_ACC(); - Runtime::method_throwException(engine, accumulator); + Runtime::ThrowException::call(engine, accumulator); goto handleUnwind; MOTH_END_INSTR(ThrowException) @@ -944,40 +854,36 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(SetException) MOTH_BEGIN_INSTR(PushCatchContext) - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name); + Runtime::PushCatchContext::call(engine, index, name); MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(CreateCallContext) - stack[CallData::Context] = ExecutionContext::newCallContext(frame); + Runtime::PushCallContext::call(frame); MOTH_END_INSTR(CreateCallContext) MOTH_BEGIN_INSTR(PushWithContext) STORE_IP(); STORE_ACC(); - auto ctx = Runtime::method_createWithContext(engine, stack); + acc = Runtime::PushWithContext::call(engine, stack[CallData::Accumulator]); CHECK_EXCEPTION; - STACK_VALUE(CallData::Context) = ctx; MOTH_END_INSTR(PushWithContext) MOTH_BEGIN_INSTR(PushBlockContext) STORE_ACC(); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index); + Runtime::PushBlockContext::call(engine, index); MOTH_END_INSTR(PushBlockContext) MOTH_BEGIN_INSTR(CloneBlockContext) STORE_ACC(); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c); + Runtime::CloneBlockContext::call(engine); MOTH_END_INSTR(CloneBlockContext) MOTH_BEGIN_INSTR(PushScriptContext) - STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index); + Runtime::PushScriptContext::call(engine, index); MOTH_END_INSTR(PushScriptContext) MOTH_BEGIN_INSTR(PopScriptContext) - STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine); + Runtime::PopScriptContext::call(engine); MOTH_END_INSTR(PopScriptContext) MOTH_BEGIN_INSTR(PopContext) @@ -988,14 +894,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(GetIterator) STORE_IP(); STORE_ACC(); - acc = Runtime::method_getIterator(engine, accumulator, iterator); + acc = Runtime::GetIterator::call(engine, accumulator, iterator); CHECK_EXCEPTION; MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(IteratorNext) STORE_IP(); STORE_ACC(); - acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value)); + acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value)); STACK_VALUE(done) = acc; CHECK_EXCEPTION; MOTH_END_INSTR(IteratorNext) @@ -1003,98 +909,74 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(IteratorClose) STORE_IP(); STORE_ACC(); - acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done)); + acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done)); CHECK_EXCEPTION; MOTH_END_INSTR(IteratorClose) MOTH_BEGIN_INSTR(DestructureRestElement) STORE_IP(); STORE_ACC(); - acc = Runtime::method_destructureRestElement(engine, ACC); + acc = Runtime::DestructureRestElement::call(engine, ACC); CHECK_EXCEPTION; MOTH_END_INSTR(DestructureRestElement) MOTH_BEGIN_INSTR(DeleteProperty) - if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) { - if (function->isStrict()) { - STORE_IP(); - engine->throwTypeError(); - goto handleUnwind; - } - acc = Encode(false); - } else { - acc = Encode(true); - } + acc = Runtime::DeleteProperty::call(engine, function, STACK_VALUE(base), STACK_VALUE(index)); + CHECK_EXCEPTION; MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) - if (!Runtime::method_deleteName(engine, name)) { - if (function->isStrict()) { - STORE_IP(); - QString n = function->compilationUnit->runtimeStrings[name]->toQString(); - engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); - goto handleUnwind; - } - acc = Encode(false); - } else { - acc = Encode(true); - } + acc = Runtime::DeleteName::call(engine, function, name); + CHECK_EXCEPTION; MOTH_END_INSTR(DeleteName) MOTH_BEGIN_INSTR(TypeofName) - acc = Runtime::method_typeofName(engine, name); + acc = Runtime::TypeofName::call(engine, name); MOTH_END_INSTR(TypeofName) MOTH_BEGIN_INSTR(TypeofValue) STORE_ACC(); - acc = Runtime::method_typeofValue(engine, accumulator); + acc = Runtime::TypeofValue::call(engine, accumulator); MOTH_END_INSTR(TypeofValue) MOTH_BEGIN_INSTR(DeclareVar) - Runtime::method_declareVar(engine, isDeletable, varName); + Runtime::DeclareVar::call(engine, isDeletable, varName); MOTH_END_INSTR(DeclareVar) MOTH_BEGIN_INSTR(DefineArray) QV4::Value *arguments = stack + args; - acc = Runtime::method_arrayLiteral(engine, arguments, argc); + acc = Runtime::ArrayLiteral::call(engine, arguments, argc); MOTH_END_INSTR(DefineArray) MOTH_BEGIN_INSTR(DefineObjectLiteral) QV4::Value *arguments = stack + args; - acc = Runtime::method_objectLiteral(engine, internalClassId, arguments, argc); + acc = Runtime::ObjectLiteral::call(engine, internalClassId, arguments, argc); MOTH_END_INSTR(DefineObjectLiteral) MOTH_BEGIN_INSTR(CreateClass) - acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); + acc = Runtime::CreateClass::call(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); MOTH_END_INSTR(CreateClass) MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) - acc = Runtime::method_createMappedArgumentsObject(engine); + acc = Runtime::CreateMappedArgumentsObject::call(engine); MOTH_END_INSTR(CreateMappedArgumentsObject) MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) - acc = Runtime::method_createUnmappedArgumentsObject(engine); + acc = Runtime::CreateUnmappedArgumentsObject::call(engine); MOTH_END_INSTR(CreateUnmappedArgumentsObject) MOTH_BEGIN_INSTR(CreateRestParameter) - acc = Runtime::method_createRestParameter(engine, argIndex); + acc = Runtime::CreateRestParameter::call(engine, argIndex); MOTH_END_INSTR(CreateRestParameter) MOTH_BEGIN_INSTR(ConvertThisToObject) - Value *t = &stack[CallData::This]; - if (!t->isObject()) { - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - STORE_ACC(); - *t = t->toObject(engine)->asReturnedValue(); - CHECK_EXCEPTION; - } - } + STORE_ACC(); + stack[CallData::This] = Runtime::ConvertThisToObject::call(engine, stack[CallData::This]); + CHECK_EXCEPTION; MOTH_END_INSTR(ConvertThisToObject) MOTH_BEGIN_INSTR(LoadSuperConstructor) - acc = Runtime::method_loadSuperConstructor(engine, stack[CallData::Function]); + acc = Runtime::LoadSuperConstructor::call(engine, stack[CallData::Function]); CHECK_EXCEPTION; MOTH_END_INSTR(LoadSuperConstructor) @@ -1114,7 +996,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = ACC.int_32(); else takeJump = ACC.toBoolean(); - traceJumpTakesTruePath(takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpTrue) @@ -1125,7 +1006,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = !ACC.int_32(); else takeJump = !ACC.toBoolean(); - traceJumpTakesTruePath(!takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpFalse) @@ -1140,6 +1020,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, code += offset; MOTH_END_INSTR(JumpNotUndefined) + MOTH_BEGIN_INSTR(CheckException) + CHECK_EXCEPTION; + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) acc = Encode(ACC.isNullOrUndefined()); MOTH_END_INSTR(CmpEqNull) @@ -1176,7 +1060,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.int_32() == ACC.int_32()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpEq) @@ -1187,7 +1071,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(bool(left.int_32() != ACC.int_32())); } else { STORE_ACC(); - acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator))); + acc = Encode(bool(!Runtime::CompareEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpNe) @@ -1200,7 +1084,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() > ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator))); + acc = Encode(bool(Runtime::CompareGreaterThan::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpGt) @@ -1213,7 +1097,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() >= ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareGreaterEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpGe) @@ -1226,7 +1110,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() < ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator))); + acc = Encode(bool(Runtime::CompareLessThan::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpLt) @@ -1239,7 +1123,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(left.asDouble() <= ACC.asDouble()); } else { STORE_ACC(); - acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator))); + acc = Encode(bool(Runtime::CompareLessEqual::call(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpLe) @@ -1249,7 +1133,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = Encode(true); } else { STORE_ACC(); - acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator))); + acc = Runtime::StrictEqual::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpStrictEqual) @@ -1257,7 +1141,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CmpStrictNotEqual) if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) { STORE_ACC(); - acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)); + acc = Runtime::StrictNotEqual::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; } else { acc = Encode(false); @@ -1266,13 +1150,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CmpIn) STORE_ACC(); - acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator); + acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(CmpIn) MOTH_BEGIN_INSTR(CmpInstanceOf) STORE_ACC(); - acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC); + acc = Runtime::Instanceof::call(engine, STACK_VALUE(lhs), ACC); CHECK_EXCEPTION; MOTH_END_INSTR(CmpInstanceOf) @@ -1296,17 +1180,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, int a = ACC.int_32(); if (a == 0 || a == std::numeric_limits<int>::min()) { acc = Encode(-static_cast<double>(a)); - traceDoubleValue(function, traceSlot); } else { - acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(0, ACC.int_32()); } } else if (ACC.isDouble()) { acc ^= (1ull << 63); // simply flip sign bit - traceDoubleValue(function, traceSlot); } else { acc = Encode(-ACC.toNumberImpl()); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(UMinus) @@ -1317,57 +1198,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = add_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() + 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() + 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) if (Q_LIKELY(ACC.integerCompatible())) { - acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = sub_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() - 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() - 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = add_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_add(engine, left, accumulator); + acc = Runtime::Add::call(engine, left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(Sub) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_sub(left, accumulator); + acc = Runtime::Sub::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1376,7 +1249,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, double base = left.toNumber(); double exp = ACC.toNumber(); if (qIsInf(exp) && (base == 1 || base == -1)) - acc = Encode(qSNaN()); + acc = Encode(qQNaN()); else acc = Encode(pow(base,exp)); MOTH_END_INSTR(Exp) @@ -1384,29 +1257,26 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = mul_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); - acc = Runtime::method_mul(left, accumulator); + acc = Runtime::Mul::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) STORE_ACC(); - acc = Runtime::method_div(STACK_VALUE(lhs), accumulator); + acc = Runtime::Div::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) STORE_ACC(); - acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); + acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) @@ -1493,7 +1363,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(ThrowOnNullOrUndefined) MOTH_BEGIN_INSTR(GetTemplateObject) - acc = RuntimeHelpers::getTemplateObject(function, index); + acc = Runtime::GetTemplateObject::call(function, index); MOTH_END_INSTR(GetTemplateObject) MOTH_BEGIN_INSTR(Debug) @@ -1504,7 +1374,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(Debug) handleUnwind: - Q_ASSERT(engine->hasException || frame->unwindLevel); + // We do start the exception handler in case of isInterrupted. The exception handler will + // immediately abort, due to the same isInterrupted. We don't skip the exception handler + // because the current behavior is easier to implement in the JIT. + Q_ASSERT(engine->hasException || engine->isInterrupted.loadAcquire() || frame->unwindLevel); if (!frame->unwindHandler) { acc = Encode::undefined(); return acc; 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/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index aff1ae82d7..9dda104cd1 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -58,7 +58,7 @@ namespace QV4 { struct Lookup; -struct OwnPropertyKeyIterator { +struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator { virtual ~OwnPropertyKeyIterator() = 0; virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0; }; @@ -90,8 +90,8 @@ struct VTable typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &); const VTable * const parent; - quint32 inlinePropertyOffset : 16; - quint32 nInlineProperties : 16; + quint16 inlinePropertyOffset; + quint16 nInlineProperties; quint8 isExecutionContext; quint8 isString; quint8 isObject; diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri index 7956e4a9a1..0bc8e90d27 100644 --- a/src/qml/memory/memory.pri +++ b/src/qml/memory/memory.pri @@ -1,15 +1,11 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -!qmldevtools_build { SOURCES += \ $$PWD/qv4mm.cpp \ HEADERS += \ $$PWD/qv4mm_p.h \ $$PWD/qv4mmdefs_p.h \ - $$PWD/qv4writebarrier_p.h -} - -HEADERS += \ + $$PWD/qv4writebarrier_p.h \ $$PWD/qv4heap_p.h diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 07af5a6f7a..d7cfa193e6 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -234,7 +234,7 @@ struct QQmlQPointer { } T *data() const { - return d == nullptr || d->strongref.load() == 0 ? nullptr : qObject; + return d == nullptr || d->strongref.loadRelaxed() == 0 ? nullptr : qObject; } operator T*() const { return data(); } inline T* operator->() const { return data(); } @@ -247,7 +247,7 @@ struct QQmlQPointer { } bool isNull() const Q_DECL_NOTHROW { - return d == nullptr || qObject == nullptr || d->strongref.load() == 0; + return d == nullptr || qObject == nullptr || d->strongref.loadRelaxed() == 0; } private: diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index cac68bdcaf..c7f52eac6b 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,10 +60,10 @@ #include <iostream> #include <cstdlib> #include <algorithm> -#include "qv4alloca_p.h" #include "qv4profiling_p.h" #include "qv4mapobject_p.h" #include "qv4setobject_p.h" +#include "qv4writebarrier_p.h" //#define MM_STATS @@ -111,7 +112,11 @@ enum { struct MemorySegment { enum { +#ifdef Q_OS_RTEMS + NumChunks = sizeof(quint64), +#else NumChunks = 8*sizeof(quint64), +#endif SegmentSize = NumChunks*Chunk::ChunkSize, }; @@ -846,7 +851,7 @@ MarkStack::MarkStack(ExecutionEngine *engine) { base = (Heap::Base **)engine->gcStack->base(); top = base; - limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4; + limit = base + engine->maxGCStackSize()/sizeof(Heap::Base)*3/4; } void MarkStack::drain() @@ -999,12 +1004,12 @@ bool MemoryManager::shouldRunGC() const return false; } -size_t dumpBins(BlockAllocator *b, bool printOutput = true) +static size_t dumpBins(BlockAllocator *b, const char *title) { const QLoggingCategory &stats = lcGcAllocatorStats(); size_t totalSlotMem = 0; - if (printOutput) - qDebug(stats) << "Slot map:"; + if (title) + qDebug(stats) << "Slot map for" << title << "allocator:"; for (uint i = 0; i < BlockAllocator::NumBins; ++i) { uint nEntries = 0; HeapItem *h = b->freeBins[i]; @@ -1013,7 +1018,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true) totalSlotMem += h->freeData.availableSlots; h = h->freeData.next; } - if (printOutput) + if (title) qDebug(stats) << " number of entries in slot" << i << ":" << nEntries; } SDUMP() << " large slot map"; @@ -1023,7 +1028,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true) h = h->freeData.next; } - if (printOutput) + if (title) qDebug(stats) << " total mem in bins" << totalSlotMem*Chunk::SlotSize; return totalSlotMem*Chunk::SlotSize; } @@ -1064,7 +1069,8 @@ void MemoryManager::runGC() size_t oldChunks = blockAllocator.chunks.size(); qDebug(stats) << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks"; qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore); - dumpBins(&blockAllocator); + dumpBins(&blockAllocator, "Block"); + dumpBins(&icAllocator, "InternalClass"); QElapsedTimer t; t.start(); @@ -1082,7 +1088,8 @@ void MemoryManager::runGC() qDebug(stats) << " new unmanaged heap:" << unmanagedHeapSize; qDebug(stats) << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; } - size_t memInBins = dumpBins(&blockAllocator); + size_t memInBins = dumpBins(&blockAllocator, "Block") + + dumpBins(&icAllocator, "InternalClasss"); qDebug(stats) << "Marked object in" << markTime << "us."; qDebug(stats) << " " << markStackSize << "objects marked"; qDebug(stats) << "Sweeped object in" << sweepTime << "us."; @@ -1104,7 +1111,8 @@ void MemoryManager::runGC() qDebug(stats) << "Used memory after GC:" << usedAfter; qDebug(stats) << "Freed up bytes :" << (usedBefore - usedAfter); qDebug(stats) << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size()); - size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; + size_t lost = blockAllocator.allocatedMem() + icAllocator.allocatedMem() + - memInBins - usedAfter; if (lost) qDebug(stats) << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; if (largeItemsBefore || largeItemsAfter) { @@ -1126,7 +1134,9 @@ void MemoryManager::runGC() if (aggressiveGC) { // ensure we don't 'loose' any memory Q_ASSERT(blockAllocator.allocatedMem() - == blockAllocator.usedMem() + dumpBins(&blockAllocator, false)); + == blockAllocator.usedMem() + dumpBins(&blockAllocator, nullptr)); + Q_ASSERT(icAllocator.allocatedMem() + == icAllocator.usedMem() + dumpBins(&icAllocator, nullptr)); } usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep; diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h index 8b04aa6cb1..c65cfc0269 100644 --- a/src/qml/memory/qv4writebarrier_p.h +++ b/src/qml/memory/qv4writebarrier_p.h @@ -51,7 +51,6 @@ // #include <private/qv4global_p.h> -#include <private/qv4enginebase_p.h> QT_BEGIN_NAMESPACE @@ -60,6 +59,7 @@ QT_BEGIN_NAMESPACE #define WRITEBARRIER(x) (1/WRITEBARRIER_##x == 1) namespace QV4 { +struct EngineBase; namespace WriteBarrier { 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 4ad9057ced..8ac7633ae0 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -58,6 +58,7 @@ %token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" %token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" %token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_VERSION_NUMBER "version number" %token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" %token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" %token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" @@ -245,6 +246,7 @@ #include <private/qqmljsgrammar_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <QtCore/qlist.h> #include <QtCore/qstring.h> @@ -297,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; @@ -314,6 +319,7 @@ public: AST::UiArrayMemberList *UiArrayMemberList; AST::UiQualifiedId *UiQualifiedId; AST::UiEnumMemberList *UiEnumMemberList; + AST::UiVersionSpecifier *UiVersionSpecifier; }; public: @@ -365,7 +371,7 @@ public: inline DiagnosticMessage diagnosticMessage() const { for (const DiagnosticMessage &d : diagnostic_messages) { - if (d.kind != DiagnosticMessage::Warning) + if (d.type != QtWarningMsg) return d; } @@ -376,10 +382,10 @@ public: { return diagnosticMessage().message; } inline int errorLineNumber() const - { return diagnosticMessage().loc.startLine; } + { return diagnosticMessage().line; } inline int errorColumnNumber() const - { return diagnosticMessage().loc.startColumn; } + { return diagnosticMessage().column; } protected: bool parse(int startToken); @@ -403,13 +409,26 @@ protected: void pushToken(int token); int lookaheadToken(Lexer *lexer); + static DiagnosticMessage compileError(const AST::SourceLocation &location, + const QString &message, QtMsgType kind = QtCriticalMsg) + { + DiagnosticMessage error; + error.line = location.startLine; + error.column = location.startColumn; + error.message = message; + error.type = kind; + return error; + } + void syntaxError(const AST::SourceLocation &location, const char *message) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message))); + diagnostic_messages.append(compileError(location, QLatin1String(message))); } void syntaxError(const AST::SourceLocation &location, const QString &message) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message)); + diagnostic_messages.append(compileError(location, message)); } + bool ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnTypeAnnotation, AST::FormalParameterList *formals); + protected: Engine *driver; MemoryPool *pool; @@ -580,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) @@ -769,8 +803,10 @@ UiHeaderItemList: UiHeaderItemList UiImport; PragmaId: JsIdentifier; -UiPragma: T_PRAGMA PragmaId T_AUTOMATIC_SEMICOLON; -UiPragma: T_PRAGMA PragmaId T_SEMICOLON; +Semicolon: T_AUTOMATIC_SEMICOLON; +Semicolon: T_SEMICOLON; + +UiPragma: T_PRAGMA PragmaId Semicolon; /. case $rule_number: { AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2)); @@ -782,28 +818,52 @@ UiPragma: T_PRAGMA PragmaId T_SEMICOLON; ImportId: MemberExpression; -UiImport: UiImportHead T_AUTOMATIC_SEMICOLON; -UiImport: UiImportHead T_SEMICOLON; +UiImport: UiImportHead Semicolon; /. case $rule_number: { sym(1).UiImport->semicolonToken = loc(2); } break; ./ -UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON; -UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON; +UiVersionSpecifier: T_VERSION_NUMBER T_DOT T_VERSION_NUMBER; /. case $rule_number: { - sym(1).UiImport->versionToken = loc(2); + auto version = new (pool) AST::UiVersionSpecifier(sym(1).dval, sym(3).dval); + version->majorToken = loc(1); + version->minorToken = loc(3); + sym(1).UiVersionSpecifier = version; + } break; +./ + + +UiVersionSpecifier: T_VERSION_NUMBER; +/. + case $rule_number: { + auto version = new (pool) AST::UiVersionSpecifier(sym(1).dval, 0); + version->majorToken = loc(1); + sym(1).UiVersionSpecifier = version; + } break; +./ + +UiImport: UiImportHead UiVersionSpecifier Semicolon; +/. + case $rule_number: { + auto versionToken = loc(2); + auto version = sym(2).UiVersionSpecifier; + sym(1).UiImport->version = version; + if (version->minorToken.isValid()) { + versionToken.length += version->minorToken.length + (version->minorToken.offset - versionToken.offset - versionToken.length); + } + sym(1).UiImport->versionToken = versionToken; sym(1).UiImport->semicolonToken = loc(3); } break; ./ -UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON; +UiImport: UiImportHead UiVersionSpecifier T_AS QmlIdentifier Semicolon; /. case $rule_number: { sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->version = sym(2).UiVersionSpecifier; sym(1).UiImport->asToken = loc(3); sym(1).UiImport->importIdToken = loc(4); sym(1).UiImport->importId = stringRef(4); @@ -811,8 +871,7 @@ UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON; } break; ./ -UiImport: UiImportHead T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiImport: UiImportHead T_AS QmlIdentifier T_SEMICOLON; +UiImport: UiImportHead T_AS QmlIdentifier Semicolon; /. case $rule_number: { sym(1).UiImport->asToken = loc(2); @@ -840,7 +899,7 @@ UiImportHead: T_IMPORT ImportId; if (node) { node->importToken = loc(1); } else { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + diagnostic_messages.append(compileError(loc(1), QLatin1String("Expected a qualified name id or a string literal"))); return false; // ### remove me @@ -1045,6 +1104,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: { @@ -1055,6 +1125,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: { @@ -1066,8 +1148,7 @@ UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier; } break; ./ -UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); @@ -1081,8 +1162,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEM } break; ./ -UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON; +UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); @@ -1095,8 +1175,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON; } break; ./ -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); @@ -1110,8 +1189,7 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T } break; ./ -UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); @@ -1127,8 +1205,7 @@ UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlI } break; ./ -UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); @@ -1140,8 +1217,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; } break; ./ -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); @@ -1155,8 +1231,7 @@ UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; } break; ./ -UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; -UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); @@ -1171,8 +1246,13 @@ UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlId sym(1).Node = node; } break; ./ +OptionalSemicolon: | Semicolon; +/. +/* we need OptionalSemicolon because UiScriptStatement might already parse the last semicolon + and then we would miss a semicolon (see tests/auto/quick/qquickvisualdatamodel/data/objectlist.qml)*/ + ./ -UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement); @@ -1184,7 +1264,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatemen } break; ./ -UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement); @@ -1198,7 +1278,7 @@ UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScr } break; ./ -UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement); @@ -1212,7 +1292,7 @@ UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScri } break; ./ -UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); @@ -1238,7 +1318,7 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T } break; ./ -UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET; +UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); @@ -1266,7 +1346,7 @@ UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlI } break; ./ -UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; +UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); @@ -1289,7 +1369,7 @@ UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatem } break; ./ -UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4)); @@ -1314,13 +1394,21 @@ 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); } break; ./ +UiObjectMember: GeneratorExpression; +/. + case $rule_number: { + auto node = new (pool) AST::UiSourceElement(sym(1).Node); + sym(1).Node = node; + } break; +./ + UiObjectMember: VariableStatement; /. case $rule_number: { @@ -1332,8 +1420,8 @@ UiQualifiedId: MemberExpression; /. case $rule_number: { if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, - QLatin1String("Ignored annotation"))); + diagnostic_messages.append(compileError(mem->lbracketToken, + QLatin1String("Ignored annotation"), QtWarningMsg)); sym(1).Expression = mem->base; } @@ -1343,7 +1431,7 @@ UiQualifiedId: MemberExpression; } else { sym(1).UiQualifiedId = 0; - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + diagnostic_messages.append(compileError(loc(1), QLatin1String("Expected a qualified name id"))); return false; // ### recover @@ -1430,6 +1518,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 -------------------------------------------------------------------------------------------------------- @@ -1590,7 +1735,7 @@ RegularExpressionLiteral: T_DIVIDE_EQ; scan_regexp: { bool rx = lexer->scanRegExp(prefix); if (!rx) { - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + diagnostic_messages.append(compileError(location(lexer), lexer->errorMessage())); return false; } @@ -2759,8 +2904,7 @@ StatementListItem: Statement; } break; ./ -StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_AUTOMATIC_SEMICOLON; -StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_SEMICOLON; +StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration Semicolon; /. case $rule_number: { sym(1).Node = new (pool) AST::StatementList(sym(3).FunctionDeclaration); @@ -2810,14 +2954,20 @@ 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; ./ -VariableStatement: VarDeclaration_In T_AUTOMATIC_SEMICOLON; -VariableStatement: VarDeclaration_In T_SEMICOLON; +VariableStatement: VarDeclaration_In Semicolon; BindingList: LexicalBinding_In; /. case $rule_number: Q_FALLTHROUGH(); ./ @@ -2847,22 +2997,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; ./ @@ -3012,15 +3162,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; @@ -3037,7 +3187,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; @@ -3087,8 +3237,7 @@ ExpressionStatementLookahead: ; } break; ./ -ExpressionStatement: Expression_In T_AUTOMATIC_SEMICOLON; -ExpressionStatement: Expression_In T_SEMICOLON; +ExpressionStatement: Expression_In Semicolon; /. case $rule_number: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); @@ -3121,9 +3270,8 @@ IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement; ./ -IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_AUTOMATIC_SEMICOLON; IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_COMPATIBILITY_SEMICOLON; -- for JSC/V8 compatibility -IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_SEMICOLON; +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN Semicolon; /. case $rule_number: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); @@ -3227,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; @@ -3252,8 +3404,7 @@ ForDeclaration: Var BindingPattern; } break; ./ -ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON; -ContinueStatement: T_CONTINUE T_SEMICOLON; +ContinueStatement: T_CONTINUE Semicolon; /. case $rule_number: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); @@ -3263,8 +3414,7 @@ ContinueStatement: T_CONTINUE T_SEMICOLON; } break; ./ -ContinueStatement: T_CONTINUE IdentifierReference T_AUTOMATIC_SEMICOLON; -ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON; +ContinueStatement: T_CONTINUE IdentifierReference Semicolon; /. case $rule_number: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); @@ -3275,8 +3425,7 @@ ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON; } break; ./ -BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON; -BreakStatement: T_BREAK T_SEMICOLON; +BreakStatement: T_BREAK Semicolon; /. case $rule_number: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); @@ -3286,8 +3435,7 @@ BreakStatement: T_BREAK T_SEMICOLON; } break; ./ -BreakStatement: T_BREAK IdentifierReference T_AUTOMATIC_SEMICOLON; -BreakStatement: T_BREAK IdentifierReference T_SEMICOLON; +BreakStatement: T_BREAK IdentifierReference Semicolon; /. case $rule_number: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); @@ -3298,8 +3446,7 @@ BreakStatement: T_BREAK IdentifierReference T_SEMICOLON; } break; ./ -ReturnStatement: T_RETURN ExpressionOpt_In T_AUTOMATIC_SEMICOLON; -ReturnStatement: T_RETURN ExpressionOpt_In T_SEMICOLON; +ReturnStatement: T_RETURN ExpressionOpt_In Semicolon; /. case $rule_number: { if (!functionNestingLevel) { @@ -3423,8 +3570,7 @@ LabelledItem: ExpressionStatementLookahead T_FORCE_DECLARATION FunctionDeclarati } break; ./ -ThrowStatement: T_THROW Expression_In T_AUTOMATIC_SEMICOLON; -ThrowStatement: T_THROW Expression_In T_SEMICOLON; +ThrowStatement: T_THROW Expression_In Semicolon; /. case $rule_number: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); @@ -3501,8 +3647,7 @@ CatchParameter: BindingPattern; } break; ./ -DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON; -- automatic semicolon -DebuggerStatement: T_DEBUGGER T_SEMICOLON; +DebuggerStatement: T_DEBUGGER Semicolon; /. case $rule_number: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); @@ -3519,59 +3664,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; ./ @@ -3681,7 +3852,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; @@ -3715,30 +3886,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); @@ -3747,30 +3922,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; @@ -4077,13 +4256,10 @@ ModuleItemList: ModuleItemList ModuleItem; } break; ./ -ModuleItem: ImportDeclaration T_AUTOMATIC_SEMICOLON; -/. case $rule_number: Q_FALLTHROUGH(); ./ -ModuleItem: ImportDeclaration T_SEMICOLON; -/. case $rule_number: Q_FALLTHROUGH(); ./ -ModuleItem: ExportDeclaration T_AUTOMATIC_SEMICOLON; + +ModuleItem: ImportDeclaration Semicolon; /. case $rule_number: Q_FALLTHROUGH(); ./ -ModuleItem: ExportDeclaration T_SEMICOLON; +ModuleItem: ExportDeclaration Semicolon; /. case $rule_number: { sym(1).StatementList = new (pool) AST::StatementList(sym(1).Node); @@ -4420,7 +4596,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; yylloc.length = 0; //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'"); - //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + //diagnostic_messages.append(compileError(yyloc, msg, QtWarningMsg)); first_token = &token_buffer[0]; last_token = &token_buffer[1]; @@ -4457,7 +4633,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; msg = QCoreApplication::translate("QQmlParser", "Syntax error"); else msg = QCoreApplication::translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + diagnostic_messages.append(compileError(token_buffer[0].loc, msg)); action = errorState; goto _Lcheck_token; @@ -4488,7 +4664,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; qDebug() << "Parse error, trying to recover (2)."; #endif const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + diagnostic_messages.append(compileError(token_buffer[0].loc, msg)); pushToken(*tk); goto _Lcheck_token; @@ -4503,7 +4679,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; int a = t_action(errorState, tk); if (a > 0 && t_action(a, yytoken)) { const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + diagnostic_messages.append(compileError(token_buffer[0].loc, msg)); pushToken(tk); goto _Lcheck_token; @@ -4511,7 +4687,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; } const QString msg = QCoreApplication::translate("QQmlParser", "Syntax error"); - diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + diagnostic_messages.append(compileError(token_buffer[0].loc, msg)); } return false; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 2b1a999b38..b63b2191b9 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; @@ -960,6 +960,7 @@ void FunctionDeclaration::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(formals, visitor); + accept(typeAnnotation, visitor); accept(body, visitor); } @@ -970,6 +971,7 @@ void FunctionExpression::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(formals, visitor); + accept(typeAnnotation, visitor); accept(body, visitor); } @@ -981,9 +983,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) { @@ -991,18 +993,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); @@ -1270,6 +1272,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)) { @@ -1340,13 +1371,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()) @@ -1354,7 +1386,7 @@ void PatternElement::boundNames(QStringList *names) else if (PatternPropertyList *p = propertyList()) p->boundNames(names); } else { - names->append(bindingIdentifier.toString()); + names->append({bindingIdentifier.toString(), typeAnnotation}); } } @@ -1371,7 +1403,7 @@ void PatternElementList::accept0(Visitor *visitor) } } -void PatternElementList::boundNames(QStringList *names) +void PatternElementList::boundNames(BoundNames *names) { for (PatternElementList *it = this; it; it = it->next) { if (it->element) @@ -1384,13 +1416,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); } @@ -1406,7 +1439,7 @@ void PatternPropertyList::accept0(Visitor *visitor) } } -void PatternPropertyList::boundNames(QStringList *names) +void PatternPropertyList::boundNames(BoundNames *names) { for (PatternPropertyList *it = this; it; it = it->next) it->property->boundNames(names); @@ -1475,6 +1508,37 @@ LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast() return this; } +void UiVersionSpecifier::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + 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 b81553776d..39194068bf 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,7 +234,9 @@ public: Kind_PatternElementList, Kind_PatternProperty, Kind_PatternPropertyList, - + Kind_Type, + Kind_TypeArgumentList, + Kind_TypeAnnotation, Kind_UiArrayBinding, Kind_UiImport, @@ -251,7 +254,8 @@ public: Kind_UiSourceElement, Kind_UiHeaderItemList, Kind_UiEnumDeclaration, - Kind_UiEnumMemberList + Kind_UiEnumMemberList, + Kind_UiVersionSpecifier }; inline Node() {} @@ -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: @@ -492,7 +621,30 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression +class QML_PARSER_EXPORT UiVersionSpecifier : public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiVersionSpecifier) + + UiVersionSpecifier(int majorum, int minorum) : majorVersion(majorum), minorVersion(minorum) { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override { return majorToken; } + + SourceLocation lastSourceLocation() const override + { + return minorToken.isValid() ? minorToken : majorToken; + } + + // attributes: + int majorVersion; + int minorVersion; + SourceLocation majorToken; + SourceLocation minorToken; +}; + +class QML_PARSER_EXPORT StringLiteral : public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(StringLiteral) @@ -681,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: @@ -705,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; @@ -726,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; } @@ -736,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; @@ -744,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; @@ -773,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(); } @@ -796,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) @@ -813,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 @@ -841,7 +1023,7 @@ public: void accept0(Visitor *visitor) override; - void boundNames(QStringList *names); + void boundNames(BoundNames *names); inline PatternPropertyList *finish () { @@ -2131,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; @@ -2151,6 +2334,7 @@ public: bool isGenerator = false; FormalParameterList *formals; StatementList *body; + TypeAnnotation *typeAnnotation; SourceLocation functionToken; SourceLocation identifierToken; SourceLocation lparenToken; @@ -2164,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; @@ -2235,9 +2419,9 @@ public: return false; } - QStringList formals() const; + BoundNames formals() const; - QStringList boundNames() const; + BoundNames boundNames() const; void accept0(Visitor *visitor) override; @@ -2786,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: @@ -2855,6 +3001,7 @@ public: SourceLocation asToken; SourceLocation importIdToken; SourceLocation semicolonToken; + UiVersionSpecifier *version = nullptr; }; class QML_PARSER_EXPORT UiObjectMember: public Node @@ -3090,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 () { @@ -3109,6 +3256,7 @@ public: SourceLocation commaToken; SourceLocation propertyTypeToken; SourceLocation identifierToken; + SourceLocation colonToken; }; class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember @@ -3203,7 +3351,7 @@ public: SourceLocation firstSourceLocation() const override { - if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition()) return funDecl->firstSourceLocation(); else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) return varStmt->firstSourceLocation(); @@ -3213,7 +3361,7 @@ public: SourceLocation lastSourceLocation() const override { - if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition()) return funDecl->lastSourceLocation(); else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) return varStmt->lastSourceLocation(); diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index e9caa918d5..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; @@ -178,8 +181,10 @@ class UiQualifiedId; class UiHeaderItemList; class UiEnumDeclaration; class UiEnumMemberList; +class UiVersionSpecifier; -} } // namespace AST +} // namespace AST +} // namespace QQmlJS QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index 9115449a46..7146cd00ac 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -111,6 +111,7 @@ public: virtual bool visit(UiQualifiedId *) { return true; } virtual bool visit(UiEnumDeclaration *) { return true; } virtual bool visit(UiEnumMemberList *) { return true; } + virtual bool visit(UiVersionSpecifier *) { return true; } virtual void endVisit(UiProgram *) {} virtual void endVisit(UiImport *) {} @@ -129,6 +130,7 @@ public: virtual void endVisit(UiQualifiedId *) {} virtual void endVisit(UiEnumDeclaration *) {} virtual void endVisit(UiEnumMemberList *) { } + virtual void endVisit(UiVersionSpecifier *) {} // QQmlJS virtual bool visit(ThisExpression *) { return true; } @@ -401,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 b7f7da9478..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> @@ -91,28 +92,6 @@ public: } }; - -class QML_PARSER_EXPORT DiagnosticMessage -{ -public: - enum Kind { Hint, Warning, Error }; - - DiagnosticMessage() {} - - DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message) - : kind(kind), loc(loc), message(message) {} - - bool isWarning() const - { return kind == Warning; } - - bool isError() const - { return kind == Error; } - - Kind kind = Error; - AST::SourceLocation loc; - QString message; -}; - class QML_PARSER_EXPORT Engine { Lexer *_lexer; diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index c53b13f64d..1e0ac72bd1 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -39,12 +39,15 @@ #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> #include <QtCore/qdebug.h> +#include <QtCore/QScopedValueRollback> QT_BEGIN_NAMESPACE Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); @@ -269,16 +272,29 @@ int Lexer::lex() ++_bracesCount; Q_FALLTHROUGH(); case T_SEMICOLON: + _importState = ImportState::NoQmlImport; + Q_FALLTHROUGH(); case T_QUESTION: case T_COLON: case T_TILDE: _delimited = true; break; + case T_AUTOMATIC_SEMICOLON: + case T_AS: + _importState = ImportState::NoQmlImport; + Q_FALLTHROUGH(); default: if (isBinop(_tokenKind)) _delimited = true; break; + case T_IMPORT: + if (qmlMode() || (_handlingDirectives && previousTokenKind == T_DOT)) + _importState = ImportState::SawImport; + if (isBinop(_tokenKind)) + _delimited = true; + break; + case T_IF: case T_FOR: case T_WHILE: @@ -618,6 +634,8 @@ again: return T_DIVIDE_; case '.': + if (_importState == ImportState::SawImport) + return T_DOT; if (isDecimalDigit(_char.unicode())) return scanNumber(ch); if (_char == QLatin1Char('.')) { @@ -728,7 +746,10 @@ again: case '7': case '8': case '9': - return scanNumber(ch); + if (_importState == ImportState::SawImport) + return scanVersionNumber(ch); + else + return scanNumber(ch); default: { uint c = ch.unicode(); @@ -1146,6 +1167,26 @@ int Lexer::scanNumber(QChar ch) return T_NUMERIC_LITERAL; } +int Lexer::scanVersionNumber(QChar ch) +{ + if (ch == QLatin1Char('0')) { + _tokenValue = 0; + return T_VERSION_NUMBER; + } + + int acc = 0; + acc += ch.digitValue(); + + while (_char.isDigit()) { + acc *= 10; + acc += _char.digitValue(); + scanChar(); // consume the digit + } + + _tokenValue = acc; + return T_VERSION_NUMBER; +} + bool Lexer::scanRegExp(RegExpBodyPrefix prefix) { _tokenText.resize(0); @@ -1402,6 +1443,13 @@ static inline bool isUriToken(int token) bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) { + auto setError = [error, this](QString message) { + error->message = std::move(message); + error->line = tokenStartLine(); + error->column = tokenStartColumn(); + }; + + QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true); Q_ASSERT(!_qmlMode); lex(); // fetch the first token @@ -1422,9 +1470,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) if (! (directiveName == QLatin1String("pragma") || directiveName == QLatin1String("import"))) { - error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser", "Syntax error")); return false; // not a valid directive name } @@ -1432,9 +1478,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) if (directiveName == QLatin1String("pragma")) { // .pragma library if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) { - error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser", "Syntax error")); return false; // expected `library } @@ -1456,20 +1500,15 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) pathOrUri = tokenText(); if (!pathOrUri.endsWith(QLatin1String("js"))) { - error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser","Imported file must be a script")); return false; } } else if (_tokenKind == T_IDENTIFIER) { - // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER - + // .import T_IDENTIFIER (. T_IDENTIFIER)* T_VERSION_NUMBER . T_VERSION_NUMBER as T_IDENTIFIER while (true) { if (!isUriToken(_tokenKind)) { - error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser","Invalid module URI")); return false; } @@ -1477,9 +1516,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) lex(); if (tokenStartLine() != lineNumber) { - error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser","Invalid module URI")); return false; } if (_tokenKind != QQmlJSGrammar::T_DOT) @@ -1489,21 +1526,30 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) lex(); if (tokenStartLine() != lineNumber) { - error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser","Invalid module URI")); return false; } } - if (_tokenKind != T_NUMERIC_LITERAL) { - error->message = QCoreApplication::translate("QQmlParser","Module import requires a version"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + if (_tokenKind != T_VERSION_NUMBER) { + setError(QCoreApplication::translate("QQmlParser","Module import requires a version")); return false; // expected the module version number } version = tokenText(); + lex(); + if (_tokenKind != T_DOT) { + setError(QCoreApplication::translate( "QQmlParser", "Module import requires a minor version (missing dot)")); + return false; // expected the module version number + } + version += QLatin1Char('.'); + + lex(); + if (_tokenKind != T_VERSION_NUMBER) { + setError(QCoreApplication::translate( "QQmlParser", "Module import requires a minor version (missing number)")); + return false; // expected the module version number + } + version += tokenText(); } // @@ -1511,34 +1557,27 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) // if (! (lex() == T_AS && tokenStartLine() == lineNumber)) { if (fileImport) - error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier"); + setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier")); else - error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"); + setError(QCoreApplication::translate("QQmlParser", "Module import requires a qualifier")); if (tokenStartLine() != lineNumber) { - error->loc.startLine = lineNumber; - error->loc.startColumn = column; - } else { - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + error->line = lineNumber; + error->line = column; } return false; // expected `as' } if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) { if (fileImport) - error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier"); + setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier")); else - error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser", "Module import requires a qualifier")); return false; // expected module name } const QString module = tokenText(); if (!module.at(0).isUpper()) { - error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser","Invalid import qualifier")); return false; } @@ -1549,9 +1588,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) } if (tokenStartLine() != lineNumber) { - error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); - error->loc.startLine = tokenStartLine(); - error->loc.startColumn = tokenStartColumn(); + setError(QCoreApplication::translate("QQmlParser", "Syntax error")); return false; // the directives cannot span over multiple lines } diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 51152bfd6e..e2ee4ae351 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { class Engine; -class DiagnosticMessage; +struct DiagnosticMessage; class Directives; class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar @@ -124,6 +124,11 @@ public: StaticIsKeyword = 0x4 }; + enum class ImportState { + SawImport, + NoQmlImport + }; + public: Lexer(Engine *engine); @@ -188,6 +193,7 @@ private: inline void scanChar(); int scanToken(); int scanNumber(QChar ch); + int scanVersionNumber(QChar ch); enum ScanStringMode { SingleQuote = '\'', DoubleQuote = '"', @@ -242,6 +248,7 @@ private: int _tokenLength; int _tokenLine; int _tokenColumn; + ImportState _importState = ImportState::NoQmlImport; bool _validTokenText; bool _prohibitAutomaticSemicolon; @@ -253,6 +260,7 @@ private: bool _skipLinefeed = false; int _generatorLevel = 0; bool _staticIsKeyword = false; + bool _handlingDirectives = false; }; } // end of namespace QQmlJS diff --git a/src/qml/qml.pro b/src/qml/qml.pro index df3085a28e..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,16 +39,21 @@ 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) include(util/util.pri) include(memory/memory.pri) include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) include(jsruntime/jsruntime.pri) -include(jit/jit.pri) +qtConfig(qml-jit) { + include(jit/jit.pri) +} include(qml/qml.pri) include(debugger/debugger.pri) include(qmldirparser/qmldirparser.pri) @@ -83,6 +63,7 @@ qtConfig(qml-animation) { include(types/types.pri) include(../3rdparty/masm/masm-defs.pri) include(../3rdparty/masm/masm.pri) +include(../3rdparty/llvm/llvm.pri) MODULE_PLUGIN_TYPES = \ qmltooling diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index ade05a596b..eadba394b4 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -3,6 +3,7 @@ HEADERS += \ $$PWD/qintrusivelist_p.h \ $$PWD/qpodvector_p.h \ $$PWD/qhashedstring_p.h \ + $$PWD/qprimefornumbits_p.h \ $$PWD/qqmlrefcount_p.h \ $$PWD/qfieldlist_p.h \ $$PWD/qqmlthread_p.h \ @@ -11,12 +12,14 @@ HEADERS += \ $$PWD/qrecyclepool_p.h \ $$PWD/qflagpointer_p.h \ $$PWD/qlazilyallocated_p.h \ - $$PWD/qqmlnullablevalue_p.h + $$PWD/qqmlnullablevalue_p.h \ + $$PWD/qstringhash_p.h \ + $$PWD/qlinkedstringhash_p.h SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qqmlthread.cpp \ + $$PWD/qqmlthread.cpp # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri # clock_gettime() is implemented in librt on these systems diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index 117670dbfc..7a8fdd0a14 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -39,82 +39,7 @@ #include "qhashedstring_p.h" - - -/* - A QHash has initially around pow(2, MinNumBits) buckets. For - example, if MinNumBits is 4, it has 17 buckets. -*/ -const int MinNumBits = 4; - -/* - The prime_deltas array is a table of selected prime values, even - though it doesn't look like one. The primes we are using are 1, - 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate - surrounding of a power of two. - - The primeForNumBits() function returns the prime associated to a - power of two. For example, primeForNumBits(8) returns 257. -*/ - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - -void QStringHashData::rehashToSize(int size) -{ - short bits = qMax(MinNumBits, (int)numBits); - while (primeForNumBits(bits) < size) bits++; - - if (bits > numBits) - rehashToBits(bits); -} - -void QStringHashData::rehashToBits(short bits) -{ - numBits = qMax(MinNumBits, (int)bits); - - int nb = primeForNumBits(numBits); - if (nb == numBuckets && buckets) - return; - -#ifdef QSTRINGHASH_LINK_DEBUG - if (linkCount) - qFatal("QStringHash: Illegal attempt to rehash a linked hash."); -#endif - - QStringHashNode **newBuckets = new QStringHashNode *[nb]; - ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); - - // Preserve the existing order within buckets so that items with the - // same key will retain the same find/findNext order - for (int i = 0; i < numBuckets; ++i) { - QStringHashNode *bucket = buckets[i]; - if (bucket) - rehashNode(newBuckets, nb, bucket); - } - - delete [] buckets; - buckets = newBuckets; - numBuckets = nb; -} - -void QStringHashData::rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) -{ - QStringHashNode *next = node->next.data(); - if (next) - rehashNode(newBuckets, nb, next); - - int bucket = node->hash % nb; - node->next = newBuckets[bucket]; - newBuckets[bucket] = node; -} +QT_BEGIN_NAMESPACE // Copy of QString's qMemCompare bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) @@ -228,3 +153,4 @@ QString QHashedCStringRef::toUtf16() const return rv; } +QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 2d6c25bdd3..b9f3f81219 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -63,9 +63,6 @@ QT_BEGIN_NAMESPACE -// Enable this to debug hash linking assumptions. -// #define QSTRINGHASH_LINK_DEBUG - class QHashedStringRef; class Q_QML_PRIVATE_EXPORT QHashedString : public QString { @@ -174,854 +171,6 @@ private: mutable quint32 m_hash = 0; }; -class QStringHashData; -class Q_AUTOTEST_EXPORT QStringHashNode -{ -public: - QStringHashNode() - : ckey(nullptr) - { - } - - QStringHashNode(const QHashedString &key) - : length(key.length()), hash(key.hash()), symbolId(0) - { - strData = const_cast<QHashedString &>(key).data_ptr(); - setQString(true); - strData->ref.ref(); - } - - QStringHashNode(const QHashedCStringRef &key) - : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData()) - { - } - - QStringHashNode(const QStringHashNode &o) - : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey) - { - setQString(o.isQString()); - if (isQString()) { strData->ref.ref(); } - } - - ~QStringHashNode() - { - if (isQString()) { if (!strData->ref.deref()) free(strData); } - } - - QFlagPointer<QStringHashNode> next; - - qint32 length = 0; - quint32 hash = 0; - quint32 symbolId = 0; - - union { - const char *ckey; - QStringData *strData; - }; - - inline QHashedString key() const - { - if (isQString()) - return QHashedString(QString((QChar *)strData->data(), length), hash); - - return QHashedString(QString::fromLatin1(ckey, length), hash); - } - - bool isQString() const { return next.flag(); } - void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); } - - inline char *cStrData() const { return (char *)ckey; } - inline quint16 *utf16Data() const { return (quint16 *)strData->data(); } - - inline bool equals(const QV4::Value &string) const { - QString s = string.toQStringNoThrow(); - if (isQString()) { - QStringDataPtr dd; - dd.ptr = strData; - strData->ref.ref(); - return QString(dd) == s; - } else { - return QLatin1String(cStrData(), length) == s; - } - } - - inline bool equals(const QV4::String *string) const { - if (length != string->d()->length() || hash != string->hashValue()) - return false; - if (isQString()) { - QStringDataPtr dd; - dd.ptr = strData; - strData->ref.ref(); - return QString(dd) == string->toQString(); - } else { - return QLatin1String(cStrData(), length) == string->toQString(); - } - } - - inline bool equals(const QHashedStringRef &string) const { - return length == string.length() && - hash == string.hash() && - (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length): - QHashedString::compare(string.constData(), cStrData(), length)); - } - - inline bool equals(const QHashedCStringRef &string) const { - return length == string.length() && - hash == string.hash() && - (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length): - QHashedString::compare(string.constData(), cStrData(), length)); - } -}; - -class Q_AUTOTEST_EXPORT QStringHashData -{ -public: - QStringHashData() {} - - QStringHashNode **buckets = nullptr; - int numBuckets = 0; - int size = 0; - short numBits = 0; -#ifdef QSTRINGHASH_LINK_DEBUG - int linkCount = 0; -#endif - - struct IteratorData { - IteratorData() {} - QStringHashNode *n = nullptr; - void *p = nullptr; - }; - void rehashToBits(short); - void rehashToSize(int); - void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node); - -private: - QStringHashData(const QStringHashData &); - QStringHashData &operator=(const QStringHashData &); -}; - -// For a supplied key type, in what form do we need to keep a hashed version? -template<typename T> -struct HashedForm {}; - -template<> struct HashedForm<QString> { typedef QHashedString Type; }; -template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; }; -template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; }; -template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; }; -template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; }; -template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; }; -template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; }; -template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; }; - -class QStringHashBase -{ -public: - static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);} - static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());} - static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; } - static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; } - static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; } - static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; } - - static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); } - static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; } - - static const QString &toQString(const QString &s) { return s; } - static const QString &toQString(const QHashedString &s) { return s; } - static QString toQString(const QV4::String *s) { return s->toQString(); } - static QString toQString(const QHashedStringRef &s) { return s.toString(); } - - static QString toQString(const QLatin1String &s) { return QString(s); } - static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); } - - static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); } - static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); } - static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); } - - template<typename K> - static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); } -}; - -template<class T> -class QStringHash : public QStringHashBase -{ -public: - typedef QHashedString key_type; - typedef T mapped_type; - - struct Node : public QStringHashNode { - Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {} - Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {} - Node(const Node &o) : QStringHashNode(o), value(o.value) {} - Node() {} - T value; - }; - struct NewedNode : public Node { - NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {} - NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {} - NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {} - NewedNode *nextNewed; - }; - struct ReservedNodePool - { - ReservedNodePool() : nodes(nullptr) {} - ~ReservedNodePool() { delete [] nodes; } - int count = 0; - int used = 0; - Node *nodes; - }; - - QStringHashData data; - NewedNode *newedNodes; - ReservedNodePool *nodePool; - const QStringHash<T> *link; - - template<typename K> - inline Node *findNode(const K &) const; - - inline Node *createNode(const Node &o); - - template<typename K> - inline Node *createNode(const K &, const T &); - - inline Node *insertNode(Node *, quint32); - - inline void initializeNode(Node *, const QHashedString &key); - inline void initializeNode(Node *, const QHashedCStringRef &key); - - template<typename K> - inline Node *takeNode(const K &key, const T &value); - - inline Node *takeNode(const Node &o); - - inline void copy(const QStringHash<T> &); - - void copyNode(const QStringHashNode *otherNode); - - inline QStringHashData::IteratorData iterateFirst() const; - static inline QStringHashData::IteratorData iterateNext(const QStringHashData::IteratorData &); - -public: - inline QStringHash(); - inline QStringHash(const QStringHash &); - inline ~QStringHash(); - - QStringHash &operator=(const QStringHash<T> &); - - void copyAndReserve(const QStringHash<T> &other, int additionalReserve); - void linkAndReserve(const QStringHash<T> &other, int additionalReserve); - - inline bool isEmpty() const; - inline void clear(); - inline int count() const; - - inline int numBuckets() const; - inline bool isLinked() const; - - class ConstIterator { - public: - inline ConstIterator(); - inline ConstIterator(const QStringHashData::IteratorData &); - - inline ConstIterator &operator++(); - - inline bool operator==(const ConstIterator &o) const; - inline bool operator!=(const ConstIterator &o) const; - - template<typename K> - inline bool equals(const K &) const; - - inline QHashedString key() const; - inline const T &value() const; - inline const T &operator*() const; - - inline Node *node() const; - private: - QStringHashData::IteratorData d; - }; - - template<typename K> - inline void insert(const K &, const T &); - - inline void insert(const ConstIterator &); - - template<typename K> - inline T *value(const K &) const; - - inline T *value(const QV4::String *string) const; - inline T *value(const ConstIterator &) const; - - template<typename K> - inline bool contains(const K &) const; - - template<typename K> - inline T &operator[](const K &); - - inline ConstIterator begin() const; - inline ConstIterator end() const; - - inline ConstIterator iterator(Node *n) const; - - template<typename K> - inline ConstIterator find(const K &) const; - - inline void reserve(int); -}; - -template<class T> -QStringHash<T>::QStringHash() -: newedNodes(nullptr), nodePool(nullptr), link(nullptr) -{ -} - -template<class T> -QStringHash<T>::QStringHash(const QStringHash<T> &other) -: newedNodes(nullptr), nodePool(nullptr), link(nullptr) -{ - data.numBits = other.data.numBits; - data.size = other.data.size; - reserve(other.count()); - copy(other); -} - -template<class T> -QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other) -{ - if (&other == this) - return *this; - - clear(); - - data.numBits = other.data.numBits; - data.size = other.data.size; - reserve(other.count()); - copy(other); - - return *this; -} - -template<class T> -void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve) -{ - clear(); - data.numBits = other.data.numBits; - reserve(other.count() + additionalReserve); - copy(other); -} - -template<class T> -void QStringHash<T>::linkAndReserve(const QStringHash<T> &other, int additionalReserve) -{ - clear(); - - if (other.count()) { - data.size = other.data.size; - data.rehashToSize(other.count() + additionalReserve); - - if (data.numBuckets == other.data.numBuckets) { - nodePool = new ReservedNodePool; - nodePool->count = additionalReserve; - nodePool->used = 0; - nodePool->nodes = new Node[additionalReserve]; - -#ifdef QSTRINGHASH_LINK_DEBUG - data.linkCount++; - const_cast<QStringHash<T>&>(other).data.linkCount++; -#endif - - for (int ii = 0; ii < data.numBuckets; ++ii) - data.buckets[ii] = (Node *)other.data.buckets[ii]; - - link = &other; - return; - } - - data.size = 0; - } - - data.numBits = other.data.numBits; - reserve(other.count() + additionalReserve); - copy(other); -} - -template<class T> -QStringHash<T>::~QStringHash() -{ - clear(); -} - -template<class T> -void QStringHash<T>::clear() -{ -#ifdef QSTRINGHASH_LINK_DEBUG - if (link) { - data.linkCount--; - const_cast<QStringHash<T> *>(link)->data.linkCount--; - } - - if (data.linkCount) - qFatal("QStringHash: Illegal attempt to clear a linked hash."); -#endif - - // Delete the individually allocated nodes - NewedNode *n = newedNodes; - while (n) { - NewedNode *c = n; - n = c->nextNewed; - delete c; - } - // Delete the pool allocated nodes - if (nodePool) delete nodePool; - delete [] data.buckets; - - data.buckets = nullptr; - data.numBuckets = 0; - data.numBits = 0; - data.size = 0; - - newedNodes = nullptr; - nodePool = nullptr; - link = nullptr; -} - -template<class T> -bool QStringHash<T>::isEmpty() const -{ - return data.size== 0; -} - -template<class T> -int QStringHash<T>::count() const -{ - return data.size; -} - -template<class T> -int QStringHash<T>::numBuckets() const -{ - return data.numBuckets; -} - -template<class T> -bool QStringHash<T>::isLinked() const -{ - return link != 0; -} - -template<class T> -void QStringHash<T>::initializeNode(Node *node, const QHashedString &key) -{ - node->length = key.length(); - node->hash = key.hash(); - node->strData = const_cast<QHashedString &>(key).data_ptr(); - node->strData->ref.ref(); - node->setQString(true); -} - -template<class T> -void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key) -{ - node->length = key.length(); - node->hash = key.hash(); - node->ckey = key.constData(); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value) -{ - if (nodePool && nodePool->used != nodePool->count) { - Node *rv = nodePool->nodes + nodePool->used++; - initializeNode(rv, hashedString(key)); - rv->value = value; - return rv; - } else { - NewedNode *rv = new NewedNode(hashedString(key), value); - rv->nextNewed = newedNodes; - newedNodes = rv; - return rv; - } -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o) -{ - if (nodePool && nodePool->used != nodePool->count) { - Node *rv = nodePool->nodes + nodePool->used++; - rv->length = o.length; - rv->hash = o.hash; - if (o.isQString()) { - rv->strData = o.strData; - rv->strData->ref.ref(); - rv->setQString(true); - } else { - rv->ckey = o.ckey; - } - rv->symbolId = o.symbolId; - rv->value = o.value; - return rv; - } else { - NewedNode *rv = new NewedNode(o); - rv->nextNewed = newedNodes; - newedNodes = rv; - return rv; - } -} - -template<class T> -void QStringHash<T>::copyNode(const QStringHashNode *otherNode) -{ - // Copy the predecessor before the successor - QStringHashNode *next = otherNode->next.data(); - if (next) - copyNode(next); - - Node *mynode = takeNode(*(const Node *)otherNode); - int bucket = mynode->hash % data.numBuckets; - mynode->next = data.buckets[bucket]; - data.buckets[bucket] = mynode; -} - -template<class T> -void QStringHash<T>::copy(const QStringHash<T> &other) -{ - Q_ASSERT(data.size == 0); - - data.size = other.data.size; - - // Ensure buckets array is created - data.rehashToBits(data.numBits); - - // Preserve the existing order within buckets - for (int i = 0; i < other.data.numBuckets; ++i) { - QStringHashNode *bucket = other.data.buckets[i]; - if (bucket) - copyNode(bucket); - } -} - -template<class T> -QStringHashData::IteratorData -QStringHash<T>::iterateNext(const QStringHashData::IteratorData &d) -{ - QStringHash<T> *This = (QStringHash<T> *)d.p; - Node *node = (Node *)d.n; - - if (This->nodePool && node >= This->nodePool->nodes && - node < (This->nodePool->nodes + This->nodePool->used)) { - node--; - if (node < This->nodePool->nodes) - node = nullptr; - } else { - NewedNode *nn = (NewedNode *)node; - node = nn->nextNewed; - - if (node == nullptr && This->nodePool && This->nodePool->used) - node = This->nodePool->nodes + This->nodePool->used - 1; - } - - if (node == nullptr && This->link) - return This->link->iterateFirst(); - - QStringHashData::IteratorData rv; - rv.n = node; - rv.p = d.p; - return rv; -} - -template<class T> -QStringHashData::IteratorData QStringHash<T>::iterateFirst() const -{ - Node *n = nullptr; - if (newedNodes) - n = newedNodes; - else if (nodePool && nodePool->used) - n = nodePool->nodes + nodePool->used - 1; - - if (n == nullptr && link) - return link->iterateFirst(); - - QStringHashData::IteratorData rv; - rv.n = n; - rv.p = const_cast<QStringHash<T> *>(this); - return rv; -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::iterator(Node *n) const -{ - if (!n) - return ConstIterator(); - - const QStringHash<T> *container = this; - - if (link) { - // This node could be in the linked hash - if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) { - // The node is in this hash - } else if ((n >= link->nodePool->nodes) && (n < (link->nodePool->nodes + link->nodePool->used))) { - // The node is in the linked hash - container = link; - } else { - const NewedNode *ln = link->newedNodes; - while (ln) { - if (ln == n) { - // This node is in the linked hash's newed list - container = link; - break; - } - ln = ln->nextNewed; - } - } - } - - QStringHashData::IteratorData rv; - rv.n = n; - rv.p = const_cast<QStringHash<T> *>(container); - return ConstIterator(rv); -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o) -{ - Node *n = takeNode(o); - return insertNode(n, n->hash); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value) -{ - Node *n = takeNode(key, value); - return insertNode(n, hashOf(key)); -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash) -{ - if (data.size >= data.numBuckets) - data.rehashToBits(data.numBits + 1); - - int bucket = hash % data.numBuckets; - n->next = data.buckets[bucket]; - data.buckets[bucket] = n; - - data.size++; - - return n; -} - -template<class T> -template<class K> -void QStringHash<T>::insert(const K &key, const T &value) -{ - // If this is a linked hash, we can't rely on owning the node, so we always - // create a new one. - Node *n = link?nullptr:findNode(key); - if (n) n->value = value; - else createNode(key, value); -} - -template<class T> -void QStringHash<T>::insert(const ConstIterator &iter) -{ - insert(iter.key(), iter.value()); -} - -template<class T> -template<class K> -typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const -{ - QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr; - - typename HashedForm<K>::Type hashedKey(hashedString(key)); - while (node && !node->equals(hashedKey)) - node = (*node->next); - - return (Node *)node; -} - -template<class T> -template<class K> -T *QStringHash<T>::value(const K &key) const -{ - Node *n = findNode(key); - return n?&n->value:nullptr; -} - -template<class T> -T *QStringHash<T>::value(const ConstIterator &iter) const -{ - Node *n = iter.node(); - return value(n->key()); -} - -template<class T> -T *QStringHash<T>::value(const QV4::String *string) const -{ - Node *n = findNode(string); - return n?&n->value:nullptr; -} - -template<class T> -template<class K> -bool QStringHash<T>::contains(const K &key) const -{ - return nullptr != value(key); -} - -template<class T> -template<class K> -T &QStringHash<T>::operator[](const K &key) -{ - Node *n = findNode(key); - if (n) return n->value; - else return createNode(key, T())->value; -} - -template<class T> -void QStringHash<T>::reserve(int n) -{ - if (nodePool || 0 == n) - return; - - nodePool = new ReservedNodePool; - nodePool->count = n; - nodePool->used = 0; - nodePool->nodes = new Node[n]; - - data.rehashToSize(n); -} - -template<class T> -QStringHash<T>::ConstIterator::ConstIterator() -{ -} - -template<class T> -QStringHash<T>::ConstIterator::ConstIterator(const QStringHashData::IteratorData &d) -: d(d) -{ -} - -template<class T> -typename QStringHash<T>::ConstIterator &QStringHash<T>::ConstIterator::operator++() -{ - d = QStringHash<T>::iterateNext(d); - return *this; -} - -template<class T> -bool QStringHash<T>::ConstIterator::operator==(const ConstIterator &o) const -{ - return d.n == o.d.n; -} - -template<class T> -bool QStringHash<T>::ConstIterator::operator!=(const ConstIterator &o) const -{ - return d.n != o.d.n; -} - -template<class T> -template<typename K> -bool QStringHash<T>::ConstIterator::equals(const K &key) const -{ - return d.n->equals(key); -} - -template<class T> -QHashedString QStringHash<T>::ConstIterator::key() const -{ - Node *n = (Node *)d.n; - return n->key(); -} -template<class T> -const T &QStringHash<T>::ConstIterator::value() const -{ - Node *n = (Node *)d.n; - return n->value; -} - -template<class T> -const T &QStringHash<T>::ConstIterator::operator*() const -{ - Node *n = (Node *)d.n; - return n->value; -} - -template<class T> -typename QStringHash<T>::Node *QStringHash<T>::ConstIterator::node() const -{ - Node *n = (Node *)d.n; - return n; -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const -{ - return ConstIterator(iterateFirst()); -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringHash<T>::end() const -{ - return ConstIterator(); -} - -template<class T> -template<class K> -typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const -{ - return iterator(findNode(key)); -} - -template<class T> -class QStringMultiHash : public QStringHash<T> -{ -public: - typedef typename QStringHash<T>::ConstIterator ConstIterator; - - template<typename K> - inline void insert(const K &, const T &); - - inline void insert(const ConstIterator &); - - inline ConstIterator findNext(const ConstIterator &) const; -}; - -template<class T> -template<class K> -void QStringMultiHash<T>::insert(const K &key, const T &value) -{ - // Always create a new node - QStringHash<T>::createNode(key, value); -} - -template<class T> -void QStringMultiHash<T>::insert(const ConstIterator &iter) -{ - // Always create a new node - QStringHash<T>::createNode(iter.key(), iter.value()); -} - -template<class T> -typename QStringHash<T>::ConstIterator QStringMultiHash<T>::findNext(const ConstIterator &iter) const -{ - QStringHashNode *node = iter.node(); - if (node) { - QHashedString key(node->key()); - - while ((node = *node->next)) { - if (node->equals(key)) { - return QStringHash<T>::iterator(static_cast<typename QStringHash<T>::Node *>(node)); - } - } - } - - return ConstIterator(); -} - inline uint qHash(const QHashedString &string) { return uint(string.hash()); diff --git a/src/qml/qml/ftw/qlinkedstringhash_p.h b/src/qml/qml/ftw/qlinkedstringhash_p.h new file mode 100644 index 0000000000..67ced7fbbf --- /dev/null +++ b/src/qml/qml/ftw/qlinkedstringhash_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** 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 QLINKEDSTRINGHASH_P_H +#define QLINKEDSTRINGHASH_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/qstringhash_p.h> + +QT_BEGIN_NAMESPACE + +template<class T> +class QLinkedStringHash : private QStringHash<T> +{ +public: + using typename QStringHash<T>::Node; + using typename QStringHash<T>::NewedNode; + using typename QStringHash<T>::ReservedNodePool; + using typename QStringHash<T>::mapped_type; + + using ConstIteratorData = QStringHashData::IteratorData<const QLinkedStringHash>; + using ConstIterator = typename QStringHash<T>::template Iterator<ConstIteratorData, const T>; + + void linkAndReserve(const QLinkedStringHash<T> &other, int additionalReserve) + { + clear(); + + if (other.count()) { + data.size = other.data.size; + data.rehashToSize(other.count() + additionalReserve); + + if (data.numBuckets == other.data.numBuckets) { + nodePool = new ReservedNodePool; + nodePool->count = additionalReserve; + nodePool->used = 0; + nodePool->nodes = new Node[additionalReserve]; + + for (int ii = 0; ii < data.numBuckets; ++ii) + data.buckets[ii] = (Node *)other.data.buckets[ii]; + + link = &other; + return; + } + + data.size = 0; + } + + data.numBits = other.data.numBits; + reserve(other.count() + additionalReserve); + copy(other); + } + + inline bool isLinked() const + { + return link != 0; + } + + void clear() + { + QStringHash<T>::clear(); + link = nullptr; + } + + template<typename K> + void insert(const K &key, const T &value) + { + // If this is a linked hash, we can't rely on owning the node, so we always + // create a new one. + Node *n = link ? nullptr : QStringHash<T>::findNode(key); + if (n) + n->value = value; + else + QStringHash<T>::createNode(key, value); + } + + template<typename K> + inline ConstIterator find(const K &key) const + { + return iterator(QStringHash<T>::findNode(key)); + } + + ConstIterator begin() const + { + return ConstIterator( + QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>, + ConstIteratorData>(this)); + } + + ConstIterator end() const { return ConstIterator(); } + + inline T *value(const ConstIterator &iter) { return value(iter.node()->key()); } + + using QStringHash<T>::value; + using QStringHash<T>::reserve; + using QStringHash<T>::copy; + +protected: + friend QStringHash<T>; + using QStringHash<T>::data; + using QStringHash<T>::nodePool; + + using QStringHash<T>::createNode; + + inline ConstIteratorData iterateFirst() const + { + const ConstIteratorData rv + = QStringHash<T>::template iterateFirst<const QLinkedStringHash<T>, + ConstIteratorData>(this); + return (rv.n == nullptr && link) ? link->iterateFirst() : rv; + } + + static inline ConstIteratorData iterateNext(const ConstIteratorData &d) + { + const QLinkedStringHash<T> *self = d.p; + const ConstIteratorData rv = QStringHash<T>::iterateNext(d); + return (rv.n == nullptr && self->link) ? self->link->iterateFirst() : rv; + } + + inline ConstIterator iterator(Node *n) const + { + if (!n) + return ConstIterator(); + + const QLinkedStringHash<T> *container = this; + + if (link) { + // This node could be in the linked hash + if ((n >= nodePool->nodes) && (n < (nodePool->nodes + nodePool->used))) { + // The node is in this hash + } else if ((n >= link->nodePool->nodes) + && (n < (link->nodePool->nodes + link->nodePool->used))) { + // The node is in the linked hash + container = link; + } else { + const NewedNode *ln = link->newedNodes; + while (ln) { + if (ln == n) { + // This node is in the linked hash's newed list + container = link; + break; + } + ln = ln->nextNewed; + } + } + } + + + ConstIteratorData rv; + rv.n = n; + rv.p = container; + return ConstIterator(rv); + } + + const QLinkedStringHash<T> *link = nullptr; +}; + +template<class T> +class QLinkedStringMultiHash : public QLinkedStringHash<T> +{ +public: + using ConstIterator = typename QLinkedStringHash<T>::ConstIterator; + + template<typename K> + inline void insert(const K &key, const T &value) + { + // Always create a new node + QLinkedStringHash<T>::createNode(key, value); + } + + inline void insert(const ConstIterator &iter) + { + // Always create a new node + QLinkedStringHash<T>::createNode(iter.key(), iter.value()); + } + + inline ConstIterator findNext(const ConstIterator &iter) const + { + if (auto *node = iter.node()) { + QHashedString key(node->key()); + while ((node = static_cast<typename QLinkedStringHash<T>::Node *>(*node->next))) { + if (node->equals(key)) + return QLinkedStringHash<T>::iterator(node); + } + } + + return ConstIterator(); + } +}; + +QT_END_NAMESPACE + +#endif // QLINKEDSTRINGHASH_P_H diff --git a/src/qml/qml/ftw/qprimefornumbits_p.h b/src/qml/qml/ftw/qprimefornumbits_p.h new file mode 100644 index 0000000000..6e9acbf7fd --- /dev/null +++ b/src/qml/qml/ftw/qprimefornumbits_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 QPRIMEFORNUMBITS_P_H +#define QPRIMEFORNUMBITS_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/qtqmlglobal_p.h> + +QT_BEGIN_NAMESPACE + +/* + The prime_deltas array is a table of selected prime values, even + though it doesn't look like one. The primes we are using are 1, + 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate + surrounding of a power of two. + + The qPrimeForNumBits() function returns the prime associated to a + power of two. For example, qPrimeForNumBits(8) returns 257. +*/ + +inline int qPrimeForNumBits(int numBits) +{ + static constexpr const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 + }; + + return (1 << numBits) + prime_deltas[numBits]; +} + +QT_END_NAMESPACE + +#endif // QPRIMEFORNUMBITS_P_H diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index d32a08e0f5..b4f8acad49 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -60,18 +60,18 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlRefCount { + Q_DISABLE_COPY_MOVE(QQmlRefCount) public: inline QQmlRefCount(); - inline virtual ~QQmlRefCount(); - inline void addref(); - inline void release(); + inline void addref() const; + inline void release() const; inline int count() const; protected: - inline virtual void destroy(); + inline virtual ~QQmlRefCount(); private: - QAtomicInt refCount; + mutable QAtomicInt refCount; }; template<class T> @@ -113,30 +113,25 @@ QQmlRefCount::QQmlRefCount() QQmlRefCount::~QQmlRefCount() { - Q_ASSERT(refCount.load() == 0); + Q_ASSERT(refCount.loadRelaxed() == 0); } -void QQmlRefCount::addref() +void QQmlRefCount::addref() const { - Q_ASSERT(refCount.load() > 0); + Q_ASSERT(refCount.loadRelaxed() > 0); refCount.ref(); } -void QQmlRefCount::release() +void QQmlRefCount::release() const { - Q_ASSERT(refCount.load() > 0); + Q_ASSERT(refCount.loadRelaxed() > 0); if (!refCount.deref()) - destroy(); + delete this; } int QQmlRefCount::count() const { - return refCount.load(); -} - -void QQmlRefCount::destroy() -{ - delete this; + return refCount.loadRelaxed(); } template<class T> diff --git a/src/qml/qml/ftw/qstringhash_p.h b/src/qml/qml/ftw/qstringhash_p.h new file mode 100644 index 0000000000..f9435b4919 --- /dev/null +++ b/src/qml/qml/ftw/qstringhash_p.h @@ -0,0 +1,807 @@ +/**************************************************************************** +** +** 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 QSTRINGHASH_P_H +#define QSTRINGHASH_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/qhashedstring_p.h> +#include <private/qprimefornumbits_p.h> + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QStringHashData; +class QStringHashNode +{ +public: + QStringHashNode() + : ckey(nullptr) + { + } + + QStringHashNode(const QHashedString &key) + : length(key.length()), hash(key.hash()), symbolId(0) + { + strData = const_cast<QHashedString &>(key).data_ptr(); + setQString(true); + strData->ref.ref(); + } + + QStringHashNode(const QHashedCStringRef &key) + : length(key.length()), hash(key.hash()), symbolId(0), ckey(key.constData()) + { + } + + QStringHashNode(const QStringHashNode &o) + : length(o.length), hash(o.hash), symbolId(o.symbolId), ckey(o.ckey) + { + setQString(o.isQString()); + if (isQString()) { strData->ref.ref(); } + } + + ~QStringHashNode() + { + if (isQString()) { if (!strData->ref.deref()) free(strData); } + } + + QFlagPointer<QStringHashNode> next; + + qint32 length = 0; + quint32 hash = 0; + quint32 symbolId = 0; + + union { + const char *ckey; + QStringData *strData; + }; + + inline QHashedString key() const + { + if (isQString()) + return QHashedString(QString((QChar *)strData->data(), length), hash); + + return QHashedString(QString::fromLatin1(ckey, length), hash); + } + + bool isQString() const { return next.flag(); } + void setQString(bool v) { if (v) next.setFlag(); else next.clearFlag(); } + + inline char *cStrData() const { return (char *)ckey; } + inline quint16 *utf16Data() const { return (quint16 *)strData->data(); } + + inline bool equals(const QV4::Value &string) const { + QString s = string.toQStringNoThrow(); + if (isQString()) { + QStringDataPtr dd; + dd.ptr = strData; + strData->ref.ref(); + return QString(dd) == s; + } else { + return QLatin1String(cStrData(), length) == s; + } + } + + inline bool equals(const QV4::String *string) const { + if (length != string->d()->length() || hash != string->hashValue()) + return false; + if (isQString()) { + QStringDataPtr dd; + dd.ptr = strData; + strData->ref.ref(); + return QString(dd) == string->toQString(); + } else { + return QLatin1String(cStrData(), length) == string->toQString(); + } + } + + inline bool equals(const QHashedStringRef &string) const { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare(string.constData(), (const QChar *)utf16Data(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } + + inline bool equals(const QHashedCStringRef &string) const { + return length == string.length() && + hash == string.hash() && + (isQString()?QHashedString::compare((const QChar *)utf16Data(), string.constData(), length): + QHashedString::compare(string.constData(), cStrData(), length)); + } +}; + +class QStringHashData +{ + Q_DISABLE_COPY_MOVE(QStringHashData) +public: + QStringHashData() = default; + ~QStringHashData() = default; + + /* + A QHash has initially around pow(2, MinNumBits) buckets. For + example, if MinNumBits is 4, it has 17 buckets. + */ + enum { MinNumBits = 4 }; + + QStringHashNode **buckets = nullptr; // life cycle managed by QStringHash + int numBuckets = 0; + int size = 0; + short numBits = 0; + + template<typename StringHash> + struct IteratorData { + IteratorData(QStringHashNode *n = nullptr, StringHash *p = nullptr) : n(n), p(p) {} + + template<typename OtherData> + IteratorData(const OtherData &other) : n(other.n), p(other.p) {} + + QStringHashNode *n; + StringHash *p; + }; + + void rehashToBits(short bits) + { + numBits = qMax(short(MinNumBits), bits); + + int nb = qPrimeForNumBits(numBits); + if (nb == numBuckets && buckets) + return; + + QStringHashNode **newBuckets = new QStringHashNode *[nb]; + ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); + + // Preserve the existing order within buckets so that items with the + // same key will retain the same find/findNext order + for (int i = 0; i < numBuckets; ++i) { + QStringHashNode *bucket = buckets[i]; + if (bucket) + rehashNode(newBuckets, nb, bucket); + } + + delete [] buckets; + buckets = newBuckets; + numBuckets = nb; + } + + void rehashToSize(int size) + { + short bits = qMax(short(MinNumBits), numBits); + while (qPrimeForNumBits(bits) < size) + bits++; + + if (bits > numBits) + rehashToBits(bits); + } + + void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) + { + QStringHashNode *next = node->next.data(); + if (next) + rehashNode(newBuckets, nb, next); + + int bucket = node->hash % nb; + node->next = newBuckets[bucket]; + newBuckets[bucket] = node; + } +}; + +// For a supplied key type, in what form do we need to keep a hashed version? +template<typename T> +struct HashedForm {}; + +template<> struct HashedForm<QString> { typedef QHashedString Type; }; +template<> struct HashedForm<QStringRef> { typedef QHashedStringRef Type; }; +template<> struct HashedForm<QHashedString> { typedef const QHashedString &Type; }; +template<> struct HashedForm<QV4::String *> { typedef const QV4::String *Type; }; +template<> struct HashedForm<const QV4::String *> { typedef const QV4::String *Type; }; +template<> struct HashedForm<QHashedStringRef> { typedef const QHashedStringRef &Type; }; +template<> struct HashedForm<QLatin1String> { typedef QHashedCStringRef Type; }; +template<> struct HashedForm<QHashedCStringRef> { typedef const QHashedCStringRef &Type; }; + +class QStringHashBase +{ +public: + static HashedForm<QString>::Type hashedString(const QString &s) { return QHashedString(s);} + static HashedForm<QStringRef>::Type hashedString(const QStringRef &s) { return QHashedStringRef(s.constData(), s.size());} + static HashedForm<QHashedString>::Type hashedString(const QHashedString &s) { return s; } + static HashedForm<QV4::String *>::Type hashedString(QV4::String *s) { return s; } + static HashedForm<const QV4::String *>::Type hashedString(const QV4::String *s) { return s; } + static HashedForm<QHashedStringRef>::Type hashedString(const QHashedStringRef &s) { return s; } + + static HashedForm<QLatin1String>::Type hashedString(const QLatin1String &s) { return QHashedCStringRef(s.data(), s.size()); } + static HashedForm<QHashedCStringRef>::Type hashedString(const QHashedCStringRef &s) { return s; } + + static const QString &toQString(const QString &s) { return s; } + static const QString &toQString(const QHashedString &s) { return s; } + static QString toQString(const QV4::String *s) { return s->toQString(); } + static QString toQString(const QHashedStringRef &s) { return s.toString(); } + + static QString toQString(const QLatin1String &s) { return QString(s); } + static QString toQString(const QHashedCStringRef &s) { return s.toUtf16(); } + + static inline quint32 hashOf(const QHashedStringRef &s) { return s.hash(); } + static inline quint32 hashOf(QV4::String *s) { return s->hashValue(); } + static inline quint32 hashOf(const QV4::String *s) { return s->hashValue(); } + + template<typename K> + static inline quint32 hashOf(const K &key) { return hashedString(key).hash(); } +}; + +template<class T> +class QStringHash : public QStringHashBase +{ +public: + typedef QHashedString key_type; + typedef T mapped_type; + + using MutableIteratorData = QStringHashData::IteratorData<QStringHash<T>>; + using ConstIteratorData = QStringHashData::IteratorData<const QStringHash<T>>; + + struct Node : public QStringHashNode { + Node(const QHashedString &key, const T &value) : QStringHashNode(key), value(value) {} + Node(const QHashedCStringRef &key, const T &value) : QStringHashNode(key), value(value) {} + Node(const Node &o) : QStringHashNode(o), value(o.value) {} + Node() {} + T value; + }; + struct NewedNode : public Node { + NewedNode(const QHashedString &key, const T &value) : Node(key, value), nextNewed(nullptr) {} + NewedNode(const QHashedCStringRef &key, const T &value) : Node(key, value), nextNewed(nullptr) {} + NewedNode(const Node &o) : Node(o), nextNewed(nullptr) {} + NewedNode *nextNewed; + }; + struct ReservedNodePool + { + ReservedNodePool() : nodes(nullptr) {} + ~ReservedNodePool() { delete [] nodes; } + int count = 0; + int used = 0; + Node *nodes; + }; + + QStringHashData data; + NewedNode *newedNodes; + ReservedNodePool *nodePool; + + template<typename K> + inline Node *findNode(const K &) const; + + inline Node *createNode(const Node &o); + + template<typename K> + inline Node *createNode(const K &, const T &); + + inline Node *insertNode(Node *, quint32); + + inline void initializeNode(Node *, const QHashedString &key); + inline void initializeNode(Node *, const QHashedCStringRef &key); + + template<typename K> + inline Node *takeNode(const K &key, const T &value); + + inline Node *takeNode(const Node &o); + + inline void copy(const QStringHash<T> &); + + void copyNode(const QStringHashNode *otherNode); + + template<typename StringHash, typename Data> + static inline Data iterateFirst(StringHash *self); + + template<typename Data> + static inline Data iterateNext(const Data &); + +public: + inline QStringHash(); + inline QStringHash(const QStringHash &); + inline ~QStringHash(); + + QStringHash &operator=(const QStringHash<T> &); + + void copyAndReserve(const QStringHash<T> &other, int additionalReserve); + + inline bool isEmpty() const; + inline void clear(); + inline int count() const; + + inline int numBuckets() const; + + template<typename Data, typename Value> + class Iterator { + public: + inline Iterator() = default; + inline Iterator(const Data &d) : d(d) {} + + inline Iterator &operator++() + { + d = QStringHash<T>::iterateNext(d); + return *this; + } + + inline bool operator==(const Iterator &o) const { return d.n == o.d.n; } + inline bool operator!=(const Iterator &o) const { return d.n != o.d.n; } + + template<typename K> + inline bool equals(const K &key) const { return d.n->equals(key); } + + inline QHashedString key() const { return static_cast<Node *>(d.n)->key(); } + inline Value &value() const { return static_cast<Node *>(d.n)->value; } + inline Value &operator*() const { return static_cast<Node *>(d.n)->value; } + + Node *node() const { return static_cast<Node *>(d.n); } + private: + Data d; + }; + + using MutableIterator = Iterator<MutableIteratorData, T>; + using ConstIterator = Iterator<ConstIteratorData, const T>; + + template<typename K> + inline void insert(const K &, const T &); + inline void insert(const MutableIterator &); + inline void insert(const ConstIterator &); + + template<typename K> + inline T *value(const K &) const; + inline T *value(const QV4::String *string) const; + inline T *value(const MutableIterator &) const; + inline T *value(const ConstIterator &) const; + + template<typename K> + inline bool contains(const K &) const; + + template<typename K> + inline T &operator[](const K &); + + inline MutableIterator begin(); + inline ConstIterator begin() const; + inline ConstIterator constBegin() const { return begin(); } + + inline MutableIterator end(); + inline ConstIterator end() const; + inline ConstIterator constEnd() const { return end(); } + + template<typename K> + inline MutableIterator find(const K &); + + template<typename K> + inline ConstIterator find(const K &) const; + + inline void reserve(int); +}; + +template<class T> +QStringHash<T>::QStringHash() +: newedNodes(nullptr), nodePool(nullptr) +{ +} + +template<class T> +QStringHash<T>::QStringHash(const QStringHash<T> &other) +: newedNodes(nullptr), nodePool(nullptr) +{ + data.numBits = other.data.numBits; + data.size = other.data.size; + reserve(other.count()); + copy(other); +} + +template<class T> +QStringHash<T> &QStringHash<T>::operator=(const QStringHash<T> &other) +{ + if (&other == this) + return *this; + + clear(); + + data.numBits = other.data.numBits; + data.size = other.data.size; + reserve(other.count()); + copy(other); + + return *this; +} + +template<class T> +void QStringHash<T>::copyAndReserve(const QStringHash<T> &other, int additionalReserve) +{ + clear(); + data.numBits = other.data.numBits; + reserve(other.count() + additionalReserve); + copy(other); +} + +template<class T> +QStringHash<T>::~QStringHash() +{ + clear(); +} + +template<class T> +void QStringHash<T>::clear() +{ + // Delete the individually allocated nodes + NewedNode *n = newedNodes; + while (n) { + NewedNode *c = n; + n = c->nextNewed; + delete c; + } + // Delete the pool allocated nodes + if (nodePool) delete nodePool; + delete [] data.buckets; + + data.buckets = nullptr; + data.numBuckets = 0; + data.numBits = 0; + data.size = 0; + + newedNodes = nullptr; + nodePool = nullptr; +} + +template<class T> +bool QStringHash<T>::isEmpty() const +{ + return data.size== 0; +} + +template<class T> +int QStringHash<T>::count() const +{ + return data.size; +} + +template<class T> +int QStringHash<T>::numBuckets() const +{ + return data.numBuckets; +} + +template<class T> +void QStringHash<T>::initializeNode(Node *node, const QHashedString &key) +{ + node->length = key.length(); + node->hash = key.hash(); + node->strData = const_cast<QHashedString &>(key).data_ptr(); + node->strData->ref.ref(); + node->setQString(true); +} + +template<class T> +void QStringHash<T>::initializeNode(Node *node, const QHashedCStringRef &key) +{ + node->length = key.length(); + node->hash = key.hash(); + node->ckey = key.constData(); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const K &key, const T &value) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + initializeNode(rv, hashedString(key)); + rv->value = value; + return rv; + } else { + NewedNode *rv = new NewedNode(hashedString(key), value); + rv->nextNewed = newedNodes; + newedNodes = rv; + return rv; + } +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::takeNode(const Node &o) +{ + if (nodePool && nodePool->used != nodePool->count) { + Node *rv = nodePool->nodes + nodePool->used++; + rv->length = o.length; + rv->hash = o.hash; + if (o.isQString()) { + rv->strData = o.strData; + rv->strData->ref.ref(); + rv->setQString(true); + } else { + rv->ckey = o.ckey; + } + rv->symbolId = o.symbolId; + rv->value = o.value; + return rv; + } else { + NewedNode *rv = new NewedNode(o); + rv->nextNewed = newedNodes; + newedNodes = rv; + return rv; + } +} + +template<class T> +void QStringHash<T>::copyNode(const QStringHashNode *otherNode) +{ + // Copy the predecessor before the successor + QStringHashNode *next = otherNode->next.data(); + if (next) + copyNode(next); + + Node *mynode = takeNode(*(const Node *)otherNode); + int bucket = mynode->hash % data.numBuckets; + mynode->next = data.buckets[bucket]; + data.buckets[bucket] = mynode; +} + +template<class T> +void QStringHash<T>::copy(const QStringHash<T> &other) +{ + Q_ASSERT(data.size == 0); + + data.size = other.data.size; + + // Ensure buckets array is created + data.rehashToBits(data.numBits); + + // Preserve the existing order within buckets + for (int i = 0; i < other.data.numBuckets; ++i) { + QStringHashNode *bucket = other.data.buckets[i]; + if (bucket) + copyNode(bucket); + } +} + +template<class T> +template<typename Data> +Data QStringHash<T>::iterateNext(const Data &d) +{ + auto *This = d.p; + Node *node = (Node *)d.n; + + if (This->nodePool && node >= This->nodePool->nodes && + node < (This->nodePool->nodes + This->nodePool->used)) { + node--; + if (node < This->nodePool->nodes) + node = nullptr; + } else { + NewedNode *nn = (NewedNode *)node; + node = nn->nextNewed; + + if (node == nullptr && This->nodePool && This->nodePool->used) + node = This->nodePool->nodes + This->nodePool->used - 1; + } + + Data rv; + rv.n = node; + rv.p = d.p; + return rv; +} + +template<class T> +template<typename StringHash, typename Data> +Data QStringHash<T>::iterateFirst(StringHash *self) +{ + typename StringHash::Node *n = nullptr; + if (self->newedNodes) + n = self->newedNodes; + else if (self->nodePool && self->nodePool->used) + n = self->nodePool->nodes + self->nodePool->used - 1; + + Data rv; + rv.n = n; + rv.p = self; + return rv; +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const Node &o) +{ + Node *n = takeNode(o); + return insertNode(n, n->hash); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::createNode(const K &key, const T &value) +{ + Node *n = takeNode(key, value); + return insertNode(n, hashOf(key)); +} + +template<class T> +typename QStringHash<T>::Node *QStringHash<T>::insertNode(Node *n, quint32 hash) +{ + if (data.size >= data.numBuckets) + data.rehashToBits(data.numBits + 1); + + int bucket = hash % data.numBuckets; + n->next = data.buckets[bucket]; + data.buckets[bucket] = n; + + data.size++; + + return n; +} + +template<class T> +template<class K> +void QStringHash<T>::insert(const K &key, const T &value) +{ + Node *n = findNode(key); + if (n) + n->value = value; + else + createNode(key, value); +} + +template<class T> +void QStringHash<T>::insert(const MutableIterator &iter) +{ + insert(iter.key(), iter.value()); +} + +template<class T> +void QStringHash<T>::insert(const ConstIterator &iter) +{ + insert(iter.key(), iter.value()); +} + +template<class T> +template<class K> +typename QStringHash<T>::Node *QStringHash<T>::findNode(const K &key) const +{ + QStringHashNode *node = data.numBuckets?data.buckets[hashOf(key) % data.numBuckets]:nullptr; + + typename HashedForm<K>::Type hashedKey(hashedString(key)); + while (node && !node->equals(hashedKey)) + node = (*node->next); + + return (Node *)node; +} + +template<class T> +template<class K> +T *QStringHash<T>::value(const K &key) const +{ + Node *n = findNode(key); + return n?&n->value:nullptr; +} + +template<typename T> +T *QStringHash<T>::value(const MutableIterator &iter) const +{ + return value(iter.node()->key()); +} + +template<class T> +T *QStringHash<T>::value(const ConstIterator &iter) const +{ + return value(iter.node()->key()); +} + +template<class T> +T *QStringHash<T>::value(const QV4::String *string) const +{ + Node *n = findNode(string); + return n?&n->value:nullptr; +} + +template<class T> +template<class K> +bool QStringHash<T>::contains(const K &key) const +{ + return nullptr != value(key); +} + +template<class T> +template<class K> +T &QStringHash<T>::operator[](const K &key) +{ + Node *n = findNode(key); + if (n) return n->value; + else return createNode(key, T())->value; +} + +template<class T> +void QStringHash<T>::reserve(int n) +{ + if (nodePool || 0 == n) + return; + + nodePool = new ReservedNodePool; + nodePool->count = n; + nodePool->used = 0; + nodePool->nodes = new Node[n]; + + data.rehashToSize(n); +} + +template<class T> +typename QStringHash<T>::MutableIterator QStringHash<T>::begin() +{ + return MutableIterator(iterateFirst<QStringHash<T>, MutableIteratorData>(this)); +} + +template<class T> +typename QStringHash<T>::ConstIterator QStringHash<T>::begin() const +{ + return ConstIterator(iterateFirst<const QStringHash<T>, ConstIteratorData>(this)); +} + +template<class T> +typename QStringHash<T>::MutableIterator QStringHash<T>::end() +{ + return MutableIterator(); +} + +template<class T> +typename QStringHash<T>::ConstIterator QStringHash<T>::end() const +{ + return ConstIterator(); +} + +template<class T> +template<class K> +typename QStringHash<T>::MutableIterator QStringHash<T>::find(const K &key) +{ + Node *n = findNode(key); + return n ? MutableIterator(MutableIteratorData(n, this)) : MutableIterator(); +} + +template<class T> +template<class K> +typename QStringHash<T>::ConstIterator QStringHash<T>::find(const K &key) const +{ + Node *n = findNode(key); + return n ? ConstIterator(ConstIteratorData(n, this)) : ConstIterator(); +} + +QT_END_NAMESPACE + +#endif // QSTRINGHASH_P_H diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index ca13ce9211..2e9c6f3de6 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,5 +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 \ @@ -14,14 +23,21 @@ SOURCES += \ $$PWD/qqmlvme.cpp \ $$PWD/qqmlboundsignal.cpp \ $$PWD/qqmlmetatype.cpp \ + $$PWD/qqmlmetatypedata.cpp \ $$PWD/qqmlstringconverters.cpp \ + $$PWD/qqmltype.cpp \ + $$PWD/qqmltypemodule.cpp \ + $$PWD/qqmltypemoduleversion.cpp \ $$PWD/qqmlparserstatus.cpp \ $$PWD/qqmltypeloader.cpp \ $$PWD/qqmlinfo.cpp \ $$PWD/qqmlvaluetype.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ + $$PWD/qqmlmetaobject.cpp \ $$PWD/qqmlnotifier.cpp \ + $$PWD/qqmlobjectorgadget.cpp \ + $$PWD/qqmlstaticmetaobject.cpp \ $$PWD/qqmltypenotavailable.cpp \ $$PWD/qqmltypenamecache.cpp \ $$PWD/qqmlscriptstring.cpp \ @@ -44,13 +60,26 @@ SOURCES += \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ $$PWD/qqmldelayedcallqueue.cpp \ - $$PWD/qqmlloggingcategory.cpp + $$PWD/qqmlloggingcategory.cpp \ + $$PWD/qqmlirloader.cpp \ + $$PWD/qqmlpropertyresolver.cpp \ + $$PWD/qqmltypecompiler.cpp \ + $$PWD/qqmlpropertycachecreator.cpp \ + $$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 \ @@ -68,6 +97,12 @@ HEADERS += \ $$PWD/qqmlexpression_p.h \ $$PWD/qqmlprivate.h \ $$PWD/qqmlmetatype_p.h \ + $$PWD/qqmlmetatypedata_p.h \ + $$PWD/qqmltype_p.h \ + $$PWD/qqmltype_p_p.h \ + $$PWD/qqmltypemodule_p.h \ + $$PWD/qqmltypemodule_p_p.h \ + $$PWD/qqmltypemoduleversion_p.h \ $$PWD/qqmlengine.h \ $$PWD/qqmlcontext.h \ $$PWD/qqmlexpression.h \ @@ -81,9 +116,17 @@ HEADERS += \ $$PWD/qqmldata_p.h \ $$PWD/qqmlvaluetype_p.h \ $$PWD/qqmlcleanup_p.h \ + $$PWD/qqmlenumdata_p.h \ + $$PWD/qqmlenumvalue_p.h \ $$PWD/qqmlpropertycache_p.h \ + $$PWD/qqmlpropertycachemethodarguments_p.h \ + $$PWD/qqmlpropertycachevector_p.h \ + $$PWD/qqmlpropertydata_p.h \ $$PWD/qqmlpropertyindex_p.h \ + $$PWD/qqmlmetaobject_p.h \ $$PWD/qqmlnotifier_p.h \ + $$PWD/qqmlobjectorgadget_p.h \ + $$PWD/qqmlstaticmetaobject_p.h \ $$PWD/qqmltypenotavailable_p.h \ $$PWD/qqmltypenamecache_p.h \ $$PWD/qqmlscriptstring.h \ @@ -110,7 +153,13 @@ HEADERS += \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ $$PWD/qqmldelayedcallqueue_p.h \ - $$PWD/qqmlloggingcategory_p.h + $$PWD/qqmlloggingcategory_p.h \ + $$PWD/qqmlirloader_p.h \ + $$PWD/qqmlpropertyresolver_p.h \ + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qqmlpropertycachecreator_p.h \ + $$PWD/qqmlpropertyvalidator_p.h \ + $$PWD/qqmlsourcecoordinate_p.h qtConfig(qml-xml-http-request) { HEADERS += \ @@ -129,5 +178,15 @@ qtConfig(qml-locale) { $$PWD/qqmllocale.cpp } +qtConfig(qml-network) { + HEADERS += \ + $$PWD/qqmltypeloadernetworkreplyproxy_p.h + + SOURCES += \ + $$PWD/qqmltypeloadernetworkreplyproxy.cpp +} + +android: DEFINES += LIBS_SUFFIX='\\"_$${QT_ARCH}.so\\"' + include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp new file mode 100644 index 0000000000..5b16a3c9e2 --- /dev/null +++ b/src/qml/qml/qqml.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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 "qqml.h" + +#include <QtQml/qqmlprivate.h> + +#include <private/qqmlengine_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmltype_p_p.h> +#include <private/qqmltypemodule_p_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +void qmlClearTypeRegistrations() // Declared in qqml.h +{ + QQmlMetaType::clearTypeRegistrations(); + QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types + qmlClearEnginePlugins(); +} + +//From qqml.h +bool qmlProtectModule(const char *uri, int majVersion) +{ + return QQmlMetaType::protectModule(uri, majVersion); +} + +//From qqml.h +void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) +{ + QQmlMetaType::registerModule(uri, versionMajor, versionMinor); +} + +//From qqml.h +int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName); +} + +// From qqmlprivate.h +QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *) +{ + if (!m_object) { + QQmlError error; + error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine.")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + + if (qeng->thread() != m_object->thread()) { + QQmlError error; + error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + if (alreadyCalled) { + QQmlError error; + error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + alreadyCalled = true; + qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership); + return m_object; +}; + +/* +This method is "over generalized" to allow us to (potentially) register more types of things in +the future without adding exported symbols. +*/ +int QQmlPrivate::qmlregister(RegistrationType type, void *data) +{ + if (type == AutoParentRegistration) { + return QQmlMetaType::registerAutoParentFunction( + *reinterpret_cast<RegisterAutoParent *>(data)); + } else if (type == QmlUnitCacheHookRegistration) { + return QQmlMetaType::registerUnitCacheHook( + *reinterpret_cast<RegisterQmlUnitCacheHook *>(data)); + } + + QQmlType dtype; + if (type == TypeRegistration) + dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)); + else if (type == InterfaceRegistration) + dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)); + else if (type == SingletonRegistration) + dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data)); + else if (type == CompositeRegistration) + dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data)); + else if (type == CompositeSingletonRegistration) + dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data)); + else + return -1; + + if (!dtype.isValid()) + return -1; + + QQmlMetaType::registerUndeletableType(dtype); + return dtype.index(); +} + +void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data) +{ + switch (type) { + case AutoParentRegistration: + QQmlMetaType::unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data)); + break; + case QmlUnitCacheHookRegistration: + QQmlMetaType::removeCachedUnitLookupFunction( + reinterpret_cast<QmlUnitCacheLookupFunction>(data)); + break; + case TypeRegistration: + case InterfaceRegistration: + case SingletonRegistration: + case CompositeRegistration: + case CompositeSingletonRegistration: + QQmlMetaType::unregisterType(data); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 6bd66464b5..b2bf1f328f 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor; void Q_QML_EXPORT qmlClearTypeRegistrations(); template<typename T> -int qmlRegisterType() +int qmlRegisterAnonymousType(const char *uri, int versionMajor) { QML_GETTYPENAMES @@ -115,7 +115,7 @@ int qmlRegisterType() nullptr, QString(), - nullptr, 0, 0, nullptr, &T::staticMetaObject, + uri, versionMajor, 0, nullptr, &T::staticMetaObject, QQmlPrivate::attachedPropertiesFunc<T>(), QQmlPrivate::attachedPropertiesMetaObject<T>(), @@ -133,6 +133,14 @@ int qmlRegisterType() return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); } +#if QT_DEPRECATED_SINCE(5, 14) +template<typename T> +QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType() +{ + return qmlRegisterAnonymousType<T>("", 1); +} +#endif + int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); template<typename T> @@ -579,9 +587,13 @@ namespace QtQml { Q_QML_EXPORT void qmlExecuteDeferred(QObject *); Q_QML_EXPORT QQmlContext *qmlContext(const QObject *); Q_QML_EXPORT QQmlEngine *qmlEngine(const QObject *); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *, const QObject *, - const QMetaObject *, bool create); +#if QT_DEPRECATED_SINCE(5, 14) + Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool") + QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); + Q_QML_EXPORT QT_DEPRECATED_VERSION_X_5_14("Use qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc, bool") + QObject *qmlAttachedPropertiesObject( + int *, const QObject *, const QMetaObject *, bool create); +#endif Q_QML_EXPORT QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *, const QMetaObject *); Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc func, @@ -614,8 +626,6 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create); } -Q_QML_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor); - inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*callback)(QQmlEngine *, QJSEngine *)) { @@ -624,13 +634,13 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi uri, versionMajor, versionMinor, typeName, - callback, nullptr, nullptr, 0, 0 + callback, nullptr, nullptr, 0, 0, {} }; return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } -enum { QmlCurrentSingletonTypeRegistrationVersion = 2 }; +enum { QmlCurrentSingletonTypeRegistrationVersion = 3 }; template <typename T> inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*callback)(QQmlEngine *, QJSEngine *)) @@ -642,12 +652,40 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi uri, versionMajor, versionMinor, typeName, - nullptr, callback, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0 + nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback }; return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } +template <typename T, typename F, typename std::enable_if<std::is_convertible<F, std::function<QObject *(QQmlEngine *, QJSEngine *)>>::value + && !std::is_convertible<F, QObject *(*)(QQmlEngine *, QJSEngine *)>::value, void>::type* = nullptr> +inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, + F&& callback) +{ + + QML_GETTYPENAMES + + QQmlPrivate::RegisterSingletonType api = { + QmlCurrentSingletonTypeRegistrationVersion, + + uri, versionMajor, versionMinor, typeName, + + nullptr, nullptr, &T::staticMetaObject, qRegisterNormalizedMetaType<T *>(pointerName.constData()), 0, callback + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); +} + +template<typename T> +inline auto qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, + const char *typeName, T *cppObject) -> typename std::enable_if<std::is_base_of<QObject, T>::value, int>::type +{ + QQmlPrivate::RegisterSingletonFunctor registrationFunctor; + registrationFunctor.m_object = cppObject; + return qmlRegisterSingletonType<T>(uri, versionMajor, versionMinor, typeName, registrationFunctor); +} + inline int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) { if (url.isRelative()) { diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index facd79d211..adb036e2d0 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> @@ -62,9 +63,6 @@ void QQmlApplicationEnginePrivate::cleanUp() obj->disconnect(q); qDeleteAll(objects); -#if QT_CONFIG(translation) - qDeleteAll(translators); -#endif } void QQmlApplicationEnginePrivate::init() @@ -75,10 +73,11 @@ void QQmlApplicationEnginePrivate::init() q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit, Qt::QueuedConnection); #if QT_CONFIG(translation) - QTranslator* qtTranslator = new QTranslator; + QTranslator* qtTranslator = new QTranslator(q); if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm"))) QCoreApplication::installTranslator(qtTranslator); - translators << qtTranslator; + else + delete qtTranslator; #endif new QQmlFileSelector(q,q); QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true)); @@ -92,13 +91,12 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile)); - QTranslator *translator = new QTranslator; - if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) { + Q_Q(QQmlApplicationEngine); + QTranslator *translator = new QTranslator(q); + if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) QCoreApplication::installTranslator(translator); - translators << translator; - } else { + else delete translator; - } #else Q_UNUSED(rootFile) #endif @@ -133,7 +131,7 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) q->objectCreated(nullptr, c->url()); break; case QQmlComponent::Ready: { - auto newObj = c->create(); + auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties); objects << newObj; QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); }); q->objectCreated(objects.constLast(), c->url()); @@ -281,6 +279,22 @@ void QQmlApplicationEngine::load(const QString &filePath) } /*! + Sets the \a initialProperties with which the QML component gets initialized after + it gets loaded. + + + \sa QQmlComponent::setInitialProperties + \sa QQmlApplicationEngine::load + \sa QQmlApplicationEngine::loadData + \since 5.14 +*/ +void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties) +{ + Q_D(QQmlApplicationEngine); + d->initialProperties = initialProperties; +} + +/*! Loads the QML given in \a data. The object tree defined by \a data is instantiated immediately. diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index bb5d6b5d68..2b4de91154 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -66,6 +66,7 @@ public: public Q_SLOTS: void load(const QUrl &url); void load(const QString &filePath); + void setInitialProperties(const QVariantMap &initialProperties); void loadData(const QByteArray &data, const QUrl &url = QUrl()); Q_SIGNALS: diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 6cf6828832..1279e400e8 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE -class QTranslator; class QFileSelector; class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate { @@ -74,10 +73,7 @@ public: void loadTranslations(const QUrl &rootFile); void finishLoad(QQmlComponent *component); QList<QObject *> objects; - -#if QT_CONFIG(translation) - QList<QTranslator *> translators; -#endif + QVariantMap initialProperties; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b164517011..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> @@ -335,7 +336,7 @@ protected: class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> { public: - QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) + QQmlTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) { setCompilationUnit(compilationUnit); m_binding = binding; @@ -356,7 +357,7 @@ public: if (!isAddedToObject() || hasError()) return; - const QString result = m_binding->valueAsString(m_compilationUnit.data()); + const QString result = m_compilationUnit->bindingValueAsString(m_binding); Q_ASSERT(targetObject()); @@ -378,7 +379,7 @@ private: const QV4::CompiledData::Binding *m_binding; }; -QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) +QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) { QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding); @@ -517,9 +518,9 @@ QString QQmlBinding::expressionIdentifier() const { if (auto f = function()) { QString url = f->sourceFile(); - quint16 lineNumber = f->compiledFunction->location.line; - quint16 columnNumber = f->compiledFunction->location.column; - return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); + uint lineNumber = f->compiledFunction->location.line; + uint columnNumber = f->compiledFunction->location.column; + return url + QString::asprintf(":%u:%u", lineNumber, columnNumber); } return QStringLiteral("[native code]"); diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index f192de4342..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 @@ -79,7 +80,7 @@ public: const QString &url = QString(), quint16 lineNumber = 0); static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function, QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope); - static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, + static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt); ~QQmlBinding() override; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index e5b78591e0..ff01e737ca 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -44,7 +44,6 @@ #include "qqmlengine_p.h" #include "qqmlexpression_p.h" #include "qqmlcontext_p.h" -#include "qqmlmetatype_p.h" #include "qqml.h" #include "qqmlcontext.h" #include "qqmlglobal_p.h" @@ -57,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> @@ -210,8 +210,6 @@ void QQmlBoundSignalExpression::evaluate(void **a) } else if (type == QMetaType::Int) { //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise jsCall->args[ii] = QV4::Value::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); } else if (ep->isQObject(type)) { if (!*reinterpret_cast<void* const *>(a[ii + 1])) jsCall->args[ii] = QV4::Value::nullValue(); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 7ad9310160..b26b90d2aa 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -51,8 +51,6 @@ #include "qqmlincubator_p.h" #include <private/qqmljavascriptexpression_p.h> -#include <private/qv8engine_p.h> - #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> @@ -66,7 +64,6 @@ #include <QThreadStorage> #include <QtCore/qdebug.h> #include <qqmlinfo.h> -#include "qqmlmemoryprofiler_p.h" namespace { QThreadStorage<int> creationDepth; @@ -74,7 +71,7 @@ namespace { QT_BEGIN_NAMESPACE -class QQmlComponentExtension : public QV8Engine::Deletable +class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable { public: QQmlComponentExtension(QV4::ExecutionEngine *v4); @@ -353,6 +350,32 @@ void QQmlComponentPrivate::clear() compilationUnit = nullptr; } +QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context) +{ + if (!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 = engine->rootContext(); + return q->beginCreate(context); +} + +bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value) +{ + QQmlProperty prop(component, name); + auto privProp = QQmlPropertyPrivate::get(prop); + if (!prop.isValid() || !privProp->writeValueProperty(value, nullptr)) { + QQmlError error{}; + error.setUrl(url); + error.setDescription(QLatin1String("Could not set property %1").arg(name)); + state.errors.push_back(error); + return false; + } else + return true; +} + /*! \internal */ @@ -552,7 +575,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, /*! \internal */ -QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent) +QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit, + int start, QObject *parent) : QQmlComponent(engine, parent) { Q_D(QQmlComponent); @@ -781,20 +805,30 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent) QObject *QQmlComponent::create(QQmlContext *context) { Q_D(QQmlComponent); - QML_MEMORY_SCOPE_URL(url()); - 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; - } + QObject *rv = d->doBeginCreate(this, context); + if (rv) + completeCreate(); + return rv; +} - if (!context) - context = d->engine->rootContext(); +/*! + Create an object instance of this component, and initialize its toplevel + properties with \a initialProperties. \a context specifies the context + where the object instance is to be created. - QObject *rv = beginCreate(context); - if (rv) + \sa QQmlComponent::create + \since 5.14 +*/ +QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context) +{ + Q_D(QQmlComponent); + + QObject *rv = d->doBeginCreate(this, context); + if (rv) { + setInitialProperties(rv, initialProperties); completeCreate(); + } return rv; } @@ -1070,6 +1104,26 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, enginePriv->incubate(incubator, forContextData); } +/*! + Set toplevel \a properties of the \a component. + + + This method provides advanced control over component instance creation. + In general, programmers should use + \l QQmlComponent::createWithInitialProperties to create a component. + + Use this method after beginCreate and before completeCreate has been called. + If a provided property does not exist, a warning is issued. + + \since 5.14 +*/ +void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties) +{ + Q_D(QQmlComponent); + for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) + d->setInitialProperty(component, it.key(), it.value()); +} + /* This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData arguments instead of QQmlContext which means we don't have to construct the rather weighty diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 20199d0b21..f259c99b08 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -59,9 +59,7 @@ class QQmlComponentPrivate; class QQmlComponentAttached; namespace QV4 { -namespace CompiledData { -struct CompilationUnit; -} +class ExecutableCompilationUnit; } class Q_QML_EXPORT QQmlComponent : public QObject @@ -102,6 +100,8 @@ public: QUrl url() const; virtual QObject *create(QQmlContext *context = nullptr); + QObject *createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context = nullptr); + void setInitialProperties(QObject *component, const QVariantMap &properties); virtual QObject *beginCreate(QQmlContext *); virtual void completeCreate(); @@ -128,7 +128,8 @@ protected: Q_INVOKABLE void incubateObject(QQmlV4Function *); private: - QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent); + QQmlComponent(QQmlEngine *, QV4::ExecutableCompilationUnit *compilationUnit, int, + QObject *parent); Q_DISABLE_COPY(QQmlComponent) friend class QQmlTypeData; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 4d9e4c6c15..2170646b89 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> @@ -105,7 +106,7 @@ public: qreal progress; int start; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; struct ConstructionState { ConstructionState() @@ -142,6 +143,9 @@ public: static QQmlComponentPrivate *get(QQmlComponent *c) { return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(c)); } + + QObject *doBeginCreate(QQmlComponent *q, QQmlContext *context); + bool setInitialProperty(QObject *component, const QString &name, const QVariant& value); }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 3710cee162..af8deb085c 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -317,6 +317,12 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value) d->propertyValues[idx] = value; QMetaObject::activate(this, d->notifyIndex, idx, nullptr); } + + if (auto *obj = qvariant_cast<QObject *>(value)) { + connect(obj, &QObject::destroyed, this, [d, name](QObject *destroyed) { + d->dropDestroyedQObject(name, destroyed); + }); + } } /*! @@ -352,7 +358,7 @@ void QQmlContext::setContextProperties(const QVector<PropertyPair> &properties) data->expressions = nullptr; data->childContexts = nullptr; - for (auto property : properties) + for (const auto &property : properties) setContextProperty(property.name, property.value); data->expressions = expressions; @@ -438,23 +444,20 @@ QUrl QQmlContext::resolvedUrl(const QUrl &src) QUrl QQmlContextData::resolvedUrl(const QUrl &src) { - QQmlContextData *ctxt = this; - QUrl resolved; if (src.isRelative() && !src.isEmpty()) { - if (ctxt) { - while(ctxt) { - if (ctxt->url().isValid()) - break; - else - ctxt = ctxt->parent; - } - - if (ctxt) - resolved = ctxt->url().resolved(src); - else if (engine) - resolved = engine->baseUrl().resolved(src); - } + QQmlContextData *ctxt = this; + do { + if (ctxt->url().isValid()) + break; + else + ctxt = ctxt->parent; + } while (ctxt); + + if (ctxt) + resolved = ctxt->url().resolved(src); + else if (engine) + resolved = engine->baseUrl().resolved(src); } else { resolved = src; } @@ -527,6 +530,17 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind } } +void QQmlContextPrivate::dropDestroyedQObject(const QString &name, QObject *destroyed) +{ + const int idx = data->propertyNames().value(name); + Q_ASSERT(idx >= 0); + if (qvariant_cast<QObject *>(propertyValues[idx]) != destroyed) + return; + + propertyValues[idx] = QVariant::fromValue<QObject *>(nullptr); + QMetaObject::activate(q_func(), notifyIndex, idx, nullptr); +} + QQmlContextData::QQmlContextData() : QQmlContextData(nullptr) @@ -845,7 +859,7 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() return QQmlContextPrivate::get(asQQmlContext()); } -void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex) +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) { typeCompilationUnit = unit; componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index 7e3cef8e1d..5f7316b00c 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -66,7 +66,7 @@ #include <private/qflagpointer_p.h> #include <private/qqmlguard_p.h> -#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <private/qv4identifier_p.h> QT_BEGIN_NAMESPACE @@ -104,6 +104,8 @@ public: static int context_count(QQmlListProperty<QObject> *); static QObject *context_at(QQmlListProperty<QObject> *, int); + + void dropDestroyedQObject(const QString &name, QObject *destroyed); }; class QQmlComponentAttached; @@ -152,12 +154,12 @@ public: QQmlIncubatorPrivate *incubator; // Compilation unit for contexts that belong to a compiled type. - QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit; // object index in CompiledData::Unit to component that created this context int componentObjectIndex; - void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex); + void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex); // flag indicates whether the context owns the cache (after mutation) or not. mutable QV4::IdentifierHash propertyNameCache; diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 5cf87f5264..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> @@ -100,7 +100,12 @@ void QQmlCustomParser::clearErrors() */ void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description) { - exceptions << QQmlCompileError(location, description); + QQmlJS::DiagnosticMessage error; + error.line = location.line; + error.column = location.column; + error.message = description; + + exceptions << error; } struct StaticQtMetaObject : public QObject diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index bf28bca447..df8cbc9072 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -51,10 +51,9 @@ // We mean it. // -#include "qqmlmetatype_p.h" #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> @@ -81,10 +80,10 @@ public: void clearErrors(); Flags flags() const { return m_flags; } - virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; - virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0; - QVector<QQmlCompileError> errors() const { return exceptions; } + QVector<QQmlJS::DiagnosticMessage> errors() const { return exceptions; } protected: void error(const QV4::CompiledData::Binding *binding, const QString& description) @@ -98,7 +97,7 @@ protected: const QMetaObject *resolveType(const QString&) const; private: - QVector<QQmlCompileError> exceptions; + QVector<QQmlJS::DiagnosticMessage> exceptions; QQmlEnginePrivate *engine; const QQmlPropertyValidator *validator; Flags m_flags; diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index f4c03fc17c..299476f5c8 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -76,8 +76,8 @@ class QQmlDataExtended; class QQmlNotifierEndpoint; namespace QV4 { +class ExecutableCompilationUnit; namespace CompiledData { -struct CompilationUnit; struct Binding; } } @@ -226,14 +226,14 @@ public: ~DeferredData(); unsigned int deferredIdx; QMultiHash<int, const QV4::CompiledData::Binding *> bindings; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;//Not always the same as the other compilation unit + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext Q_DISABLE_COPY(DeferredData); }; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QVector<DeferredData *> deferredData; - void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *); + void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, QQmlContextData *); void releaseDeferredData(); QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp new file mode 100644 index 0000000000..750fc6de50 --- /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 (const 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..0450e94c02 --- /dev/null +++ b/src/qml/qml/qqmldatablob_p.h @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** 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. +protected: + QList<QQmlDataBlob *> m_waitingOnMe; +private: + + // 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 61cb0a9065..02fde97b3d 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -38,12 +38,12 @@ ****************************************************************************/ #include "qqmldelayedcallqueue_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #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..de74dfdf9b --- /dev/null +++ b/src/qml/qml/qqmldirdata.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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; +} + +QQmlTypeLoader::Blob::PendingImportPtr QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const +{ + auto it = m_imports.find(blob); + if (it == m_imports.end()) + return nullptr; + return *it; +} + +void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, QQmlTypeLoader::Blob::PendingImportPtr import) +{ + m_imports[blob] = std::move(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..34f1ff1678 --- /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; + + PendingImportPtr import(QQmlTypeLoader::Blob *) const; + void setImport(QQmlTypeLoader::Blob *, PendingImportPtr); + + 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 *, QQmlTypeLoader::Blob::PendingImportPtr> 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 4c6a1b69d8..0fd07ea209 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> @@ -86,19 +87,7 @@ #if QT_CONFIG(qml_animation) #include <private/qqmltimer_p.h> #endif -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#endif #include <private/qqmlplatform_p.h> -#include <private/qquickpackage_p.h> -#if QT_CONFIG(qml_delegate_model) -#include <private/qqmldelegatemodel_p.h> -#endif -#include <private/qqmlobjectmodel_p.h> -#if QT_CONFIG(qml_worker_script) -#include <private/qquickworkerscript_p.h> -#endif -#include <private/qqmlinstantiator_p.h> #include <private/qqmlloggingcategory_p.h> #ifdef Q_OS_WIN // for %APPDATA% @@ -116,13 +105,6 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE -void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) -{ - QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); - QQmlEnginePrivate::registerQtQuick2Types(uri, versionMajor, versionMinor); - QQmlValueTypeFactory::registerValueTypes(uri, versionMajor, versionMinor); -} - // Declared in qqml.h int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, @@ -215,63 +197,56 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, bool QQmlEnginePrivate::qml_debugging_enabled = false; bool QQmlEnginePrivate::s_designerMode = false; -// these types are part of the QML language -void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int versionMinor) +void QQmlEnginePrivate::defineModule() { - qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component"); - qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject"); - qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding"); - qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 - qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser); - if (!strcmp(uri, "QtQuick")) - qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7 - else - qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3 -#if QT_CONFIG(qml_animation) - qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); -#endif - qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 - qmlRegisterType<QQmlInstanceModel>(); + const char uri[] = "QtQml"; - qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8 - qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12 -} + qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component"); + qmlRegisterType<QObject>(uri, 2, 0, "QtObject"); + qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding"); + qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); // Only available in >= 2.8 + qmlRegisterType<QQmlBind, 14>(uri, 2, 14, "Binding"); + // TODO: We won't need Connections to be a custom type anymore once we can drop the + // automatic signal handler inference from undeclared properties. + qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser); + qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3 -// These QtQuick types' implementation resides in the QtQml module -void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) -{ -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility - qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility -#endif -#if QT_CONFIG(qml_worker_script) - qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript"); +#if QT_CONFIG(qml_animation) + qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer"); #endif - qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package"); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, versionMajor, versionMinor, "VisualDataGroup"); + + qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8 + qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12 + +#if QT_CONFIG(qml_locale) + qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); #endif - qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel"); -#endif // < Qt 6 } -void QQmlEnginePrivate::defineQtQuick2Module() +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +void QQmlEnginePrivate::registerQuickTypes() { - // register the base types into the QtQuick namespace - registerBaseTypes("QtQuick",2,0); + // Don't add anything here. These are only for backwards compatibility. - // register the QtQuick2 types which are implemented in the QtQml module. - registerQtQuick2Types("QtQuick",2,0); + const char uri[] = "QtQuick"; + + qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component"); + qmlRegisterType<QObject>(uri, 2, 0, "QtObject"); + qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding"); + qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); + qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser); + qmlRegisterCustomType<QQmlConnections, 3>(uri, 2, 7, "Connections", new QQmlConnectionsParser); +#if QT_CONFIG(qml_animation) + qmlRegisterType<QQmlTimer>(uri, 2, 0,"Timer"); +#endif + qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); + qmlRegisterType<QQmlLoggingCategory, 12>(uri, 2, 12, "LoggingCategory"); #if QT_CONFIG(qml_locale) - qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); #endif - - // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward - qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); } +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) bool QQmlEnginePrivate::designerMode() { @@ -841,19 +816,20 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in // marshalled back onto the QObject's thread and handled by QML from there. This is tested // by the qqmlecmascript::threadSignal() autotest. if (ddata->notifyList && - QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.load()) { + QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.loadRelaxed()) { - if (!QObjectPrivate::get(object)->threadData->thread) + if (!QObjectPrivate::get(object)->threadData->thread.loadAcquire()) return; QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index); QList<QByteArray> parameterTypes = m.parameterTypes(); - int *types = (int *)malloc((parameterTypes.count() + 1) * sizeof(int)); - void **args = (void **) malloc((parameterTypes.count() + 1) *sizeof(void *)); + QScopedPointer<QMetaCallEvent> ev(new QMetaCallEvent(m.methodIndex(), 0, nullptr, + object, index, + parameterTypes.count() + 1)); - types[0] = 0; // return type - args[0] = nullptr; // return value + void **args = ev->args(); + int *types = ev->types(); for (int ii = 0; ii < parameterTypes.count(); ++ii) { const QByteArray &typeName = parameterTypes.at(ii); @@ -866,21 +842,16 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" "(Make sure '%s' is registered using qRegisterMetaType().)", typeName.constData(), typeName.constData()); - free(types); - free(args); return; } args[ii + 1] = QMetaType::create(types[ii + 1], a[ii + 1]); } - QMetaCallEvent *ev = new QMetaCallEvent(m.methodIndex(), 0, nullptr, object, index, - parameterTypes.count() + 1, types, args); - QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject; mpo->target = object; - mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread); - QCoreApplication::postEvent(mpo, ev); + mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread.loadAcquire()); + QCoreApplication::postEvent(mpo, ev.take()); } else { QQmlNotifierEndpoint *ep = ddata->notify(index); @@ -975,14 +946,10 @@ void QQmlEnginePrivate::init() Q_Q(QQmlEngine); if (baseModulesUninitialized) { - qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler. - registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. -#if QT_CONFIG(qml_locale) - qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); -#endif - // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward - qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); + // required for the Compiler. + qmlRegisterType<QObject>("QML", 1, 0, "QtObject"); + qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); QQmlData::init(); baseModulesUninitialized = false; @@ -994,24 +961,13 @@ void QQmlEnginePrivate::init() qRegisterMetaType<QQmlComponent::Status>(); qRegisterMetaType<QList<QObject*> >(); qRegisterMetaType<QList<int> >(); - qRegisterMetaType<QQmlV4Handle>(); qRegisterMetaType<QQmlBinding*>(); - v8engine()->setEngine(q); + q->handle()->setQmlEngine(q); rootContext = new QQmlContext(q,true); } -#if QT_CONFIG(qml_worker_script) -QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() -{ - Q_Q(QQmlEngine); - if (!workerScriptEngine) - workerScriptEngine = new QQuickWorkerScriptEngine(q); - return workerScriptEngine; -} -#endif - /*! \class QQmlEngine \since 5.0 @@ -1091,7 +1047,7 @@ QQmlEngine::~QQmlEngine() // XXX TODO: performance -- store list of singleton types separately? QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes(); for (const QQmlType &currType : singletonTypes) - currType.singletonInstanceInfo()->destroy(this); + d->destroySingletonInstance(currType); delete d->rootContext; d->rootContext = nullptr; @@ -1214,6 +1170,13 @@ void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) } } +QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const +{ + const QString providerIdLower = providerId.toLower(); + QMutexLocker locker(&mutex); + return imageProviders.value(providerIdLower); +} + #if QT_CONFIG(qml_network) /*! Sets the \a factory to use for creating QNetworkAccessManager(s). @@ -1303,8 +1266,10 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider) { Q_D(QQmlEngine); + QString providerIdLower = providerId.toLower(); + QSharedPointer<QQmlImageProviderBase> sp(provider); QMutexLocker locker(&d->mutex); - d->imageProviders.insert(providerId.toLower(), QSharedPointer<QQmlImageProviderBase>(provider)); + d->imageProviders.insert(std::move(providerIdLower), std::move(sp)); } /*! @@ -1315,8 +1280,9 @@ void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBa QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const { Q_D(const QQmlEngine); + const QString providerIdLower = providerId.toLower(); QMutexLocker locker(&d->mutex); - return d->imageProviders.value(providerId.toLower()).data(); + return d->imageProviders.value(providerIdLower).data(); } /*! @@ -1327,8 +1293,9 @@ QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) cons void QQmlEngine::removeImageProvider(const QString &providerId) { Q_D(QQmlEngine); + const QString providerIdLower = providerId.toLower(); QMutexLocker locker(&d->mutex); - d->imageProviders.take(providerId.toLower()); + d->imageProviders.take(providerIdLower); } /*! @@ -1436,23 +1403,13 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) template<> QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) { + Q_D(QQmlEngine); QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType); if (!type.isValid() || !type.isSingleton()) return QJSValue(); - QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo(); - info->init(this); - - if (QObject* o = info->qobjectApi(this)) - return this->newQObject(o); - else { - QJSValue value = info->scriptApi(this); - if (!value.isUndefined()) - return value; - } - - return QJSValue(); + return d->singletonInstance<QJSValue>(type); } /*! @@ -1669,6 +1626,7 @@ static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlDat return rv; } +#if QT_DEPRECATED_SINCE(5, 14) QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) { QQmlData *data = QQmlData::get(object, create); @@ -1696,6 +1654,7 @@ QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, return qmlAttachedPropertiesObjectById(*idCache, object, create); } +#endif QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object, const QMetaObject *attachedMetaObject) @@ -1824,7 +1783,7 @@ void QQmlData::NotifyList::layout() todo = nullptr; } -void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context) +void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *context) { QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = objectIndex; @@ -1832,7 +1791,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData 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) { @@ -2157,20 +2116,21 @@ 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.loc.startLine, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message)); continue; } QQmlError error; error.setUrl(QUrl(fileName)); error.setDescription(m.message); - error.setLine(m.loc.startLine); - error.setColumn(m.loc.startColumn); + error.setLine(m.line); + error.setColumn(m.column); errors << error; } return errors; @@ -2298,6 +2258,7 @@ void QQmlEngine::setPluginPathList(const QStringList &paths) d->importDatabase.setPluginPathList(paths); } +#if QT_CONFIG(library) /*! Imports the plugin named \a filePath with the \a uri provided. Returns true if the plugin was successfully imported; otherwise returns false. @@ -2311,6 +2272,7 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList Q_D(QQmlEngine); return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), -1, errors); } +#endif /*! \property QQmlEngine::offlineStoragePath @@ -2365,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); @@ -2461,7 +2434,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe } } -void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { compilationUnit->isRegisteredWithEngine = true; @@ -2471,7 +2444,7 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::Compila m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit); } -void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { compilationUnit->isRegisteredWithEngine = false; @@ -2479,6 +2452,70 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::Compi m_compositeTypes.remove(compilationUnit->metaTypeId); } +template<> +QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type) +{ + Q_Q(QQmlEngine); + + QJSValue value = singletonInstances.value(type); + if (!value.isUndefined()) { + return value; + } + + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); + Q_ASSERT(siinfo != nullptr); + + if (siinfo->scriptCallback) { + value = siinfo->scriptCallback(q, q); + if (value.isQObject()) { + QObject *o = value.toQObject(); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + } + singletonInstances.insert(type, value); + + } else if (siinfo->qobjectCallback) { + QObject *o = siinfo->qobjectCallback(q, q); + if (!o) { + QQmlError error; + error.setMessageType(QtMsgType::QtCriticalMsg); + error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", + qPrintable(QString::fromUtf8(type.typeName())))); + warning(error); + } else { + // if this object can use a property cache, create it now + QQmlData::ensurePropertyCache(q, o); + } + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + value = q->newQObject(o); + singletonInstances.insert(type, value); + } else if (!siinfo->url.isEmpty()) { + QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous); + QObject *o = component.beginCreate(q->rootContext()); + value = q->newQObject(o); + singletonInstances.insert(type, value); + component.completeCreate(); + } + + return value; +} + +void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type) +{ + Q_ASSERT(type.isSingleton() || type.isCompositeSingleton()); + + QObject* o = singletonInstances.take(type).toQObject(); + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (type.singletonInstanceInfo()->url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) + return; + delete o; + } +} + bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const { return typeLoader.isTypeLoaded(url); diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 871e6bd9b4..31fe3a1849 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -114,7 +114,9 @@ public: bool addNamedBundle(const QString &name, const QString &fileName); +#if QT_CONFIG(library) bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); +#endif #if QT_CONFIG(qml_network) void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *); @@ -175,12 +177,7 @@ Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId); template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) { - QJSValue instance = singletonInstance<QJSValue>(qmlTypeId); - if (!instance.isQObject()) - return nullptr; - - QObject *object = instance.toQObject(); - return qobject_cast<T>(object); + return qobject_cast<T>(singletonInstance<QJSValue>(qmlTypeId).toQObject()); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 92e56cd957..385ae02ce5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -62,11 +62,11 @@ #include "qqmlcontext_p.h" #include "qqmlexpression.h" #include "qqmlproperty_p.h" -#include "qqmlpropertycache_p.h" #include "qqmlmetatype_p.h" #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> @@ -77,7 +77,6 @@ #include <private/qobject_p.h> -#include <private/qv8engine_p.h> #include <private/qjsengine_p.h> #include <private/qqmldirparser_p.h> @@ -95,12 +94,12 @@ class QQmlTypeNameCache; class QQmlComponentAttached; class QQmlCleanup; class QQmlDelayedError; -class QQuickWorkerScriptEngine; class QQmlObjectCreator; class QDir; class QQmlIncubator; class QQmlProfiler; class QQmlPropertyCapture; +class QQmlMetaObject; // This needs to be declared here so that the pool for it can live in QQmlEnginePrivate. // The inline method definitions are in qqmljavascriptexpression_p.h @@ -150,12 +149,10 @@ public: QQmlDelayedError *erroredBindings; int inProgressCreations; - QV8Engine *v8engine() const { return q_func()->handle()->v8Engine; } QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); } #if QT_CONFIG(qml_worker_script) - QQuickWorkerScriptEngine *getWorkerScriptEngine(); - QQuickWorkerScriptEngine *workerScriptEngine; + QThread *workerScriptEngine; #endif QUrl baseUrl; @@ -171,6 +168,8 @@ public: mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory; #endif QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders; + QSharedPointer<QQmlImageProviderBase> imageProvider(const QString &providerId) const; + QQmlAbstractUrlInterceptor* urlInterceptor; @@ -223,12 +222,16 @@ public: QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1); - void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); - void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); + void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; + template <typename T> + T singletonInstance(const QQmlType &type); + void destroySingletonInstance(const QQmlType &type); + void sendQuit(); void sendExit(int retCode = 0); void warning(const QQmlError &); @@ -238,7 +241,6 @@ public: static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList<QQmlError> &); - inline static QV8Engine *getV8Engine(QQmlEngine *e); inline static QV4::ExecutionEngine *getV4Engine(QQmlEngine *e); inline static QQmlEnginePrivate *get(QQmlEngine *e); inline static const QQmlEnginePrivate *get(const QQmlEngine *e); @@ -249,9 +251,10 @@ public: static QList<QQmlError> qmlErrorFromDiagnostics(const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages); - static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); - static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor); - static void defineQtQuick2Module(); + static void defineModule(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + static void registerQuickTypes(); +#endif static bool designerMode(); static void activateDesignerMode(); @@ -261,9 +264,11 @@ public: mutable QMutex networkAccessManagerMutex; private: + QHash<QQmlType, QJSValue> singletonInstances; + // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. - QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes; + QHash<int, QV4::ExecutableCompilationUnit *> m_compositeTypes; static bool s_designerMode; // These members is protected by the full QQmlEnginePrivate::mutex mutex @@ -381,13 +386,6 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(const QQmlType &type, int minorVersi return QQmlMetaType::propertyCache(type, minorVersion); } -QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e) -{ - Q_ASSERT(e); - - return e->handle()->v8Engine; -} - QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e) { Q_ASSERT(e); @@ -434,6 +432,14 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) return get(qmlEngine); } +template<> +Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type); + +template<typename T> +T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { + return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject()); +} + QT_END_NAMESPACE #endif // QQMLENGINE_P_H diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/qml/qqmlenumdata_p.h index 939ecc1500..df99c2c1bc 100644 --- a/src/qml/types/qqmlmodelsmodule_p.h +++ b/src/qml/qml/qqmlenumdata_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Research In Motion. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLMODELSMODULE_H -#define QQMLMODELSMODULE_H +#ifndef QQMLENUMDATA_P_H +#define QQMLENUMDATA_P_H // // W A R N I N G @@ -51,17 +51,16 @@ // We mean it. // -#include <private/qtqmlglobal_p.h> +#include <private/qqmlenumvalue_p.h> QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QQmlModelsModule +struct QQmlEnumData { -public: - static void defineModule(); - static void defineLabsModule(); + QString name; + QVector<QQmlEnumValue> values; }; QT_END_NAMESPACE -#endif +#endif // QQMLENUMDATA_P_H diff --git a/src/qml/qml/qqmlenumvalue_p.h b/src/qml/qml/qqmlenumvalue_p.h new file mode 100644 index 0000000000..ea0fc244cb --- /dev/null +++ b/src/qml/qml/qqmlenumvalue_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 QQMLENUMVALUE_P_H +#define QQMLENUMVALUE_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/qstring.h> + +QT_BEGIN_NAMESPACE + +struct QQmlEnumValue +{ + QQmlEnumValue() {} + QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {} + QString namedValue; + int value = -1; +}; + +QT_END_NAMESPACE + +#endif // QQMLENUMVALUE_P_H diff --git a/src/qml/qmldirparser/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp index 5e181f7e27..0b94ed3b49 100644 --- a/src/qml/qmldirparser/qqmlerror.cpp +++ b/src/qml/qml/qqmlerror.cpp @@ -38,17 +38,17 @@ ****************************************************************************/ #include "qqmlerror.h" +#include "qqmlfile.h" #include "qqmlsourcecoordinate_p.h" +#include <private/qqmljsdiagnosticmessage_p.h> #include <QtCore/qdebug.h> #include <QtCore/qfile.h> #include <QtCore/qstringlist.h> #include <QtCore/qvector.h> -#ifndef QT_NO_QOBJECT #include <QtCore/qobject.h> #include <QtCore/qpointer.h> -#endif QT_BEGIN_NAMESPACE @@ -77,26 +77,14 @@ QT_BEGIN_NAMESPACE \sa QQuickView::errors(), QQmlComponent::errors() */ -class QQmlErrorPrivate +class QQmlErrorPrivate : public QQmlJS::DiagnosticMessage { public: - QQmlErrorPrivate(); - + QQmlErrorPrivate() { type = QtWarningMsg; } QUrl url; - QString description; - quint16 line; - quint16 column; - QtMsgType messageType; -#ifndef QT_NO_QOBJECT QPointer<QObject> object; -#endif }; -QQmlErrorPrivate::QQmlErrorPrivate() -: line(0), column(0), messageType(QtMsgType::QtWarningMsg) -{ -} - /*! Creates an empty error object. */ @@ -126,13 +114,11 @@ QQmlError &QQmlError::operator=(const QQmlError &other) if (!d) d = new QQmlErrorPrivate; d->url = other.d->url; - d->description = other.d->description; + d->message = other.d->message; d->line = other.d->line; d->column = other.d->column; -#ifndef QT_NO_QOBJECT d->object = other.d->object; -#endif - d->messageType = other.d->messageType; + d->type = other.d->type; } return *this; } @@ -179,7 +165,7 @@ void QQmlError::setUrl(const QUrl &url) QString QQmlError::description() const { if (d) - return d->description; + return d->message; return QString(); } @@ -190,7 +176,7 @@ void QQmlError::setDescription(const QString &description) { if (!d) d = new QQmlErrorPrivate; - d->description = description; + d->message = description; } /*! @@ -199,7 +185,7 @@ void QQmlError::setDescription(const QString &description) int QQmlError::line() const { if (d) - return qmlSourceCoordinate(d->line); + return qmlConvertSourceCoordinate<quint32, int>(d->line); return -1; } @@ -210,7 +196,7 @@ void QQmlError::setLine(int line) { if (!d) d = new QQmlErrorPrivate; - d->line = qmlSourceCoordinate(line); + d->line = qmlConvertSourceCoordinate<int, quint32>(line); } /*! @@ -219,7 +205,7 @@ void QQmlError::setLine(int line) int QQmlError::column() const { if (d) - return qmlSourceCoordinate(d->column); + return qmlConvertSourceCoordinate<quint32, int>(d->column); return -1; } @@ -230,10 +216,9 @@ void QQmlError::setColumn(int column) { if (!d) d = new QQmlErrorPrivate; - d->column = qmlSourceCoordinate(column); + d->column = qmlConvertSourceCoordinate<int, quint32>(column); } -#ifndef QT_NO_QOBJECT /*! Returns the nearest object where this error occurred. Exceptions in bound property expressions set this to the object @@ -256,7 +241,6 @@ void QQmlError::setObject(QObject *object) d = new QQmlErrorPrivate; d->object = object; } -#endif // QT_NO_QOBJECT /*! \since 5.9 @@ -266,7 +250,7 @@ void QQmlError::setObject(QObject *object) QtMsgType QQmlError::messageType() const { if (d) - return d->messageType; + return d->type; return QtMsgType::QtWarningMsg; } @@ -280,7 +264,7 @@ void QQmlError::setMessageType(QtMsgType messageType) { if (!d) d = new QQmlErrorPrivate; - d->messageType = messageType; + d->type = messageType; } /*! @@ -324,8 +308,8 @@ QDebug operator<<(QDebug debug, const QQmlError &error) QUrl url = error.url(); - if (error.line() > 0 && url.scheme() == QLatin1String("file")) { - QString file = url.toLocalFile(); + if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) { + QString file = QQmlFile::urlToLocalFileOrQrc(url); QFile f(file); if (f.open(QIODevice::ReadOnly)) { QByteArray data = f.readAll(); diff --git a/src/qml/qmldirparser/qqmlerror.h b/src/qml/qml/qqmlerror.h index f4221358e9..8ea493c11d 100644 --- a/src/qml/qmldirparser/qqmlerror.h +++ b/src/qml/qml/qqmlerror.h @@ -69,10 +69,8 @@ public: int column() const; void setColumn(int); -#ifndef QT_NO_QOBJECT QObject *object() const; void setObject(QObject *); -#endif QtMsgType messageType() const; void setMessageType(QtMsgType messageType); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index ac2629979f..f6a5afb891 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -45,8 +45,8 @@ #include "qqmlcontext_p.h" #include "qqmlscriptstring_p.h" #include "qqmlbinding_p.h" -#include <private/qv8engine_p.h> #include <private/qqmlsourcecoordinate_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> @@ -352,7 +352,7 @@ QString QQmlExpression::sourceFile() const int QQmlExpression::lineNumber() const { Q_D(const QQmlExpression); - return qmlSourceCoordinate(d->line); + return qmlConvertSourceCoordinate<quint16, int>(d->line); } /*! @@ -362,7 +362,7 @@ int QQmlExpression::lineNumber() const int QQmlExpression::columnNumber() const { Q_D(const QQmlExpression); - return qmlSourceCoordinate(d->column); + return qmlConvertSourceCoordinate<quint16, int>(d->column); } /*! @@ -373,8 +373,8 @@ void QQmlExpression::setSourceLocation(const QString &url, int line, int column) { Q_D(QQmlExpression); d->url = url; - d->line = qmlSourceCoordinate(line); - d->column = qmlSourceCoordinate(column); + d->line = qmlConvertSourceCoordinate<int, quint16>(line); + d->column = qmlConvertSourceCoordinate<int, quint16>(column); } /*! diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 1d60c518c4..acebb6bac3 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -149,7 +149,8 @@ QVariant QQmlValueTypeProvider::createVariantFromString(int type, const QString return QVariant(); } -QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, QQmlV4Handle obj, QV4::ExecutionEngine *e, bool *ok) +QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, const QV4::Value &obj, + QV4::ExecutionEngine *e, bool *ok) { QVariant v; @@ -225,63 +226,53 @@ bool QQmlValueTypeProvider::createFromString(int, const QString &, void *, size_ bool QQmlValueTypeProvider::createStringFrom(int, const void *, QString *) { return false; } bool QQmlValueTypeProvider::variantFromString(const QString &, QVariant *) { return false; } bool QQmlValueTypeProvider::variantFromString(int, const QString &, QVariant *) { return false; } -bool QQmlValueTypeProvider::variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *) { return false; } +bool QQmlValueTypeProvider::variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *) { return false; } bool QQmlValueTypeProvider::equal(int, const void *, const QVariant&) { return false; } bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return false; } bool QQmlValueTypeProvider::read(const QVariant&, void *, int) { return false; } bool QQmlValueTypeProvider::write(int, const void *, QVariant&) { return false; } -Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider) -static QQmlValueTypeProvider *valueTypeProvider = nullptr; +struct ValueTypeProviderList { + QQmlValueTypeProvider nullProvider; + QQmlValueTypeProvider *head = &nullProvider; +}; -static QQmlValueTypeProvider **getValueTypeProvider(void) -{ - if (valueTypeProvider == nullptr) { - valueTypeProvider = nullValueTypeProvider; - } - - return &valueTypeProvider; -} +Q_GLOBAL_STATIC(ValueTypeProviderList, valueTypeProviders) Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newProvider) { - static QQmlValueTypeProvider **providerPtr = getValueTypeProvider(); - newProvider->next = *providerPtr; - *providerPtr = newProvider; + if (ValueTypeProviderList *providers = valueTypeProviders()) { + newProvider->next = providers->head; + providers->head = newProvider; + } } Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider) { - if (oldProvider == nullValueTypeProvider) { - // don't remove the null provider - // we get here when the QtQml library is being unloaded - return; - } - - // the only entry with next = 0 is the null provider - Q_ASSERT(oldProvider->next); + if (ValueTypeProviderList *providers = valueTypeProviders()) { + QQmlValueTypeProvider *prev = providers->head; + if (prev == oldProvider) { + providers->head = oldProvider->next; + return; + } - QQmlValueTypeProvider *prev = valueTypeProvider; - if (prev == oldProvider) { - valueTypeProvider = oldProvider->next; - return; - } + // singly-linked list removal + for (; prev; prev = prev->next) { + if (prev->next != oldProvider) + continue; // this is not the provider you're looking for + prev->next = oldProvider->next; + return; + } - // singly-linked list removal - for ( ; prev; prev = prev->next) { - if (prev->next != oldProvider) - continue; // this is not the provider you're looking for - prev->next = oldProvider->next; - return; + qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider); } - - qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider); } -Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void) +Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider() { - static QQmlValueTypeProvider **providerPtr = getValueTypeProvider(); - return *providerPtr; + if (ValueTypeProviderList *providers = valueTypeProviders()) + return providers->head; + return nullptr; } QQmlColorProvider::~QQmlColorProvider() {} diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 818537560c..3c540a6124 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -53,9 +53,8 @@ #include <private/qtqmlglobal_p.h> #include <QtCore/QObject> -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlmetaobject_p.h> #include <private/qmetaobject_p.h> -#include <private/qv8engine_p.h> QT_BEGIN_NAMESPACE @@ -91,7 +90,7 @@ QT_BEGIN_NAMESPACE \endcode */ #define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \ -{ \ +do { \ SenderType *sender = (Sender); \ ReceiverType *receiver = (Receiver); \ const char *signal = (Signal); \ @@ -99,11 +98,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -112,7 +111,7 @@ QT_BEGIN_NAMESPACE } \ Q_ASSERT(signalIdx != -1 && methodIdx != -1); \ QMetaObject::connect(sender, signalIdx, receiver, methodIdx, Qt::DirectConnection); \ -} +} while (0) /*! Disconnect \a Signal of \a Sender from \a Method of \a Receiver. \a Signal must be @@ -130,7 +129,7 @@ QT_BEGIN_NAMESPACE \endcode */ #define qmlobject_disconnect(Sender, SenderType, Signal, Receiver, ReceiverType, Method) \ -{ \ +do { \ SenderType *sender = (Sender); \ ReceiverType *receiver = (Receiver); \ const char *signal = (Signal); \ @@ -138,11 +137,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -151,7 +150,7 @@ QT_BEGIN_NAMESPACE } \ Q_ASSERT(signalIdx != -1 && methodIdx != -1); \ QMetaObject::disconnect(sender, signalIdx, receiver, methodIdx); \ -} +} while (0) /*! This method is identical to qobject_cast<T>() except that it does not require lazy @@ -233,7 +232,7 @@ public: QVariant createVariantFromString(const QString &); QVariant createVariantFromString(int, const QString &, bool *); - QVariant createVariantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, bool*); + QVariant createVariantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, bool *); bool equalValueType(int, const void *, const QVariant&); bool storeValueType(int, const void *, void *, size_t); @@ -250,7 +249,7 @@ private: virtual bool variantFromString(const QString &, QVariant *); virtual bool variantFromString(int, const QString &, QVariant *); - virtual bool variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *); + virtual bool variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *); virtual bool equal(int, const void *, const QVariant&); virtual bool store(int, const void *, void *, size_t); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ba8dce4b6e..8b5e11c890 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -54,6 +54,8 @@ #include <private/qqmltypenamecache_p.h> #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> @@ -131,85 +133,6 @@ bool isPathAbsolute(const QString &path) #endif } -/* - \internal - - Fetches the QQmlType instance registered for \a urlString, creating a - registration for it if it is not already registered, using the associated - \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion - details. - - Errors (if there are any) are placed into \a errors, if it is nonzero. Note - that errors are treated as fatal if \a errors is not set. -*/ -QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, - bool isCompositeSingleton, QList<QQmlError> *errors, - int majorVersion=-1, int minorVersion=-1) -{ - QUrl url(urlString); // ### unfortunate (costly) conversion - QQmlType ret = QQmlMetaType::qmlType(url); - if (ret.isValid()) - return ret; - - int dot = typeName.indexOf(QLatin1Char('.')); - QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); - - // We need a pointer, but we were passed a string. Take a copy so we - // can guarentee it will live long enough to reach qmlregister. - QByteArray buf(unqualifiedtype.toString().toUtf8()); - - QQmlMetaTypeRegistrationFailureRecorder failureRecorder; - - // Register the type. Note that the URI parameters here are empty; for - // file type imports, we do not place them in a URI as we don't - // necessarily have a good and unique one (picture a library import, - // which may be found in multiple plugin locations on disk), but there - // are other reasons for this too. - // - // By not putting them in a URI, we prevent the types from being - // registered on a QQmlTypeModule; this is important, as once types are - // placed on there, they cannot be easily removed, meaning if the - // developer subsequently loads a different import (meaning different - // types) with the same URI (using, say, a different plugin path), it is - // very undesirable that we continue to associate the types from the - // "old" URI with that new module. - // - // Not having URIs also means that the types cannot be found by name - // etc, the only way to look them up is through QQmlImports -- for - // better or worse. - if (isCompositeSingleton) { - QQmlPrivate::RegisterCompositeSingletonType reg = { - url, - "", // uri - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::registerCompositeSingletonType(reg); - } else { - QQmlPrivate::RegisterCompositeType reg = { - url, - "", // uri - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::registerCompositeType(reg); - } - - // This means that the type couldn't be found by URL, but could not be - // registered either, meaning we most likely were passed some kind of bad - // data. - if (!ret.isValid()) { - if (!errors) // Cannot list errors properly, just quit - qFatal("%s", failureRecorder.failures().join('\n').toLatin1().constData()); - QQmlError error; - error.setDescription(failureRecorder.failures().join('\n')); - errors->prepend(error); - } - return ret; -} - } // namespace struct RegisteredPlugin { @@ -219,6 +142,13 @@ struct RegisteredPlugin { struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> { QMutex mutex; + + ~StringRegisteredPluginMap() + { + QMutexLocker lock(&mutex); + for (const RegisteredPlugin &plugin : qAsConst(*this)) + delete plugin.loader; + } }; Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders @@ -831,9 +761,9 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (candidate != end) { if (!base) // ensure we have a componentUrl componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); - QQmlType returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, - nullptr, candidate->majorVersion, - candidate->minorVersion); + QQmlType returnType = QQmlMetaType::typeForUrl(componentUrl, type, isCompositeSingleton, + nullptr, candidate->majorVersion, + candidate->minorVersion); if (vmajor) *vmajor = candidate->majorVersion; if (vminor) @@ -850,12 +780,12 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt typeStr + dotqml_string, // Type -> Type.qml typeStr + dotuidotqml_string // Type -> Type.ui.qml }; - for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) { - exists = typeLoader->fileExists(localDirectoryPath, urlsToTry[i]); + for (const QString &urlToTry : urlsToTry) { + exists = typeLoader->fileExists(localDirectoryPath, urlToTry); if (exists) { #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) // don't let function.qml confuse the use of "new Function(...)" for example. - if (!QQml_isFileCaseCorrect(localDirectoryPath + urlsToTry[i])) { + if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) { exists = false; if (errors) { QQmlError caseError; @@ -867,7 +797,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt #else Q_UNUSED(errors); #endif - qmlUrl = url + urlsToTry[i]; + qmlUrl = url + urlToTry; break; } } @@ -877,8 +807,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType returnType = fetchOrCreateTypeForUrl( - qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors); + QQmlType returnType = QQmlMetaType::typeForUrl( + qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors); if (type_return) *type_return = returnType; return returnType.isValid(); @@ -926,7 +856,10 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); + *type_return = QQmlMetaType::typeForUrl( + resolveLocalUrl(s->imports.at(0)->url, + unqualifiedtype.toString() + QLatin1String(".qml")), + type, false, errors); return type_return->isValid(); } } @@ -1099,13 +1032,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res return true; } -#if defined(QT_SHARED) || !QT_CONFIG(library) -static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) -{ - return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why); -} -#endif - /* Import an extension defined by a qmldir file. @@ -1154,7 +1080,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, int dynamicPluginsFound = 0; int staticPluginsFound = 0; -#if defined(QT_SHARED) +#if QT_CONFIG(library) const auto qmldirPlugins = qmldir.plugins(); for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) { QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); @@ -1165,9 +1091,11 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, // XXX TODO: should we leave the import plugin error alone? // Here, we pop it off the top and coalesce it into this error's message. // The reason is that the lower level may add url and line/column numbering information. - QQmlError poppedError = errors->takeFirst(); QQmlError error; - error.setDescription(msgCannotLoadPlugin(uri, poppedError.description())); + error.setDescription( + QQmlImportDatabase::tr( + "plugin cannot be loaded for module \"%1\": %2") + .arg(uri, errors->takeFirst().description())); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } @@ -1175,7 +1103,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } } } -#endif // QT_SHARED +#endif // QT_CONFIG(library) if (dynamicPluginsFound < qmldirPluginCount) { // Check if the missing plugins can be resolved statically. We do this by looking at @@ -1823,6 +1751,16 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e) addImportPath(QStringLiteral("qrc:/qt-project.org/imports")); addImportPath(QCoreApplication::applicationDirPath()); +#if defined(Q_OS_ANDROID) + addImportPath(QStringLiteral("qrc:/android_rcc_bundle/qml")); + if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QT_BUNDLED_LIBS_PATH"))) { + const QString envImportPath = qEnvironmentVariable("QT_BUNDLED_LIBS_PATH"); + QLatin1Char pathSep(':'); + QStringList paths = envImportPath.split(pathSep, QString::SkipEmptyParts); + for (int ii = paths.count() - 1; ii >= 0; --ii) + addPluginPath(paths.at(ii)); + } +#endif } QQmlImportDatabase::~QQmlImportDatabase() @@ -1870,6 +1808,18 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, if (!resolvedPath.endsWith(Slash)) resolvedPath += Slash; +#if defined(Q_OS_ANDROID) + if (qmldirPath.size() > 25 && qmldirPath.at(0) == QLatin1Char(':') && qmldirPath.at(1) == QLatin1Char('/') && + qmldirPath.startsWith(QStringLiteral(":/android_rcc_bundle/qml/"), Qt::CaseInsensitive)) { + QString pluginName = qmldirPath.mid(21) + Slash + baseName; + auto bundledPath = resolvedPath + QLatin1String("lib") + pluginName.replace(QLatin1Char('/'), QLatin1Char('_')); + for (const QString &suffix : suffixes) { + const QString absolutePath = typeLoader->absoluteFilePath(bundledPath + suffix); + if (!absolutePath.isEmpty()) + return absolutePath; + } + } +#endif resolvedPath += prefix + baseName; for (const QString &suffix : suffixes) { const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix); @@ -1924,9 +1874,15 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader, QLatin1String(".so"), QLatin1String(".bundle") }; -# else // Unix +#else // Unix static const QString prefix = QLatin1String("lib"); - static const QStringList suffixes = { QLatin1String(".so") }; + static const QStringList suffixes = { +# if defined(Q_OS_ANDROID) + QStringLiteral(LIBS_SUFFIX), +# endif + QLatin1String(".so") + + }; #endif return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, suffixes, prefix); @@ -2030,7 +1986,9 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths) if (qmlImportTrace()) qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths; - fileImportPath = paths; + fileImportPath.clear(); + for (auto it = paths.crbegin(); it != paths.crend(); ++it) + addImportPath(*it); // Our existing cached paths may have been invalidated clearDirCache(); @@ -2044,77 +2002,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b { if (qmlImportTrace()) qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath; - - QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance); - if (!iface) { - if (errors) { - QQmlError error; - error.setDescription(tr("Module loaded for URI '%1' does not implement QQmlTypesExtensionInterface").arg(typeNamespace)); - errors->prepend(error); - } - return false; - } - - const QByteArray bytes = uri.toUtf8(); - const char *moduleId = bytes.constData(); - - QQmlMetaTypeRegistrationFailureRecorder failureRecorder; - { - // Create a scope for QWriteLocker to keep it as narrow as possible, and - // to ensure that we release it before the call to initalizeEngine below - QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); - - if (!typeNamespace.isEmpty()) { - // This is an 'identified' module - if (typeNamespace != uri) { - // The namespace for type registrations must match the URI for locating the module - if (errors) { - QQmlError error; - error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri)); - errors->prepend(error); - } - return false; - } - - if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace, vmaj)) { - // Other modules have already installed to this namespace - if (errors) { - QQmlError error; - error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace)); - errors->prepend(error); - } - return false; - } else { - QQmlMetaType::protectNamespace(typeNamespace); - } - } else { - // This is not an identified module - provide a warning - qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module identifier directive - it cannot be protected from external registrations.").arg(uri)); - } - - QQmlMetaType::setTypeRegistrationNamespace(typeNamespace); - - if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { - // basepath should point to the directory of the module, not the plugin file itself: - QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); - } - - iface->registerTypes(moduleId); - QQmlMetaType::setTypeRegistrationNamespace(QString()); - } // QWriteLocker lock(QQmlMetaType::typeRegistrationLock()) - - if (!failureRecorder.failures().isEmpty()) { - if (errors) { - for (const QString &failure : failureRecorder.failures()) { - QQmlError error; - error.setDescription(failure); - errors->prepend(error); - } - } - return false; - } - - return true; + return QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors); } /*! @@ -2169,13 +2057,13 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba return true; } +#if QT_CONFIG(library) /*! \internal */ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors) { -#if QT_CONFIG(library) QFileInfo fileInfo(filePath); const QString absoluteFilePath = fileInfo.absoluteFilePath(); @@ -2253,20 +2141,14 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr } return true; -#else - Q_UNUSED(filePath); - Q_UNUSED(uri); - Q_UNUSED(typeNamespace); - Q_UNUSED(vmaj); - Q_UNUSED(errors); - return false; -#endif } +#endif // QT_CONFIG(library) + void QQmlImportDatabase::clearDirCache() { - QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.begin(); - while (itr != qmldirCache.end()) { + QStringHash<QmldirCache *>::ConstIterator itr = qmldirCache.constBegin(); + while (itr != qmldirCache.constEnd()) { QmldirCache *cache = *itr; do { QmldirCache *nextCache = cache->next; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index f8c01ed876..ea9c2eafb5 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -44,9 +44,10 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qset.h> #include <QtCore/qstringlist.h> +#include <QtQml/qqmlerror.h> #include <private/qqmldirparser_p.h> -#include <private/qqmlmetatype_p.h> -#include <private/qhashedstring_p.h> +#include <private/qqmltype_p.h> +#include <private/qstringhash_p.h> // // W A R N I N G @@ -211,7 +212,9 @@ public: QQmlImportDatabase(QQmlEngine *); ~QQmlImportDatabase(); +#if QT_CONFIG(library) bool importDynamicPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, int vmaj, QList<QQmlError> *errors); +#endif QStringList importPathList(PathType type = LocalOrRemote) const; void setImportPathList(const QStringList &paths); diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 39da550d63..bc06226cbf 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -42,7 +42,6 @@ #include "qqmlincubator_p.h" #include "qqmlexpression_p.h" -#include "qqmlmemoryprofiler_p.h" #include "qqmlobjectcreator_p.h" void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) @@ -272,8 +271,6 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) if (!compilationUnit) return; - QML_MEMORY_SCOPE_URL(compilationUnit->finalUrl()); - QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this); QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this); diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 676ba1a29a..57ec8249cb 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -87,7 +87,7 @@ public: QPointer<QObject> result; QQmlGuardedContextData rootContext; QQmlEnginePrivate *enginePriv; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QScopedPointer<QQmlObjectCreator> creator; int subComponentToCreate; QQmlVMEGuard vmeGuard; diff --git a/src/qml/qml/qqmlinfo.cpp b/src/qml/qml/qqmlinfo.cpp index 6322302422..2bfd2d5bb4 100644 --- a/src/qml/qml/qqmlinfo.cpp +++ b/src/qml/qml/qqmlinfo.cpp @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlDebug(const QObject *object) - \relates QQmlEngine + \relates QtQml \since 5.9 Prints debug messages that include the file and line number for the @@ -91,7 +91,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlInfo(const QObject *object) - \relates QQmlEngine + \relates QtQml Prints informational messages that include the file and line number for the specified QML \a object. @@ -119,7 +119,7 @@ QT_BEGIN_NAMESPACE /*! \fn QQmlInfo QtQml::qmlWarning(const QObject *object) - \relates QQmlEngine + \relates QtQml \since 5.9 Prints warning messages that include the file and line number for the 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 new file mode 100644 index 0000000000..82cad8eba8 --- /dev/null +++ b/src/qml/qml/qqmlirloader.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qqmlirloader_p.h" +#include <private/qqmlirbuilder_p.h> + +QT_BEGIN_NAMESPACE + +QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void QQmlIRLoader::load() +{ + output->jsGenerator.stringTable.initializeFromBackingUnit(unit); + + const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); + + for (quint32 i = 0; i < qmlUnit->nImports; ++i) + output->imports << qmlUnit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New<QmlIR::Pragma>(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + for (uint i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New<QmlIR::Object>(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + object->flags = serializedObject->flags; + object->id = serializedObject->id; + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector<int> functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New<QmlIR::Binding>(); + *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New<QmlIR::Signal>(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New<QmlIR::PoolList<QmlIR::Parameter> >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::Parameter *p = pool->New<QmlIR::Parameter>(); + *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + for (uint i = 0; i < serializedObject->nEnums; ++i) { + const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); + QmlIR::Enum *e = pool->New<QmlIR::Enum>(); + e->nameIndex = serializedEnum->nameIndex; + e->location = serializedEnum->location; + e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); + + for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { + QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); + *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); + e->enumValues->append(v); + } + + object->qmlEnums->append(e); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New<QmlIR::Property>(); + *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; + object->properties->append(p); + } + + { + const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); + for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { + QmlIR::Alias *a = pool->New<QmlIR::Alias>(); + *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; + object->aliases->append(a); + } + } + + const quint32_le *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New<QmlIR::Function>(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + 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 QV4::CompiledData::Parameter *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) + *static_cast<QV4::CompiledData::Parameter*>(&f->formals[i]) = *formalNameIdx; + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h new file mode 100644 index 0000000000..4812946ef0 --- /dev/null +++ b/src/qml/qml/qqmlirloader_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QQMLIRLOADER_P_H +#define QQMLIRLOADER_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/qtqmlglobal_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qqmljsmemorypool_p.h> + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +struct Object; +} + +struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { + QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + +QT_END_NAMESPACE + +#endif // QQMLIRLOADER_P_H diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9a3a5218e0..8661ebcc13 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -208,7 +208,10 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b } Q_ASSERT(m_qmlScope.valueRef()); - QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); + QV4::ReturnedValue res = v4Function->call( + &(callData->thisObject.asValue<QV4::Value>()), + callData->argValues<QV4::Value>(), callData->argc(), + static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); QV4::Scope scope(v4); QV4::ScopedValue result(scope, res); @@ -392,10 +395,10 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext, return; m_qmlScope.set(qmlContext->engine(), *qmlContext); m_v4Function = f; - setCompilationUnit(m_v4Function->compilationUnit); + setCompilationUnit(m_v4Function->executableCompilationUnit()); } -void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) { m_compilationUnit = compilationUnit; } diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 453c8ab8a8..eecee08062 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -153,7 +153,7 @@ protected: void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); - void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); + void setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); // We store some flag bits in the following flag pointers. // activeGuards:flag1 - notifyOnValueChanged @@ -178,11 +178,11 @@ private: QQmlJavaScriptExpression *m_nextExpression; QV4::PersistentValue m_qmlScope; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit; QV4::Function *m_v4Function; }; -class QQmlPropertyCapture +class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture { public: QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w) diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h index 04bab8d929..e182ced51d 100644 --- a/src/qml/qml/qqmllist_p.h +++ b/src/qml/qml/qqmllist_p.h @@ -52,7 +52,7 @@ // #include "qqmllist.h" -#include "qqmlpropertycache_p.h" +#include "qqmlmetaobject_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 2f769c1aef..5349572921 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qqmllistwrapper_p.h" -#include <private/qv8engine_p.h> #include <private/qqmllist_p.h> #include <private/qv4objectproto_p.h> #include <qv4objectiterator_p.h> diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 2b17037df0..dca13ac8d4 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -662,7 +662,7 @@ LOCALE_STRING_PROPERTY(exponential) LOCALE_STRING_PROPERTY(amText) LOCALE_STRING_PROPERTY(pmText) -class QV4LocaleDataDeletable : public QV8Engine::Deletable +class QV4LocaleDataDeletable : public QV4::ExecutionEngine::Deletable { public: QV4LocaleDataDeletable(QV4::ExecutionEngine *engine); diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h index ece06e04b4..ee5d9af2e7 100644 --- a/src/qml/qml/qqmlloggingcategory_p.h +++ b/src/qml/qml/qqmlloggingcategory_p.h @@ -65,7 +65,7 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString name READ name WRITE setName) - Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1) + Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 12) public: enum DefaultLogLevel { diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp new file mode 100644 index 0000000000..a967f46b12 --- /dev/null +++ b/src/qml/qml/qqmlmetaobject.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** 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 "qqmlmetaobject_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> + +QT_BEGIN_NAMESPACE + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &staticQtMetaObject; } +}; + +static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, + const QByteArray &name) +{ + for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = resolvedMetaObject->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) + return true; + } + return false; +} + +static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = scopedName.lastIndexOf("::"); + if (scopeIdx != -1) { + scope = scopedName.left(scopeIdx); + name = scopedName.mid(scopeIdx + 2); + } else { + name = scopedName; + } + + if (scope == "Qt") + return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name); + + if (isNamedEnumeratorInScope(metaObj, scope, name)) + return true; + + if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { + for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { + if (isNamedEnumeratorInScope(*related, scope, name)) + return true; + } + } + + return false; +} + +// Returns true if \a from is assignable to a property of type \a to +bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to) +{ + Q_ASSERT(!from.isNull() && !to.isNull()); + + struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) { + return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); + } }; + + const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2(); + if (tom == &QObject::staticMetaObject) return true; + + if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache + QQmlPropertyCache *fromp = from._m.asT1(); + QQmlPropertyCache *top = to._m.asT1(); + + while (fromp) { + if (fromp == top) return true; + fromp = fromp->parent(); + } + } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject + QQmlPropertyCache *fromp = from._m.asT1(); + + while (fromp) { + const QMetaObject *fromm = fromp->metaObject(); + if (fromm && I::equal(fromm, tom)) return true; + fromp = fromp->parent(); + } + } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache + const QMetaObject *fromm = from._m.asT2(); + + if (!tom) return false; + + while (fromm) { + if (I::equal(fromm, tom)) return true; + fromm = fromm->superClass(); + } + } else { // QMetaObject -> QMetaObject + const QMetaObject *fromm = from._m.asT2(); + + while (fromm) { + if (I::equal(fromm, tom)) return true; + fromm = fromm->superClass(); + } + } + + return false; +} + +void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) +{ + int offset; + + switch (type) { + case QMetaObject::ReadProperty: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + case QMetaObject::QueryPropertyDesignable: + case QMetaObject::QueryPropertyEditable: + case QMetaObject::QueryPropertyScriptable: + case QMetaObject::QueryPropertyStored: + case QMetaObject::QueryPropertyUser: + offset = (*metaObject)->propertyOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->propertyOffset(); + } + break; + case QMetaObject::InvokeMetaMethod: + offset = (*metaObject)->methodOffset(); + while (*index < offset) { + *metaObject = (*metaObject)->superClass(); + offset = (*metaObject)->methodOffset(); + } + break; + default: + offset = 0; + Q_UNIMPLEMENTED(); + offset = INT_MAX; + } + + *index -= offset; +} + +QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const +{ + if (_m.isNull()) return nullptr; + if (_m.isT1()) return _m.asT1(); + else return e->cache(_m.asT2()); +} + +int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const +{ + Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); + + int type = data.propType(); + + const char *propTypeName = nullptr; + + if (type == QMetaType::UnknownType) { + // Find the return type name from the method info + QMetaMethod m; + + if (_m.isT1()) { + QQmlPropertyCache *c = _m.asT1(); + Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); + + while (data.coreIndex() < c->methodIndexCacheStart) + c = c->_parent; + + const QMetaObject *metaObject = c->createMetaObject(); + Q_ASSERT(metaObject); + m = metaObject->method(data.coreIndex()); + } else { + m = _m.asT2()->method(data.coreIndex()); + } + + type = m.returnType(); + propTypeName = m.typeName(); + } + + if (QMetaType::sizeOf(type) <= int(sizeof(int))) { + if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) + return QMetaType::Int; + + if (isNamedEnumerator(metaObject(), propTypeName)) + return QMetaType::Int; + + if (type == QMetaType::UnknownType) { + if (unknownTypeError) + *unknownTypeError = propTypeName; + } + } // else we know that it's a known type, as sizeOf(UnknownType) == 0 + + return type; +} + +int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(!_m.isNull() && index >= 0); + + if (_m.isT1()) { + typedef QQmlPropertyCacheMethodArguments A; + + QQmlPropertyCache *c = _m.asT1(); + Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); + + while (index < c->methodIndexCacheStart) + c = c->_parent; + + QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); + + if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) + return static_cast<A *>(rv->arguments())->arguments; + + const QMetaObject *metaObject = c->createMetaObject(); + Q_ASSERT(metaObject); + QMetaMethod m = metaObject->method(index); + + int argc = m.parameterCount(); + if (!rv->arguments()) { + A *args = c->createArgumentsObject(argc, m.parameterNames()); + rv->setArguments(args); + } + A *args = static_cast<A *>(rv->arguments()); + + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType){ + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + + } + args->arguments[ii + 1] = type; + } + args->argumentsValid = true; + return static_cast<A *>(rv->arguments())->arguments; + + } else { + QMetaMethod m = _m.asT2()->method(index); + return methodParameterTypes(m, argStorage, unknownTypeError); + + } +} + +int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const +{ + Q_ASSERT(argStorage); + + int argc = m.parameterCount(); + argStorage->resize(argc + 1); + argStorage->operator[](0) = argc; + QList<QByteArray> argTypeNames; // Only loaded if needed + + for (int ii = 0; ii < argc; ++ii) { + int type = m.parameterType(ii); + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { + if (argTypeNames.isEmpty()) + argTypeNames = m.parameterTypes(); + if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType) { + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + } + argStorage->operator[](ii + 1) = type; + } + + return argStorage->data(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetaobject_p.h b/src/qml/qml/qqmlmetaobject_p.h new file mode 100644 index 0000000000..65d6361b90 --- /dev/null +++ b/src/qml/qml/qqmlmetaobject_p.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** 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 QQMLMETAOBJECT_P_H +#define QQMLMETAOBJECT_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 <private/qflagpointer_p.h> +#include <private/qqmlpropertycache_p.h> + +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmetaobject.h> + +QT_BEGIN_NAMESPACE + +// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. +// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but +// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a +// QObject type used in assignment or when we don't have a QQmlEngine etc. +// +// This class does NOT reference the propertycache. +class QQmlEnginePrivate; +class QQmlPropertyData; +class Q_QML_EXPORT QQmlMetaObject +{ +public: + typedef QVarLengthArray<int, 9> ArgTypeStorage; + + inline QQmlMetaObject(); + inline QQmlMetaObject(QObject *); + inline QQmlMetaObject(const QMetaObject *); + inline QQmlMetaObject(QQmlPropertyCache *); + inline QQmlMetaObject(const QQmlMetaObject &); + + inline QQmlMetaObject &operator=(const QQmlMetaObject &); + + inline bool isNull() const; + + inline const char *className() const; + inline int propertyCount() const; + + inline bool hasMetaObject() const; + inline const QMetaObject *metaObject() const; + + QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; + + int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; + int *methodParameterTypes(int index, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; + + static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); + + // static_metacall (on Gadgets) doesn't call the base implementation and therefore + // we need a helper to find the correct meta object and property/method index. + static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index); + +protected: + QBiPointer<QQmlPropertyCache, const QMetaObject> _m; + int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, + QByteArray *unknownTypeError) const; + +}; + +QQmlMetaObject::QQmlMetaObject() +{ +} + +QQmlMetaObject::QQmlMetaObject(QObject *o) +{ + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (ddata && ddata->propertyCache) _m = ddata->propertyCache; + else _m = o->metaObject(); + } +} + +QQmlMetaObject::QQmlMetaObject(const QMetaObject *m) + : _m(m) +{ +} + +QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m) + : _m(m) +{ +} + +QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o) + : _m(o._m) +{ +} + +QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o) +{ + _m = o._m; + return *this; +} + +bool QQmlMetaObject::isNull() const +{ + return _m.isNull(); +} + +const char *QQmlMetaObject::className() const +{ + if (_m.isNull()) { + return nullptr; + } else if (_m.isT1()) { + return _m.asT1()->className(); + } else { + return _m.asT2()->className(); + } +} + +int QQmlMetaObject::propertyCount() const +{ + if (_m.isNull()) { + return 0; + } else if (_m.isT1()) { + return _m.asT1()->propertyCount(); + } else { + return _m.asT2()->propertyCount(); + } +} + +bool QQmlMetaObject::hasMetaObject() const +{ + return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject()); +} + +const QMetaObject *QQmlMetaObject::metaObject() const +{ + if (_m.isNull()) return nullptr; + if (_m.isT1()) return _m.asT1()->createMetaObject(); + else return _m.asT2(); +} + +QT_END_NAMESPACE + +#endif // QQMLMETAOBJECT_P_H diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 0f8a850584..c2674b402a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -37,410 +37,78 @@ ** ****************************************************************************/ -#include <QtQml/qqmlprivate.h> #include "qqmlmetatype_p.h" -#include <private/qqmlproxymetaobject_p.h> -#include <private/qqmlcustomparser_p.h> -#include <private/qhashedstring_p.h> -#include <private/qqmlimport_p.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qmetaobject.h> -#include <QtCore/qbitarray.h> -#include <QtCore/qreadwritelock.h> -#include <QtCore/private/qmetaobject_p.h> -#include <QtCore/qloggingcategory.h> - -#include <qmetatype.h> -#include <qobjectdefs.h> -#include <qbytearray.h> -#include <qreadwritelock.h> -#include <qstring.h> -#include <qstringlist.h> -#include <qvector.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmltypemodule_p_p.h> +#include <private/qqmltype_p_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmlextensionplugin_p.h> +#include <private/qv4executablecompilationunit_p.h> -#include <ctype.h> -#include "qqmlcomponent.h" +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmutex.h> +#include <QtCore/qloggingcategory.h> Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) QT_BEGIN_NAMESPACE -struct QQmlMetaTypeData +struct LockedData : private QQmlMetaTypeData { - QQmlMetaTypeData(); - ~QQmlMetaTypeData(); - void registerType(QQmlTypePrivate *priv); - QList<QQmlType> types; - QSet<QQmlType> undeletableTypes; - typedef QHash<int, QQmlTypePrivate *> Ids; - Ids idToType; - typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names; - Names nameToType; - typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only - Files urlToType; - Files urlToNonFileImportType; // For non-file imported composite and composite - // singleton types. This way we can locate any - // of them by url, even if it was registered as - // a module via QQmlPrivate::RegisterCompositeType - typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects; - MetaObjects metaObjectToType; - typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; - StringConverters stringConverters; - - struct VersionedUri { - VersionedUri() - : majorVersion(0) {} - VersionedUri(const QHashedString &uri, int majorVersion) - : uri(uri), majorVersion(majorVersion) {} - bool operator==(const VersionedUri &other) const { - return other.majorVersion == majorVersion && other.uri == uri; - } - QHashedString uri; - int majorVersion; - }; - typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules; - TypeModules uriToModule; - - QBitArray objects; - QBitArray interfaces; - QBitArray lists; - - QList<QQmlPrivate::AutoParentFunction> parentFunctions; - QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit; - - QSet<QString> protectedNamespaces; - - QString typeRegistrationNamespace; - - QHash<int, int> qmlLists; - - QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches; - QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion); - QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); - - void startRecordingTypeRegFailures(QStringList *storage) - { typeRegistrationFailures = storage; } - void stopRecordingTypeRegFailures() - { startRecordingTypeRegFailures(nullptr); } - void recordTypeRegFailure(const QString &message) - { - if (typeRegistrationFailures) - typeRegistrationFailures->append(message); - else - qWarning("%s", message.toUtf8().constData()); - } - -private: - QStringList *typeRegistrationFailures = nullptr; + friend class QQmlMetaTypeDataPtr; }; -struct EnumInfo { - QStringList path; - QString metaObjectName; - QString enumName; - QString enumKey; - QString metaEnumScope; - bool scoped; -}; +Q_GLOBAL_STATIC(LockedData, metaTypeData) +Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) -class QQmlTypeModulePrivate +class QQmlMetaTypeDataPtr { + Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) public: - QQmlTypeModulePrivate() - : minMinorVersion(INT_MAX), maxMinorVersion(0), locked(false) {} - - static QQmlTypeModulePrivate* get(QQmlTypeModule* q) { return q->d; } + QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {} + ~QQmlMetaTypeDataPtr() = default; - QQmlMetaTypeData::VersionedUri uri; + QQmlMetaTypeData &operator*() { return *data; } + QQmlMetaTypeData *operator->() { return data; } + operator QQmlMetaTypeData *() { return data; } - int minMinorVersion; - int maxMinorVersion; - bool locked; + const QQmlMetaTypeData &operator*() const { return *data; } + const QQmlMetaTypeData *operator->() const { return data; } + operator const QQmlMetaTypeData *() const { return data; } - void add(QQmlTypePrivate *); - void remove(const QQmlTypePrivate *type); - - typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash; - TypeHash typeHash; -}; - -Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) -Q_GLOBAL_STATIC_WITH_ARGS(QMutex, metaTypeDataLock, (QMutex::Recursive)) - -static uint qHash(const QQmlMetaTypeData::VersionedUri &v) -{ - return v.uri.hash() ^ qHash(v.majorVersion); -} + bool isValid() const { return data != nullptr; } -QQmlMetaTypeRegistrationFailureRecorder::QQmlMetaTypeRegistrationFailureRecorder() -{ - metaTypeData()->startRecordingTypeRegFailures(&_failures); -} - -QQmlMetaTypeRegistrationFailureRecorder::~QQmlMetaTypeRegistrationFailureRecorder() -{ - metaTypeData()->stopRecordingTypeRegFailures(); -} - -QQmlMetaTypeData::QQmlMetaTypeData() -{ -} - -QQmlMetaTypeData::~QQmlMetaTypeData() -{ - for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) - delete *i; - for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); - it != end; ++it) - (*it)->release(); -} - -class QQmlTypePrivate -{ - Q_DISABLE_COPY(QQmlTypePrivate) -public: - QQmlTypePrivate(QQmlType::RegistrationType type); - ~QQmlTypePrivate(); - - void init() const; - void initEnums(const QQmlPropertyCache *cache = nullptr) const; - void insertEnums(const QMetaObject *metaObject) const; - void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; - - QAtomicInt refCount; - QQmlType::RegistrationType regType; - - struct QQmlCppTypeData - { - int allocationSize; - void (*newFunc)(void *); - QString noCreationReason; - int parserStatusCast; - QObject *(*extFunc)(QObject *); - const QMetaObject *extMetaObject; - QQmlCustomParser *customParser; - QQmlAttachedPropertiesFunc attachedPropertiesFunc; - const QMetaObject *attachedPropertiesType; - int propertyValueSourceCast; - int propertyValueInterceptorCast; - bool registerEnumClassesUnscoped; - }; - - struct QQmlSingletonTypeData - { - QQmlType::SingletonInstanceInfo *singletonInstanceInfo; - }; - - struct QQmlCompositeTypeData - { - QUrl url; - }; - - union extraData { - QQmlCppTypeData* cd; - QQmlSingletonTypeData* sd; - QQmlCompositeTypeData* fd; - } extraData; - - const char *iid; - QHashedString module; - QString name; - QString elementName; - int version_maj; - int version_min; - int typeId; - int listId; - int revision; - mutable bool containsRevisionedAttributes; - mutable QQmlType superType; - const QMetaObject *baseMetaObject; - - int index; - mutable volatile bool isSetup:1; - mutable volatile bool isEnumFromCacheSetup:1; - mutable volatile bool isEnumFromBaseSetup:1; - mutable bool haveSuperType:1; - mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects; - mutable QStringHash<int> enums; - mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums - mutable QList<QStringHash<int>*> scopedEnums; - - struct PropertyCacheByMinorVersion - { - PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {} - explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {} - QQmlPropertyCachePtr cache; - int minorVersion; - }; - QVector<PropertyCacheByMinorVersion> propertyCaches; - QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const; - void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache); private: - void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const; - void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const; + QMutexLocker locker; + LockedData *data = nullptr; }; -void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, + const QQmlPrivate::RegisterInterface &type) { - for (int i = 0; i < types.count(); ++i) { - if (!types.at(i).isValid()) { - types[i] = QQmlType(priv); - priv->index = i; - return; - } - } - types.append(QQmlType(priv)); - priv->index = types.count() - 1; -} - -void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) -{ - if (scriptCallback && scriptApi(e).isUndefined()) { - QJSValue value = scriptCallback(e, e); - if (value.isQObject()) { - QObject *o = value.toQObject(); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } - setScriptApi(e, value); - } else if (qobjectCallback && !qobjectApi(e)) { - QObject *o = qobjectCallback(e, e); - setQObjectApi(e, o); - if (!o) { - qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", qPrintable(typeName)); - } - // if this object can use a property cache, create it now - QQmlData::ensurePropertyCache(e, o); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } else if (!url.isEmpty() && !qobjectApi(e)) { - QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); - QObject *o = component.beginCreate(e->rootContext()); - setQObjectApi(e, o); - if (o) - component.completeCreate(); - } -} - -void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) -{ - // cleans up the engine-specific singleton instances if they exist. - scriptApis.remove(e); - QObject *o = qobjectApis.take(e); - if (o) { - QQmlData *ddata = QQmlData::get(o, false); - if (url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) - return; - delete o; - } -} - -void QQmlType::SingletonInstanceInfo::setQObjectApi(QQmlEngine *e, QObject *o) -{ - qobjectApis.insert(e, o); -} - -QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const -{ - return qobjectApis.value(e); -} - -void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v) -{ - scriptApis.insert(e, v); -} - -QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const -{ - return scriptApis.value(e); -} - -QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) -: refCount(1), regType(type), iid(nullptr), typeId(0), listId(0), revision(0), - containsRevisionedAttributes(false), baseMetaObject(nullptr), - index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false), - haveSuperType(false) -{ - switch (type) { - case QQmlType::CppType: - extraData.cd = new QQmlCppTypeData; - extraData.cd->allocationSize = 0; - extraData.cd->newFunc = nullptr; - extraData.cd->parserStatusCast = -1; - extraData.cd->extFunc = nullptr; - extraData.cd->extMetaObject = nullptr; - extraData.cd->customParser = nullptr; - extraData.cd->attachedPropertiesFunc = nullptr; - extraData.cd->attachedPropertiesType = nullptr; - extraData.cd->propertyValueSourceCast = -1; - extraData.cd->propertyValueInterceptorCast = -1; - extraData.cd->registerEnumClassesUnscoped = true; - break; - case QQmlType::SingletonType: - case QQmlType::CompositeSingletonType: - extraData.sd = new QQmlSingletonTypeData; - extraData.sd->singletonInstanceInfo = nullptr; - break; - case QQmlType::InterfaceType: - extraData.cd = nullptr; - break; - case QQmlType::CompositeType: - extraData.fd = new QQmlCompositeTypeData; - break; - default: qFatal("QQmlTypePrivate Internal Error."); - } -} - -QQmlTypePrivate::~QQmlTypePrivate() -{ - qDeleteAll(scopedEnums); - switch (regType) { - case QQmlType::CppType: - delete extraData.cd->customParser; - delete extraData.cd; - break; - case QQmlType::SingletonType: - case QQmlType::CompositeSingletonType: - delete extraData.sd->singletonInstanceInfo; - delete extraData.sd; - break; - case QQmlType::CompositeType: - delete extraData.fd; - break; - default: //Also InterfaceType, because it has no extra data - break; - } -} - -QQmlType::QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &interface) - : d(new QQmlTypePrivate(InterfaceType)) -{ - d->iid = interface.iid; - d->typeId = interface.typeId; - d->listId = interface.listId; + auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); + d->iid = type.iid; + d->typeId = type.typeId; + d->listId = type.listId; d->isSetup = true; d->version_maj = 0; d->version_min = 0; data->registerType(d); + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) - : d(new QQmlTypePrivate(SingletonType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterSingletonType &type) { + auto *d = new QQmlTypePrivate(QQmlType::SingletonType); data->registerType(d); - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); - + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; - if (type.qobjectApi) { + if (type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi)) { if (type.version >= 1) // static metaobject added in version 1 d->baseMetaObject = type.instanceMetaObject; if (type.version >= 2) // typeId added in version 2 @@ -449,37 +117,26 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm d->revision = type.revision; } - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; + d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; - d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + if (type.version >= 3) { + d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.generalizedQobjectApi; + } else { + d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + } d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); d->extraData.sd->singletonInstanceInfo->instanceMetaObject - = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : nullptr; -} - -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type) - : d(new QQmlTypePrivate(CompositeSingletonType)) -{ - data->registerType(d); - - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); - - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; + = ((type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi) ) && type.version >= 1) ? type.instanceMetaObject : nullptr; - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; - d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url); - d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterType &type) - : d(new QQmlTypePrivate(CppType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterType &type) { + QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType); data->registerType(d); - - d->elementName = elementName; - d->module = QString::fromUtf8(type.uri); + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; @@ -509,141 +166,41 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) d->extraData.cd->registerEnumClassesUnscoped = false; } + + return d; } -QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) - : d(new QQmlTypePrivate(CompositeType)) +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterCompositeType &type) { + auto *d = new QQmlTypePrivate(QQmlType::CompositeType); data->registerType(d); - - d->elementName = elementName; - - d->module = QString::fromUtf8(type.uri); + d->setName(QString::fromUtf8(type.uri), elementName); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; d->extraData.fd->url = QQmlTypeLoader::normalize(type.url); + return d; } -QQmlType::QQmlType() - : d(nullptr) -{ -} - -QQmlType::QQmlType(const QQmlType &other) - : d(other.d) -{ - if (d) - d->refCount.ref(); -} - -QQmlType &QQmlType::operator =(const QQmlType &other) -{ - if (d != other.d) { - if (d && !d->refCount.deref()) - delete d; - d = other.d; - if (d) - d->refCount.ref(); - } - return *this; -} - -QQmlType::QQmlType(QQmlTypePrivate *priv) - : d(priv) -{ - if (d) - d->refCount.ref(); -} - -QQmlType::~QQmlType() -{ - if (d && !d->refCount.deref()) - delete d; -} - -QHashedString QQmlType::module() const -{ - if (!d) - return QHashedString(); - return d->module; -} - -int QQmlType::majorVersion() const -{ - if (!d) - return -1; - return d->version_maj; -} - -int QQmlType::minorVersion() const +static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterCompositeSingletonType &type) { - if (!d) - return -1; - return d->version_min; -} - -bool QQmlType::availableInVersion(int vmajor, int vminor) const -{ - Q_ASSERT(vmajor >= 0 && vminor >= 0); - if (!d) - return false; - return vmajor == d->version_maj && vminor >= d->version_min; -} - -bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const -{ - Q_ASSERT(vmajor >= 0 && vminor >= 0); - if (!d) - return false; - return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; -} - -// returns the nearest _registered_ super class -QQmlType QQmlType::superType() const -{ - if (!d) - return QQmlType(); - if (!d->haveSuperType && d->baseMetaObject) { - const QMetaObject *mo = d->baseMetaObject->superClass(); - while (mo && !d->superType.isValid()) { - d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min); - mo = mo->superClass(); - } - d->haveSuperType = true; - } - - return d->superType; -} + auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); + data->registerType(d); + d->setName(QString::fromUtf8(type.uri), elementName); -QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const -{ - Q_ASSERT(isComposite()); - if (!engine || !d) - return QQmlType(); - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); - if (td.isNull() || !td->isComplete()) - return QQmlType(); - QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); - const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - return QQmlMetaType::qmlType(mo); -} + d->version_maj = type.versionMajor; + d->version_min = type.versionMinor; -QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const -{ - // similar logic to resolveCompositeBaseType - Q_ASSERT(isComposite()); - if (!engine) - return nullptr; - QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); - if (td.isNull() || !td->isComplete()) - return nullptr; - QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); - return compilationUnit->rootPropertyCache().data(); + d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; + d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url); + d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + return d; } -static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, - const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) +void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) { // Set classname builder.setClassName(ignoreEnd->className()); @@ -710,918 +267,10 @@ static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } -static bool isPropertyRevisioned(const QMetaObject *mo, int index) -{ - int i = index; - i -= mo->propertyOffset(); - if (i < 0 && mo->d.superdata) - return isPropertyRevisioned(mo->d.superdata, index); - - const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data); - if (i >= 0 && i < mop->propertyCount) { - int handle = mop->propertyData + 3*i; - int flags = mo->d.data[handle + 2]; - - return (flags & Revisioned); - } - - return false; -} - -void QQmlTypePrivate::init() const -{ - if (isSetup) - return; - - QMutexLocker lock(metaTypeDataLock()); - if (isSetup) - return; - - const QMetaObject *mo = baseMetaObject; - if (!mo) { - // version 0 singleton type without metaobject information - return; - } - - if (regType == QQmlType::CppType) { - // Setup extended meta object - // XXX - very inefficient - if (extraData.cd->extFunc) { - QMetaObjectBuilder builder; - clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, extraData.cd->extMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = mo; - QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 }; - metaObjects << data; - } - } - - mo = mo->d.superdata; - while(mo) { - QQmlTypePrivate *t = metaTypeData()->metaObjectToType.value(mo); - if (t) { - if (t->regType == QQmlType::CppType) { - if (t->extraData.cd->extFunc) { - QMetaObjectBuilder builder; - clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = baseMetaObject; - if (!metaObjects.isEmpty()) - metaObjects.constLast().metaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; - metaObjects << data; - } - } - } - mo = mo->d.superdata; - } - - for (int ii = 0; ii < metaObjects.count(); ++ii) { - metaObjects[ii].propertyOffset = - metaObjects.at(ii).metaObject->propertyOffset(); - metaObjects[ii].methodOffset = - metaObjects.at(ii).metaObject->methodOffset(); - } - - // Check for revisioned details - { - const QMetaObject *mo = nullptr; - if (metaObjects.isEmpty()) - mo = baseMetaObject; - else - mo = metaObjects.constFirst().metaObject; - - for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { - if (isPropertyRevisioned(mo, ii)) - containsRevisionedAttributes = true; - } - - for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { - if (mo->method(ii).revision() != 0) - containsRevisionedAttributes = true; - } - } - - isSetup = true; - lock.unlock(); -} - -void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const -{ - if ((isEnumFromBaseSetup || !baseMetaObject) - && (isEnumFromCacheSetup || !cache)) { - return; - } - - init(); - - QMutexLocker lock(metaTypeDataLock()); - - if (!isEnumFromCacheSetup && cache) { - insertEnumsFromPropertyCache(cache); - isEnumFromCacheSetup = true; - } - - if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject - insertEnums(baseMetaObject); - isEnumFromBaseSetup = true; - } -} - -void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const -{ - // Add any enum values defined by 'related' classes - if (metaObject->d.relatedMetaObjects) { - const auto *related = metaObject->d.relatedMetaObjects; - if (related) { - while (*related) - insertEnums(*related++); - } - } - - QSet<QString> localEnums; - const QMetaObject *localMetaObject = nullptr; - - // Add any enum values defined by this class, overwriting any inherited values - for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { - QMetaEnum e = metaObject->enumerator(ii); - const bool isScoped = e.isScoped(); - QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr; - - // We allow enums in sub-classes to overwrite enums from base-classes, such as - // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin). - // This is acceptable because the _use_ of the enum from the QML side requires qualification - // anyway, i.e. ListView.Center vs. Item.Center. - // However if a class defines two enums with the same value, then that must produce a warning - // because it represents a valid conflict. - if (e.enclosingMetaObject() != localMetaObject) { - localEnums.clear(); - localMetaObject = e.enclosingMetaObject(); - } - - for (int jj = 0; jj < e.keyCount(); ++jj) { - const QString key = QString::fromUtf8(e.key(jj)); - const int value = e.value(jj); - if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) { - if (localEnums.contains(key)) { - auto existingEntry = enums.find(key); - if (existingEntry != enums.end() && existingEntry.value() != value) { - qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData()); - createEnumConflictReport(metaObject, key); - } - } else { - localEnums.insert(key); - } - enums.insert(key, value); - } - if (isScoped) - scoped->insert(key, value); - } - - if (isScoped) { - scopedEnums << scoped; - scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1); - } - } -} - -void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const -{ - path.append(QString::fromUtf8(metaObject->className())); - - if (metaObject->d.relatedMetaObjects) { - const auto *related = metaObject->d.relatedMetaObjects; - if (related) { - while (*related) - createListOfPossibleConflictingItems(*related++, enumInfoList, path); - } - } - - for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { - const auto e = metaObject->enumerator(ii); - - for (int jj = 0; jj < e.keyCount(); ++jj) { - const QString key = QString::fromUtf8(e.key(jj)); - - EnumInfo enumInfo; - enumInfo.metaObjectName = QString::fromUtf8(metaObject->className()); - enumInfo.enumName = QString::fromUtf8(e.name()); - enumInfo.enumKey = key; - enumInfo.scoped = e.isScoped(); - enumInfo.path = path; - enumInfo.metaEnumScope = QString::fromUtf8(e.scope()); - enumInfoList.append(enumInfo); - } - } -} - -void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const -{ - QList<EnumInfo> enumInfoList; - - if (baseMetaObject) // prefer baseMetaObject if available - metaObject = baseMetaObject; - - if (!metaObject) { // If there is no metaObject at all return early - qWarning() << "No meta object information available. Skipping conflict analysis."; - return; - } - - createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList()); - - qWarning().noquote() << QLatin1String("Possible conflicting items:"); - // find items with conflicting key - for (const auto i : enumInfoList) { - if (i.enumKey == conflictingKey) - qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope " - << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->")); - } -} - -void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const -{ - const QMetaObject *cppMetaObject = cache->firstCppMetaObject(); - - while (cache && cache->metaObject() != cppMetaObject) { - - int count = cache->qmlEnumCount(); - for (int ii = 0; ii < count; ++ii) { - QStringHash<int> *scoped = new QStringHash<int>(); - QQmlEnumData *enumData = cache->qmlEnum(ii); - - for (int jj = 0; jj < enumData->values.count(); ++jj) { - const QQmlEnumValue &value = enumData->values.at(jj); - enums.insert(value.namedValue, value.value); - scoped->insert(value.namedValue, value.value); - } - scopedEnums << scoped; - scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1); - } - cache = cache->parent(); - } - insertEnums(cppMetaObject); -} - - -QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersion) const -{ - for (int i = 0; i < propertyCaches.count(); ++i) - if (propertyCaches.at(i).minorVersion == minorVersion) - return propertyCaches.at(i).cache.data(); - return nullptr; -} - -void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache) -{ - for (int i = 0; i < propertyCaches.count(); ++i) { - if (propertyCaches.at(i).minorVersion == minorVersion) { - propertyCaches[i].cache = cache; - return; - } - } - propertyCaches.append(PropertyCacheByMinorVersion(cache, minorVersion)); -} - -QByteArray QQmlType::typeName() const -{ - if (d) { - if (d->regType == SingletonType || d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); - else if (d->baseMetaObject) - return d->baseMetaObject->className(); - } - return QByteArray(); -} - -QString QQmlType::elementName() const -{ - if (!d) - return QString(); - return d->elementName; -} - -QString QQmlType::qmlTypeName() const -{ - if (!d) - return QString(); - if (d->name.isEmpty()) { - if (!d->module.isEmpty()) - d->name = static_cast<QString>(d->module) + QLatin1Char('/') + d->elementName; - else - d->name = d->elementName; - } - - return d->name; -} - -QObject *QQmlType::create() const -{ - if (!d || !isCreatable()) - return nullptr; - - d->init(); - - QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize); - d->extraData.cd->newFunc(rv); - - if (rv && !d->metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->metaObjects); - - return rv; -} - -void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const -{ - if (!d || !isCreatable()) - return; - - d->init(); - - QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory); - d->extraData.cd->newFunc(rv); - - if (rv && !d->metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->metaObjects); - - *out = rv; - *memory = ((char *)rv) + d->extraData.cd->allocationSize; -} - -QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const -{ - if (!d) - return nullptr; - if (d->regType != SingletonType && d->regType != CompositeSingletonType) - return nullptr; - return d->extraData.sd->singletonInstanceInfo; -} - -QQmlCustomParser *QQmlType::customParser() const -{ - if (!d) - return nullptr; - if (d->regType != CppType) - return nullptr; - return d->extraData.cd->customParser; -} - -QQmlType::CreateFunc QQmlType::createFunction() const -{ - if (!d || d->regType != CppType) - return nullptr; - return d->extraData.cd->newFunc; -} - -QString QQmlType::noCreationReason() const -{ - if (!d || d->regType != CppType) - return QString(); - return d->extraData.cd->noCreationReason; -} - -bool QQmlType::isCreatable() const -{ - return d && d->regType == CppType && d->extraData.cd->newFunc; -} - -QQmlType::ExtensionFunc QQmlType::extensionFunction() const -{ - if (!d || d->regType != CppType) - return nullptr; - return d->extraData.cd->extFunc; -} - -bool QQmlType::isExtendedType() const -{ - if (!d) - return false; - d->init(); - - return !d->metaObjects.isEmpty(); -} - -bool QQmlType::isSingleton() const -{ - return d && (d->regType == SingletonType || d->regType == CompositeSingletonType); -} - -bool QQmlType::isInterface() const -{ - return d && d->regType == InterfaceType; -} - -bool QQmlType::isComposite() const -{ - return d && (d->regType == CompositeType || d->regType == CompositeSingletonType); -} - -bool QQmlType::isCompositeSingleton() const -{ - return d && d->regType == CompositeSingletonType; -} - -int QQmlType::typeId() const -{ - return d ? d->typeId : -1; -} - -int QQmlType::qListTypeId() const -{ - return d ? d->listId : -1; -} - -const QMetaObject *QQmlType::metaObject() const -{ - if (!d) - return nullptr; - d->init(); - - if (d->metaObjects.isEmpty()) - return d->baseMetaObject; - else - return d->metaObjects.constFirst().metaObject; - -} - -const QMetaObject *QQmlType::baseMetaObject() const -{ - return d ? d->baseMetaObject : nullptr; -} - -bool QQmlType::containsRevisionedAttributes() const -{ - if (!d) - return false; - d->init(); - - return d->containsRevisionedAttributes; -} - -int QQmlType::metaObjectRevision() const -{ - return d ? d->revision : -1; -} - -QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const -{ - if (!d) - return nullptr; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesFunc; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesFunction(engine); -} - -const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const -{ - if (!d) - return nullptr; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesType; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesType(engine); -} - -/* -This is the id passed to qmlAttachedPropertiesById(). This is different from the index -for the case that a single class is registered under two or more names (eg. Item in -Qt 4.7 and QtQuick 1.0). -*/ -int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const -{ - if (!d) - return -1; - if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesType ? d->index : -1; - - QQmlType base; - if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); - return base.attachedPropertiesId(engine); -} - -int QQmlType::parserStatusCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->parserStatusCast; -} - -int QQmlType::propertyValueSourceCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->propertyValueSourceCast; -} - -int QQmlType::propertyValueInterceptorCast() const -{ - if (!d || d->regType != CppType) - return -1; - return d->extraData.cd->propertyValueInterceptorCast; -} - -const char *QQmlType::interfaceIId() const -{ - if (!d || d->regType != InterfaceType) - return nullptr; - return d->iid; -} - -int QQmlType::index() const -{ - return d ? d->index : -1; -} - -QUrl QQmlType::sourceUrl() const -{ - if (d) { - if (d->regType == CompositeType) - return d->extraData.fd->url; - else if (d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->url; - } - return QUrl(); -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->enums.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const -{ - Q_UNUSED(engine) - Q_ASSERT(ok); - *ok = true; - - if (d) { - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - int *rv = d->scopedEnums.at(index)->value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const -{ - Q_UNUSED(engine) - Q_ASSERT(ok); - *ok = true; - - if (d) { - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - int *rv = d->scopedEnums.at(index)->value(name); - if (rv) - return *rv; - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); - if (rv) { - int index = *rv; - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length())); - if (rv) - return *rv; - } - } - - *ok = false; - return -1; -} - -int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const -{ - Q_ASSERT(ok); - if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - - d->initEnums(cache); - - int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); - if (rv) { - int index = *rv; - Q_ASSERT(index > -1 && index < d->scopedEnums.count()); - rv = d->scopedEnums.at(index)->value(QHashedStringRef(name)); - if (rv) - return *rv; - } - } - - *ok = false; - return -1; -} - -void QQmlType::refHandle(QQmlTypePrivate *priv) -{ - if (priv) - priv->refCount.ref(); -} - -void QQmlType::derefHandle(QQmlTypePrivate *priv) -{ - if (priv && !priv->refCount.deref()) - delete priv; -} - -int QQmlType::refCount(QQmlTypePrivate *priv) -{ - if (priv) - return priv->refCount; - return -1; -} - -namespace { -template <typename QQmlTypeContainer> -void removeQQmlTypePrivate(QQmlTypeContainer &container, const QQmlTypePrivate *reference) -{ - for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) { - if (*it == reference) - it = container.erase(it); - else - ++it; - } -} - -struct IsQQmlTypePrivate -{ - const QQmlTypePrivate *reference; - explicit IsQQmlTypePrivate(const QQmlTypePrivate *ref) : reference(ref) {} - - bool operator()(const QQmlTypePrivate *priv) const { return reference == priv; } -}; -} - -QQmlTypeModule::QQmlTypeModule() -: d(new QQmlTypeModulePrivate) -{ -} - -QQmlTypeModule::~QQmlTypeModule() -{ - delete d; d = nullptr; -} - -QString QQmlTypeModule::module() const -{ - return d->uri.uri; -} - -int QQmlTypeModule::majorVersion() const -{ - return d->uri.majorVersion; -} - -int QQmlTypeModule::minimumMinorVersion() const -{ - return d->minMinorVersion; -} - -int QQmlTypeModule::maximumMinorVersion() const -{ - return d->maxMinorVersion; -} - -void QQmlTypeModulePrivate::add(QQmlTypePrivate *type) -{ - int minVersion = type->version_min; - minMinorVersion = qMin(minMinorVersion, minVersion); - maxMinorVersion = qMax(maxMinorVersion, minVersion); - - QList<QQmlTypePrivate *> &list = typeHash[type->elementName]; - for (int ii = 0; ii < list.count(); ++ii) { - Q_ASSERT(list.at(ii)); - if (list.at(ii)->version_min < minVersion) { - list.insert(ii, type); - return; - } - } - list.append(type); -} - -void QQmlTypeModulePrivate::remove(const QQmlTypePrivate *type) -{ - for (TypeHash::ConstIterator elementIt = typeHash.begin(); elementIt != typeHash.end();) { - QList<QQmlTypePrivate *> &list = const_cast<QList<QQmlTypePrivate *> &>(elementIt.value()); - - removeQQmlTypePrivate(list, type); - -#if 0 - if (list.isEmpty()) - elementIt = typeHash.erase(elementIt); - else - ++elementIt; -#else - ++elementIt; -#endif - } -} - -QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const -{ - QMutexLocker lock(metaTypeDataLock()); - - QList<QQmlTypePrivate *> *types = d->typeHash.value(name); - if (types) { - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->version_min <= minor) - return QQmlType(types->at(ii)); - } - - return QQmlType(); -} - -QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const -{ - QMutexLocker lock(metaTypeDataLock()); - - QList<QQmlTypePrivate *> *types = d->typeHash.value(name); - if (types) { - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->version_min <= minor) - return QQmlType(types->at(ii)); - } - - return QQmlType(); -} - -void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const -{ - QMutexLocker lock(metaTypeDataLock()); - for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end(); - typeCandidates != end; ++typeCandidates) { - for (auto type: typeCandidates.value()) { - if (type->regType == QQmlType::CompositeSingletonType) - callback(QQmlType(type)); - } - } -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion() -: m_module(nullptr), m_minor(0) -{ -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor) -: m_module(module), m_minor(minor) -{ - Q_ASSERT(m_module); - Q_ASSERT(m_minor >= 0); -} - -QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o) -: m_module(o.m_module), m_minor(o.m_minor) -{ -} - -QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o) -{ - m_module = o.m_module; - m_minor = o.m_minor; - return *this; -} - -QQmlTypeModule *QQmlTypeModuleVersion::module() const -{ - return m_module; -} - -int QQmlTypeModuleVersion::minorVersion() const -{ - return m_minor; -} - -QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const -{ - if (!m_module) - return QQmlType(); - return m_module->type(name, m_minor); -} - -QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const -{ - if (!m_module) - return QQmlType(); - return m_module->type(name, m_minor); -} - -void qmlClearTypeRegistrations() // Declared in qqml.h +void QQmlMetaType::clearTypeRegistrations() { //Only cleans global static, assumed no running engine - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i) delete *i; @@ -1630,44 +279,35 @@ void qmlClearTypeRegistrations() // Declared in qqml.h data->idToType.clear(); data->nameToType.clear(); data->urlToType.clear(); + data->typePropertyCaches.clear(); data->urlToNonFileImportType.clear(); data->metaObjectToType.clear(); data->uriToModule.clear(); data->undeletableTypes.clear(); - - QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types -#if QT_CONFIG(library) - qmlClearEnginePlugins(); -#endif -} - -static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) -{ - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - data->parentFunctions.removeOne(function); } -static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) +int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->parentFunctions.append(autoparent.function); return data->parentFunctions.count() - 1; } -QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface) +void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) { - if (interface.version > 0) - qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; + data->parentFunctions.removeOne(function); +} - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); +QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) +{ + if (type.version > 0) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); - QQmlType type(data, interface); - QQmlTypePrivate *priv = type.priv(); + QQmlMetaTypeDataPtr data; + QQmlTypePrivate *priv = createQQmlType(data, type); Q_ASSERT(priv); data->idToType.insert(priv->typeId, priv); @@ -1676,14 +316,14 @@ QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface) if (!priv->elementName.isEmpty()) data->nameToType.insert(priv->elementName, priv); - if (data->interfaces.size() <= interface.typeId) - data->interfaces.resize(interface.typeId + 16); - if (data->lists.size() <= interface.listId) - data->lists.resize(interface.listId + 16); - data->interfaces.setBit(interface.typeId, true); - data->lists.setBit(interface.listId, true); + if (data->interfaces.size() <= type.typeId) + data->interfaces.resize(type.typeId + 16); + if (data->lists.size() <= type.listId) + data->lists.resize(type.listId + 16); + data->interfaces.setBit(type.typeId, true); + data->lists.setBit(type.listId, true); - return type; + return QQmlType(priv); } QString registrationTypeString(QQmlType::RegistrationType typeType) @@ -1701,7 +341,8 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) } // NOTE: caller must hold a QMutexLocker on "data" -bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, const QString &typeName, int majorVersion = -1) +bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, + const char *uri, const QString &typeName, int majorVersion = -1) { if (!typeName.isEmpty()) { if (typeName.at(0).isLower()) { @@ -1736,7 +377,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da versionedUri.uri = nameSpace; versionedUri.majorVersion = majorVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){ - if (QQmlTypeModulePrivate::get(qqtm)->locked){ + if (qqtm->isLocked()){ QString failure(QCoreApplication::translate("qmlRegisterType", "Cannot install %1 '%2' into protected module '%3' version '%4'")); data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion)); @@ -1755,8 +396,7 @@ QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMe QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); QQmlTypeModule *module = data->uriToModule.value(versionedUri); if (!module) { - module = new QQmlTypeModule; - module->d->uri = versionedUri; + module = new QQmlTypeModule(versionedUri.uri, versionedUri.majorVersion); data->uriToModule.insert(versionedUri, module); } return module; @@ -1792,47 +432,47 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) QQmlTypeModule *module = getTypeModule(mod, type->version_maj, data); Q_ASSERT(module); - module->d->add(type); + module->add(type); } } -QQmlType registerType(const QQmlPrivate::RegisterType &type) +QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString elementName = QString::fromUtf8(type.elementName); if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, elementName, type); + QQmlTypePrivate *priv = createQQmlType(data, elementName, type); - addTypeToData(dtype.priv(), data); + addTypeToData(priv, data); if (!type.typeId) - data->idToType.insert(dtype.typeId(), dtype.priv()); + data->idToType.insert(priv->typeId, priv); - return dtype; + return QQmlType(priv); } -QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) +QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, typeName, type); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); - addTypeToData(dtype.priv(), data); + addTypeToData(priv, data); - return dtype; + return QQmlType(priv); } QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); bool fileImport = false; if (*(type.uri) == '\0') @@ -1840,21 +480,20 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, typeName)) return QQmlType(); - QQmlType dtype(data, typeName, type); - - addTypeToData(dtype.priv(), data); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv()); + files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); - return dtype; + return QQmlType(priv); } QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + QString typeName = QString::fromUtf8(type.typeName); bool fileImport = false; if (*(type.uri) == '\0') @@ -1862,16 +501,16 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit if (!checkRegistration(QQmlType::CompositeType, data, fileImport?nullptr:type.uri, typeName, type.versionMajor)) return QQmlType(); - QQmlType dtype(data, typeName, type); - addTypeToData(dtype.priv(), data); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), dtype.priv()); + files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); - return dtype; + return QQmlType(priv); } -void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { QByteArray name = compilationUnit->rootPropertyCache()->className(); @@ -1894,125 +533,61 @@ void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationU compilationUnit->metaTypeId = ptr_type; compilationUnit->listMetaTypeId = lst_type; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *d = metaTypeData(); - d->qmlLists.insert(lst_type, ptr_type); + QQmlMetaTypeDataPtr data; + data->qmlLists.insert(lst_type, ptr_type); } -void QQmlMetaType::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) { int ptr_type = compilationUnit->metaTypeId; int lst_type = compilationUnit->listMetaTypeId; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *d = metaTypeData(); - d->qmlLists.remove(lst_type); + QQmlMetaTypeDataPtr data; + data->qmlLists.remove(lst_type); QMetaType::unregisterType(ptr_type); QMetaType::unregisterType(lst_type); } -int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) +int QQmlMetaType::registerUnitCacheHook( + const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) { if (hookRegistration.version > 0) qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit; return 0; } -/* -This method is "over generalized" to allow us to (potentially) register more types of things in -the future without adding exported symbols. -*/ -int QQmlPrivate::qmlregister(RegistrationType type, void *data) -{ - if (type == AutoParentRegistration) - return registerAutoParentFunction(*reinterpret_cast<RegisterAutoParent *>(data)); - else if (type == QmlUnitCacheHookRegistration) - return registerQmlUnitCacheHook(*reinterpret_cast<RegisterQmlUnitCacheHook *>(data)); - - QQmlType dtype; - if (type == TypeRegistration) - dtype = registerType(*reinterpret_cast<RegisterType *>(data)); - else if (type == InterfaceRegistration) - dtype = registerInterface(*reinterpret_cast<RegisterInterface *>(data)); - else if (type == SingletonRegistration) - dtype = registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data)); - else if (type == CompositeRegistration) - dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data)); - else if (type == CompositeSingletonRegistration) - dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data)); - else - return -1; - - if (!dtype.isValid()) - return -1; - - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *typeData = metaTypeData(); - typeData->undeletableTypes.insert(dtype); - - return dtype.index(); -} - -void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data) -{ - switch (type) { - case AutoParentRegistration: - unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data)); - break; - case QmlUnitCacheHookRegistration: - QQmlMetaType::removeCachedUnitLookupFunction( - reinterpret_cast<QmlUnitCacheLookupFunction>(data)); - break; - case TypeRegistration: - case InterfaceRegistration: - case SingletonRegistration: - case CompositeRegistration: - case CompositeSingletonRegistration: - qmlUnregisterType(data); - break; - } -} - -//From qqml.h -bool qmlProtectModule(const char *uri, int majVersion) +bool QQmlMetaType::protectModule(const char *uri, int majVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlMetaTypeData::VersionedUri versionedUri; versionedUri.uri = QString::fromUtf8(uri); versionedUri.majorVersion = majVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) { - QQmlTypeModulePrivate::get(qqtm)->locked = true; + qqtm->lock(); return true; } return false; } -//From qqml.h -void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) +void QQmlMetaType::registerModule(const char *uri, int versionMajor, int versionMinor) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); Q_ASSERT(module); - QQmlTypeModulePrivate *p = QQmlTypeModulePrivate::get(module); - p->minMinorVersion = qMin(p->minMinorVersion, versionMinor); - p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor); + module->addMinorVersion(versionMinor); } -//From qqml.h -int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +int QQmlMetaType::typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); if (!module) @@ -2025,34 +600,218 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q return type.index(); } -bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion) +void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) { - const QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; + data->undeletableTypes.insert(dtype); +} +static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, + int majorVersion) +{ // Has any type previously been installed to this namespace? QHashedString nameSpace(uri); - for (const QQmlType &type : data->types) + for (const QQmlType &type : data->types) { if (type.module() == nameSpace && type.majorVersion() == majorVersion) return true; + } return false; } -void QQmlMetaType::protectNamespace(const QString &uri) +class QQmlMetaTypeRegistrationFailureRecorder +{ + Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) +public: + QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) + : data(data) + { + data->setTypeRegistrationFailures(failures); + } + + ~QQmlMetaTypeRegistrationFailureRecorder() + { + data->setTypeRegistrationFailures(nullptr); + } + + QQmlMetaTypeData *data = nullptr; +}; + + +bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath, + const QString &uri, const QString &typeNamespace, int vmaj, + QList<QQmlError> *errors) { - QQmlMetaTypeData *data = metaTypeData(); + QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance); + if (!iface) { + if (errors) { + QQmlError error; + error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement " + "QQmlTypesExtensionInterface").arg(typeNamespace)); + errors->prepend(error); + } + return false; + } + + if (!typeNamespace.isEmpty() && typeNamespace != uri) { + // This is an 'identified' module + // The namespace for type registrations must match the URI for locating the module + if (errors) { + QQmlError error; + error.setDescription( + QStringLiteral("Module namespace '%1' does not match import URI '%2'") + .arg(typeNamespace).arg(uri)); + errors->prepend(error); + } + return false; + } + + QStringList failures; + QQmlMetaTypeDataPtr data; + { + QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); + if (!typeNamespace.isEmpty()) { + // This is an 'identified' module + if (namespaceContainsRegistrations(data, typeNamespace, vmaj)) { + // Other modules have already installed to this namespace + if (errors) { + QQmlError error; + error.setDescription(QStringLiteral("Namespace '%1' has already been used " + "for type registration") + .arg(typeNamespace)); + errors->prepend(error); + } + return false; + } + + data->protectedNamespaces.insert(uri); + } else { + // This is not an identified module - provide a warning + qWarning().nospace() << qPrintable( + QStringLiteral("Module '%1' does not contain a module identifier directive - " + "it cannot be protected from external registrations.").arg(uri)); + } + + if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { + // basepath should point to the directory of the module, not the plugin file itself: + QQmlExtensionPluginPrivate::get(plugin)->baseUrl + = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); + } + + data->typeRegistrationNamespace = typeNamespace; + const QByteArray bytes = uri.toUtf8(); + const char *moduleId = bytes.constData(); + iface->registerTypes(moduleId); + data->typeRegistrationNamespace.clear(); + } + + if (!failures.isEmpty()) { + if (errors) { + for (const QString &failure : qAsConst(failures)) { + QQmlError error; + error.setDescription(failure); + errors->prepend(error); + } + } + return false; + } - data->protectedNamespaces.insert(uri); + return true; } -void QQmlMetaType::setTypeRegistrationNamespace(const QString &uri) +/* + \internal + + Fetches the QQmlType instance registered for \a urlString, creating a + registration for it if it is not already registered, using the associated + \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion + details. + + Errors (if there are any) are placed into \a errors, if it is nonzero. + Otherwise errors are printed as warnings. +*/ +QQmlType QQmlMetaType::typeForUrl(const QString &urlString, + const QHashedStringRef &qualifiedType, + bool isCompositeSingleton, QList<QQmlError> *errors, + int majorVersion, int minorVersion) { - QQmlMetaTypeData *data = metaTypeData(); + // ### unfortunate (costly) conversion + const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString)); - data->typeRegistrationNamespace = uri; + QQmlMetaTypeDataPtr data; + { + QQmlType ret(data->urlToType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } + { + QQmlType ret(data->urlToNonFileImportType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } + + const int dot = qualifiedType.indexOf(QLatin1Char('.')); + const QString typeName = dot < 0 + ? qualifiedType.toString() + : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); + + QStringList failures; + QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); + + // Register the type. Note that the URI parameters here are empty; for + // file type imports, we do not place them in a URI as we don't + // necessarily have a good and unique one (picture a library import, + // which may be found in multiple plugin locations on disk), but there + // are other reasons for this too. + // + // By not putting them in a URI, we prevent the types from being + // registered on a QQmlTypeModule; this is important, as once types are + // placed on there, they cannot be easily removed, meaning if the + // developer subsequently loads a different import (meaning different + // types) with the same URI (using, say, a different plugin path), it is + // very undesirable that we continue to associate the types from the + // "old" URI with that new module. + // + // Not having URIs also means that the types cannot be found by name + // etc, the only way to look them up is through QQmlImports -- for + // better or worse. + const QQmlType::RegistrationType registrationType = isCompositeSingleton + ? QQmlType::CompositeSingletonType + : QQmlType::CompositeType; + if (checkRegistration(registrationType, data, nullptr, typeName, majorVersion)) { + auto *priv = new QQmlTypePrivate(registrationType); + priv->setName(QString(), typeName); + priv->version_maj = majorVersion; + priv->version_min = minorVersion; + + if (isCompositeSingleton) { + priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; + priv->extraData.sd->singletonInstanceInfo->url = url; + priv->extraData.sd->singletonInstanceInfo->typeName = typeName; + } else { + priv->extraData.fd->url = url; + } + + data->registerType(priv); + addTypeToData(priv, data); + data->urlToType.insertMulti(url, priv); + return QQmlType(priv); + } + + // This means that the type couldn't be found by URL, but could not be + // registered either, meaning we most likely were passed some kind of bad + // data. + if (errors) { + QQmlError error; + error.setDescription(failures.join('\n')); + errors->prepend(error); + } else { + qWarning("%s", failures.join('\n').toLatin1().constData()); + } + return QQmlType(); } -QMutex *QQmlMetaType::typeRegistrationLock() +QRecursiveMutex *QQmlMetaType::typeRegistrationLock() { return metaTypeDataLock(); } @@ -2062,8 +821,7 @@ QMutex *QQmlMetaType::typeRegistrationLock() */ bool QQmlMetaType::isAnyModule(const QString &uri) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); iter != data->uriToModule.cend(); ++iter) { @@ -2079,14 +837,13 @@ bool QQmlMetaType::isAnyModule(const QString &uri) */ bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlMetaTypeData::VersionedUri versionedUri; versionedUri.uri = uri; versionedUri.majorVersion = majVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) - return QQmlTypeModulePrivate::get(qqtm)->locked; + return qqtm->isLocked(); return false; } @@ -2100,9 +857,7 @@ bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor) { Q_ASSERT(versionMajor >= 0 && versionMinor >= 0); - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // first, check Types QQmlTypeModule *tm = @@ -2115,15 +870,13 @@ bool QQmlMetaType::isModule(const QString &module, int versionMajor, int version QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->uriToModule.value(QQmlMetaTypeData::VersionedUri(uri, majorVersion)); } QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->parentFunctions; } @@ -2144,8 +897,7 @@ bool QQmlMetaType::isQObject(int userType) if (userType == QMetaType::QObjectStar) return true; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); } @@ -2154,8 +906,7 @@ bool QQmlMetaType::isQObject(int userType) */ int QQmlMetaType::listType(int id) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QHash<int, int>::ConstIterator iter = data->qmlLists.constFind(id); if (iter != data->qmlLists.cend()) return *iter; @@ -2166,10 +917,10 @@ int QQmlMetaType::listType(int id) return 0; } +#if QT_DEPRECATED_SINCE(5, 14) int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; for (auto it = data->metaObjectToType.constFind(mo), end = data->metaObjectToType.constEnd(); it != end && it.key() == mo; ++it) { @@ -2185,16 +936,15 @@ QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePr { if (id < 0) return nullptr; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; return data->types.at(id).attachedPropertiesFunction(engine); } +#endif QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, const QMetaObject *mo) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; QQmlType type(data->metaObjectToType.value(mo)); return type.attachedPropertiesFunction(engine); @@ -2259,8 +1009,7 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) if (userType == QMetaType::QObjectStar) return Object; - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; if (data->qmlLists.contains(userType)) return List; else if (userType < data->objects.size() && data->objects.testBit(userType)) @@ -2276,17 +1025,20 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) */ bool QQmlMetaType::isInterface(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); } const char *QQmlMetaType::interfaceIId(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - QQmlType type(data->idToType.value(userType)); - lock.unlock(); + + QQmlTypePrivate *typePrivate = nullptr; + { + QQmlMetaTypeDataPtr data; + typePrivate = data->idToType.value(userType); + } + + QQmlType type(typePrivate); if (type.isInterface() && type.typeId() == userType) return type.interfaceIId(); else @@ -2295,8 +1047,7 @@ const char *QQmlMetaType::interfaceIId(int userType) bool QQmlMetaType::isList(int userType) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; if (data->qmlLists.contains(userType)) return true; return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); @@ -2319,9 +1070,7 @@ bool QQmlMetaType::isList(int userType) */ void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter) { - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; if (data->stringConverters.contains(type)) return; data->stringConverters.insert(type, converter); @@ -2333,9 +1082,7 @@ void QQmlMetaType::registerCustomStringConverter(int type, StringConverter conve */ QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) { - QMutexLocker lock(metaTypeDataLock()); - - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; return data->stringConverters.value(type); } @@ -2362,8 +1109,7 @@ QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); while (it != data->nameToType.cend() && it.key() == name) { @@ -2383,9 +1129,7 @@ QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedString */ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - + const QQmlMetaTypeDataPtr data; return QQmlType(data->metaObjectToType.value(metaObject)); } @@ -2397,8 +1141,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); while (it != data->metaObjectToType.cend() && it.key() == metaObject) { @@ -2418,8 +1161,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin */ QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; if (category == TypeIdCategory::MetaType) { QQmlTypePrivate *type = data->idToType.value(typeId); @@ -2442,8 +1184,7 @@ QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports /* = false */) { const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl); - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QQmlType type(data->urlToType.value(url)); if (!type.isValid() && includeNonFileImports) @@ -2455,221 +1196,87 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI return QQmlType(); } -QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion) -{ - if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) - return rv; - - if (!metaObject->superClass()) { - QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject); - propertyCaches.insert(metaObject, rv); - return rv; - } - QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion); - QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion); - propertyCaches.insert(metaObject, rv); - return rv; -} - QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject, int minorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // not const: the cache is created on demand return data->propertyCache(metaObject, minorVersion); } -QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion) -{ - Q_ASSERT(type.isValid()); - - if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion)) - return pc; - - QVector<QQmlType> types; - - int maxMinorVersion = 0; - - const QMetaObject *metaObject = type.metaObject(); - - while (metaObject) { - QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); - if (t.isValid()) { - maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); - types << t; - } else { - types << QQmlType(); - } - - metaObject = metaObject->superClass(); - } - - if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) { - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc); - return pc; - } - - QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion); - - bool hasCopied = false; - - for (int ii = 0; ii < types.count(); ++ii) { - QQmlType currentType = types.at(ii); - if (!currentType.isValid()) - continue; - - int rev = currentType.metaObjectRevision(); - int moIndex = types.count() - 1 - ii; - - if (raw->allowedRevisionCache[moIndex] != rev) { - if (!hasCopied) { - raw = raw->copy(); - hasCopied = true; - } - raw->allowedRevisionCache[moIndex] = rev; - } - } - - // Test revision compatibility - the basic rule is: - // * Anything that is excluded, cannot overload something that is not excluded * - - // Signals override: - // * other signals and methods of the same name. - // * properties named on<Signal Name> - // * automatic <property name>Changed notify signals - - // Methods override: - // * other methods of the same name - - // Properties override: - // * other elements of the same name - -#if 0 - bool overloadError = false; - QString overloadName; - - for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); - !overloadError && iter != raw->stringCache.end(); - ++iter) { - - QQmlPropertyData *d = *iter; - if (raw->isAllowedInRevision(d)) - continue; // Not excluded - no problems - - // check that a regular "name" overload isn't happening - QQmlPropertyData *current = d; - while (!overloadError && current) { - current = d->overrideData(current); - if (current && raw->isAllowedInRevision(current)) - overloadError = true; - } - } - - if (overloadError) { - if (hasCopied) raw->release(); - - error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); - return 0; - } -#endif - - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw); - - if (hasCopied) - raw->release(); - - if (minorVersion != maxMinorVersion) - const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw); - - return raw; -} - QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // not const: the cache is created on demand return data->propertyCache(type, minorVersion); } -void qmlUnregisterType(int typeIndex) +void QQmlMetaType::unregisterType(int typeIndex) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - { - const QQmlType type = data->types.value(typeIndex); - const QQmlTypePrivate *d = type.priv(); - if (d) { - removeQQmlTypePrivate(data->idToType, d); - removeQQmlTypePrivate(data->nameToType, d); - removeQQmlTypePrivate(data->urlToType, d); - removeQQmlTypePrivate(data->urlToNonFileImportType, d); - removeQQmlTypePrivate(data->metaObjectToType, d); - for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { - QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); - modulePrivate->remove(d); - } - data->types[typeIndex] = QQmlType(); - data->undeletableTypes.remove(type); - } + QQmlMetaTypeDataPtr data; + const QQmlType type = data->types.value(typeIndex); + if (const QQmlTypePrivate *d = type.priv()) { + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + for (auto & module : data->uriToModule) + module->remove(d); + data->clearPropertyCachesForMinorVersion(typeIndex); + data->types[typeIndex] = QQmlType(); + data->undeletableTypes.remove(type); } } void QQmlMetaType::freeUnusedTypesAndCaches() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; // in case this is being called during program exit, `data` might be destructed already - if (!data) + if (!data.isValid()) return; - { - bool deletedAtLeastOneType; - do { - deletedAtLeastOneType = false; - QList<QQmlType>::Iterator it = data->types.begin(); - while (it != data->types.end()) { - const QQmlTypePrivate *d = (*it).priv(); - if (d && d->refCount == 1) { - deletedAtLeastOneType = true; - - removeQQmlTypePrivate(data->idToType, d); - removeQQmlTypePrivate(data->nameToType, d); - removeQQmlTypePrivate(data->urlToType, d); - removeQQmlTypePrivate(data->urlToNonFileImportType, d); - removeQQmlTypePrivate(data->metaObjectToType, d); - - for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { - QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); - modulePrivate->remove(d); - } - - *it = QQmlType(); - } else { - ++it; - } + bool deletedAtLeastOneType; + do { + deletedAtLeastOneType = false; + QList<QQmlType>::Iterator it = data->types.begin(); + while (it != data->types.end()) { + const QQmlTypePrivate *d = (*it).priv(); + if (d && d->count() == 1) { + deletedAtLeastOneType = true; + + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + + for (auto &module : data->uriToModule) + module->remove(d); + + data->clearPropertyCachesForMinorVersion(d->index); + *it = QQmlType(); + } else { + ++it; } - } while (deletedAtLeastOneType); - } - - { - bool deletedAtLeastOneCache; - do { - deletedAtLeastOneCache = false; - QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); - while (it != data->propertyCaches.end()) { - - if ((*it)->count() == 1) { - QQmlPropertyCache *pc = nullptr; - qSwap(pc, *it); - it = data->propertyCaches.erase(it); - pc->release(); - deletedAtLeastOneCache = true; - } else { - ++it; - } + } + } while (deletedAtLeastOneType); + + bool deletedAtLeastOneCache; + do { + deletedAtLeastOneCache = false; + QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); + while (it != data->propertyCaches.end()) { + + if ((*it)->count() == 1) { + QQmlPropertyCache *pc = nullptr; + qSwap(pc, *it); + it = data->propertyCaches.erase(it); + pc->release(); + deletedAtLeastOneCache = true; + } else { + ++it; } - } while (deletedAtLeastOneCache); - } + } + } while (deletedAtLeastOneCache); } /*! @@ -2677,8 +1284,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches() */ QList<QString> QQmlMetaType::qmlTypeNames() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QString> names; names.reserve(data->nameToType.count()); @@ -2697,8 +1303,7 @@ QList<QString> QQmlMetaType::qmlTypeNames() */ QList<QQmlType> QQmlMetaType::qmlTypes() { - QMutexLocker lock(metaTypeDataLock()); - const QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QQmlType> types; for (QQmlTypePrivate *t : data->nameToType) @@ -2712,9 +1317,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes() */ QList<QQmlType> QQmlMetaType::qmlAllTypes() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - + const QQmlMetaTypeDataPtr data; return data->types; } @@ -2723,8 +1326,7 @@ QList<QQmlType> QQmlMetaType::qmlAllTypes() */ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; QList<QQmlType> retn; for (const auto t : qAsConst(data->nameToType)) { @@ -2737,13 +1339,12 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeDataPtr data; for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { QString error; - if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &error)) { qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; if (status) *status = CachedUnitLookupError::VersionMismatch; @@ -2763,15 +1364,13 @@ const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUr void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit.prepend(handler); } void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) { - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + QQmlMetaTypeDataPtr data; data->lookupCachedQmlUnit.removeAll(handler); } @@ -2817,4 +1416,38 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) return typeName; } +QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo, + const QMetaObject *baseMetaObject, + QMetaObject *lastMetaObject) +{ + QList<QQmlProxyMetaObject::ProxyData> metaObjects; + mo = mo->d.superdata; + + const QQmlMetaTypeDataPtr data; + + while (mo) { + QQmlTypePrivate *t = data->metaObjectToType.value(mo); + if (t) { + if (t->regType == QQmlType::CppType) { + if (t->extraData.cd->extFunc) { + QMetaObjectBuilder builder; + clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = baseMetaObject; + if (!metaObjects.isEmpty()) + metaObjects.constLast().metaObject->d.superdata = mmo; + else if (lastMetaObject) + lastMetaObject->d.superdata = mmo; + QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; + metaObjects << data; + } + } + } + mo = mo->d.superdata; + } + + return metaObjects; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index abc79e50e2..6c2b0bb2a6 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -51,40 +51,44 @@ // We mean it. // -#include "qqml.h" #include <private/qtqmlglobal_p.h> - -#include <QtCore/qglobal.h> -#include <QtCore/qvariant.h> -#include <QtCore/qbitarray.h> -#include <QtQml/qjsvalue.h> +#include <private/qqmltype_p.h> +#include <private/qqmlproxymetaobject_p.h> QT_BEGIN_NAMESPACE -class QQmlType; -class QQmlEngine; -class QQmlEnginePrivate; -class QQmlCustomParser; -class QQmlTypePrivate; class QQmlTypeModule; -class QHashedString; -class QHashedStringRef; -class QMutex; -class QQmlPropertyCache; -class QQmlCompiledData; - -namespace QV4 { struct String; } +class QRecursiveMutex; +class QQmlError; -void Q_QML_PRIVATE_EXPORT qmlUnregisterType(int type); +namespace QV4 { class ExecutableCompilationUnit; } class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: + static QQmlType registerType(const QQmlPrivate::RegisterType &type); + static QQmlType registerInterface(const QQmlPrivate::RegisterInterface &type); + static QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type); static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type); static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type); + static bool registerPluginTypes(QObject *instance, const QString &basePath, + const QString &uri, const QString &typeNamespace, int vmaj, + QList<QQmlError> *errors); + static QQmlType typeForUrl(const QString &urlString, const QHashedStringRef& typeName, + bool isCompositeSingleton, QList<QQmlError> *errors, + int majorVersion = -1, int minorVersion = -1); + + static void unregisterType(int type); + + static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); + static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); - static void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); - static void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + static void registerModule(const char *uri, int versionMajor, int versionMinor); + static bool protectModule(const char *uri, int majVersion); + + static int typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName); + + static void registerUndeletableType(const QQmlType &dtype); static QList<QString> qmlTypeNames(); static QList<QQmlType> qmlTypes(); @@ -117,8 +121,12 @@ public: static QObject *toQObject(const QVariant &, bool *ok = nullptr); static int listType(int); - static int attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *); - static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, int); +#if QT_DEPRECATED_SINCE(5, 14) + static QT_DEPRECATED int attachedPropertiesFuncId(QQmlEnginePrivate *engine, + const QMetaObject *); + static QT_DEPRECATED QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, + int); +#endif static QQmlAttachedPropertiesFunc attachedPropertiesFunc(QQmlEnginePrivate *, const QMetaObject *); @@ -152,228 +160,38 @@ public: static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); - static bool namespaceContainsRegistrations(const QString &, int majorVersion); - - static void protectNamespace(const QString &); - - static void setTypeRegistrationNamespace(const QString &); - - static QMutex *typeRegistrationLock(); + static QRecursiveMutex *typeRegistrationLock(); static QString prettyTypeName(const QObject *object); -}; -struct QQmlMetaTypeData; -class QHashedCStringRef; -class QQmlPropertyCache; -class Q_QML_PRIVATE_EXPORT QQmlType -{ -public: - QQmlType(); - QQmlType(const QQmlType &other); - QQmlType &operator =(const QQmlType &other); - explicit QQmlType(QQmlTypePrivate *priv); - ~QQmlType(); - - bool operator ==(const QQmlType &other) const { - return d == other.d; + template <typename QQmlTypeContainer> + static void removeQQmlTypePrivate(QQmlTypeContainer &container, + const QQmlTypePrivate *reference) + { + for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) { + if (*it == reference) + it = container.erase(it); + else + ++it; + } } - bool isValid() const { return d != nullptr; } - const QQmlTypePrivate *key() const { return d; } + static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent); + static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function); - QByteArray typeName() const; - QString qmlTypeName() const; - QString elementName() const; + static int registerUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration); + static void clearTypeRegistrations(); - QHashedString module() const; - int majorVersion() const; - int minorVersion() const; - - bool availableInVersion(int vmajor, int vminor) const; - bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const; - - QObject *create() const; - void create(QObject **, void **, size_t) const; - - typedef void (*CreateFunc)(void *); - CreateFunc createFunction() const; - QQmlCustomParser *customParser() const; - - bool isCreatable() const; - typedef QObject *(*ExtensionFunc)(QObject *); - ExtensionFunc extensionFunction() const; - bool isExtendedType() const; - QString noCreationReason() const; - - bool isSingleton() const; - bool isInterface() const; - bool isComposite() const; - bool isCompositeSingleton() const; - - int typeId() const; - int qListTypeId() const; - - const QMetaObject *metaObject() const; - const QMetaObject *baseMetaObject() const; - int metaObjectRevision() const; - bool containsRevisionedAttributes() const; - - QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; - const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; - int attachedPropertiesId(QQmlEnginePrivate *engine) const; - - int parserStatusCast() const; - const char *interfaceIId() const; - int propertyValueSourceCast() const; - int propertyValueInterceptorCast() const; - - int index() const; - - class Q_QML_PRIVATE_EXPORT SingletonInstanceInfo - { - public: - SingletonInstanceInfo() - : scriptCallback(nullptr), qobjectCallback(nullptr), instanceMetaObject(nullptr) {} - - QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *); - QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *); - const QMetaObject *instanceMetaObject; - QString typeName; - QUrl url; // used by composite singletons - - void setQObjectApi(QQmlEngine *, QObject *); - QObject *qobjectApi(QQmlEngine *) const; - void setScriptApi(QQmlEngine *, const QJSValue &); - QJSValue scriptApi(QQmlEngine *) const; - - void init(QQmlEngine *); - void destroy(QQmlEngine *); - - QHash<QQmlEngine *, QJSValue> scriptApis; - QHash<QQmlEngine *, QObject *> qobjectApis; - }; - SingletonInstanceInfo *singletonInstanceInfo() const; - - QUrl sourceUrl() const; - - int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; - int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; - int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - - int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const; - int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const; - int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const; - int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const; - int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const; - - QQmlTypePrivate *priv() const { return d; } - static void refHandle(QQmlTypePrivate *priv); - static void derefHandle(QQmlTypePrivate *priv); - static int refCount(QQmlTypePrivate *priv); - - enum RegistrationType { - CppType = 0, - SingletonType = 1, - InterfaceType = 2, - CompositeType = 3, - CompositeSingletonType = 4, - AnyRegistrationType = 255 - }; + static QList<QQmlProxyMetaObject::ProxyData> proxyData(const QMetaObject *mo, + const QMetaObject *baseMetaObject, + QMetaObject *lastMetaObject); -private: - QQmlType superType() const; - QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; - friend class QQmlTypePrivate; - - friend QString registrationTypeString(RegistrationType); - friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &, int); - friend QQmlType registerType(const QQmlPrivate::RegisterType &); - friend QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &); - friend QQmlType registerInterface(const QQmlPrivate::RegisterInterface &); - friend int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &); - friend uint qHash(const QQmlType &t, uint seed); - friend Q_QML_EXPORT void qmlClearTypeRegistrations(); - friend class QQmlMetaType; - - QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterSingletonType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeType &); - QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &); - - QQmlTypePrivate *d; + static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd); }; Q_DECLARE_TYPEINFO(QQmlMetaType, Q_MOVABLE_TYPE); - -inline uint qHash(const QQmlType &t, uint seed = 0) { return qHash(reinterpret_cast<quintptr>(t.d), seed); } - - -class QQmlTypeModulePrivate; -class QQmlTypeModule -{ -public: - QString module() const; - int majorVersion() const; - - int minimumMinorVersion() const; - int maximumMinorVersion() const; - - QQmlType type(const QHashedStringRef &, int) const; - QQmlType type(const QV4::String *, int) const; - - void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const; - - QQmlTypeModulePrivate *priv() { return d; } -private: - //Used by register functions and creates the QQmlTypeModule for them - friend QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data); - friend void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data); - friend struct QQmlMetaTypeData; - friend Q_QML_EXPORT void qmlClearTypeRegistrations(); - friend class QQmlTypeModulePrivate; - - QQmlTypeModule(); - ~QQmlTypeModule(); - QQmlTypeModulePrivate *d; -}; - -class QQmlTypeModuleVersion -{ -public: - QQmlTypeModuleVersion(); - QQmlTypeModuleVersion(QQmlTypeModule *, int); - QQmlTypeModuleVersion(const QQmlTypeModuleVersion &); - QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &); - - QQmlTypeModule *module() const; - int minorVersion() const; - - QQmlType type(const QHashedStringRef &) const; - QQmlType type(const QV4::String *) const; - -private: - QQmlTypeModule *m_module; - int m_minor; -}; - -class Q_AUTOTEST_EXPORT QQmlMetaTypeRegistrationFailureRecorder -{ - QStringList _failures; - -public: - QQmlMetaTypeRegistrationFailureRecorder(); - ~QQmlMetaTypeRegistrationFailureRecorder(); - - QStringList failures() const - { return _failures; } -}; - QT_END_NAMESPACE #endif // QQMLMETATYPE_P_H diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp new file mode 100644 index 0000000000..775bc8bdb4 --- /dev/null +++ b/src/qml/qml/qqmlmetatypedata.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** 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 "qqmlmetatypedata_p.h" + +#include <private/qqmltype_p_p.h> +#include <private/qqmltypemodule_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +QQmlMetaTypeData::QQmlMetaTypeData() +{ +} + +QQmlMetaTypeData::~QQmlMetaTypeData() +{ + for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) + delete *i; + for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); + it != end; ++it) + (*it)->release(); + + // Do this before the attached properties disappear. + types.clear(); + undeletableTypes.clear(); +} + +// This expects a "fresh" QQmlTypePrivate and adopts its reference. +void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) +{ + for (int i = 0; i < types.count(); ++i) { + if (!types.at(i).isValid()) { + types[i] = QQmlType(priv); + priv->index = i; + priv->release(); + return; + } + } + types.append(QQmlType(priv)); + priv->index = types.count() - 1; + priv->release(); +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const +{ + return (index < typePropertyCaches.length()) + ? typePropertyCaches.at(index).value(minorVersion).data() + : nullptr; +} + +void QQmlMetaTypeData::setPropertyCacheForMinorVersion(int index, int minorVersion, + QQmlPropertyCache *cache) +{ + if (index >= typePropertyCaches.length()) + typePropertyCaches.resize(index + 1); + typePropertyCaches[index][minorVersion] = cache; +} + +void QQmlMetaTypeData::clearPropertyCachesForMinorVersion(int index) +{ + if (index < typePropertyCaches.length()) + typePropertyCaches[index].clear(); +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion) +{ + if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) + return rv; + + if (!metaObject->superClass()) { + QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject); + propertyCaches.insert(metaObject, rv); + return rv; + } + QQmlPropertyCache *super = propertyCache(metaObject->superClass(), minorVersion); + QQmlPropertyCache *rv = super->copyAndAppend(metaObject, minorVersion); + propertyCaches.insert(metaObject, rv); + return rv; +} + +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion) +{ + Q_ASSERT(type.isValid()); + + if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion)) + return pc; + + QVector<QQmlType> types; + + int maxMinorVersion = 0; + + const QMetaObject *metaObject = type.metaObject(); + + while (metaObject) { + QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); + if (t.isValid()) { + maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); + types << t; + } else { + types << QQmlType(); + } + + metaObject = metaObject->superClass(); + } + + if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) { + setPropertyCacheForMinorVersion(type.index(), minorVersion, pc); + return pc; + } + + QQmlPropertyCache *raw = propertyCache(type.metaObject(), minorVersion); + + bool hasCopied = false; + + for (int ii = 0; ii < types.count(); ++ii) { + const QQmlType ¤tType = types.at(ii); + if (!currentType.isValid()) + continue; + + int rev = currentType.metaObjectRevision(); + int moIndex = types.count() - 1 - ii; + + if (raw->allowedRevision(moIndex) != rev) { + if (!hasCopied) { + // TODO: The copy should be mutable, and the original should be const + // Considering this, the setAllowedRevision() below does not violate + // the immutability of already published property caches. + raw = raw->copy(); + hasCopied = true; + } + raw->setAllowedRevision(moIndex, rev); + } + } + + // Test revision compatibility - the basic rule is: + // * Anything that is excluded, cannot overload something that is not excluded * + + // Signals override: + // * other signals and methods of the same name. + // * properties named on<Signal Name> + // * automatic <property name>Changed notify signals + + // Methods override: + // * other methods of the same name + + // Properties override: + // * other elements of the same name + +#if 0 + bool overloadError = false; + QString overloadName; + + for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); + !overloadError && iter != raw->stringCache.end(); + ++iter) { + + QQmlPropertyData *d = *iter; + if (raw->isAllowedInRevision(d)) + continue; // Not excluded - no problems + + // check that a regular "name" overload isn't happening + QQmlPropertyData *current = d; + while (!overloadError && current) { + current = d->overrideData(current); + if (current && raw->isAllowedInRevision(current)) + overloadError = true; + } + } + + if (overloadError) { + if (hasCopied) raw->release(); + + error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + return 0; + } +#endif + + setPropertyCacheForMinorVersion(type.index(), minorVersion, raw); + + if (hasCopied) + raw->release(); + + if (minorVersion != maxMinorVersion) + setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw); + + return raw; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h new file mode 100644 index 0000000000..5239b635ce --- /dev/null +++ b/src/qml/qml/qqmlmetatypedata_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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 QQMLMETATYPEDATA_P_H +#define QQMLMETATYPEDATA_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/qqmltype_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qhashedstring_p.h> + +#include <QtCore/qset.h> +#include <QtCore/qvector.h> +#include <QtCore/qbitarray.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypePrivate; +struct QQmlMetaTypeData +{ + QQmlMetaTypeData(); + ~QQmlMetaTypeData(); + void registerType(QQmlTypePrivate *priv); + QList<QQmlType> types; + QSet<QQmlType> undeletableTypes; + typedef QHash<int, QQmlTypePrivate *> Ids; + Ids idToType; + typedef QHash<QHashedStringRef, QQmlTypePrivate *> Names; + Names nameToType; + typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only + Files urlToType; + Files urlToNonFileImportType; // For non-file imported composite and composite + // singleton types. This way we can locate any + // of them by url, even if it was registered as + // a module via QQmlPrivate::RegisterCompositeType + typedef QHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects; + MetaObjects metaObjectToType; + typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; + StringConverters stringConverters; + QVector<QHash<int, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches; + + struct VersionedUri { + VersionedUri() + : majorVersion(0) {} + VersionedUri(const QHashedString &uri, int majorVersion) + : uri(uri), majorVersion(majorVersion) {} + bool operator==(const VersionedUri &other) const { + return other.majorVersion == majorVersion && other.uri == uri; + } + QHashedString uri; + int majorVersion; + }; + typedef QHash<VersionedUri, QQmlTypeModule *> TypeModules; + TypeModules uriToModule; + + QBitArray objects; + QBitArray interfaces; + QBitArray lists; + + QList<QQmlPrivate::AutoParentFunction> parentFunctions; + QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit; + + QSet<QString> protectedNamespaces; + + QString typeRegistrationNamespace; + + QHash<int, int> qmlLists; + + QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches; + + QQmlPropertyCache *propertyCacheForMinorVersion(int index, int minorVersion) const; + void setPropertyCacheForMinorVersion(int index, int minorVersion, QQmlPropertyCache *cache); + void clearPropertyCachesForMinorVersion(int index); + + QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion); + QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); + + void setTypeRegistrationFailures(QStringList *failures) + { + m_typeRegistrationFailures = failures; + } + + void recordTypeRegFailure(const QString &message) + { + if (m_typeRegistrationFailures) + m_typeRegistrationFailures->append(message); + else + qWarning("%s", message.toUtf8().constData()); + } + +private: + QStringList *m_typeRegistrationFailures = nullptr; +}; + +inline uint qHash(const QQmlMetaTypeData::VersionedUri &v) +{ + return v.uri.hash() ^ qHash(v.majorVersion); +} + +QT_END_NAMESPACE + +#endif // QQMLMETATYPEDATA_P_H diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index 0706b8c0cf..1359586b31 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -118,8 +118,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine disconnect(); Q_ASSERT(engine); - if (QObjectPrivate::get(source)->threadData->threadId.load() != - QObjectPrivate::get(engine)->threadData->threadId.load()) { + if (QObjectPrivate::get(source)->threadData->threadId.loadRelaxed() != + QObjectPrivate::get(engine)->threadData->threadId.loadRelaxed()) { QString sourceName; QDebug(&sourceName) << source; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index e45515fbcf..a4270628e8 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,7 +55,9 @@ #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 <private/qv4generatorobject_p.h> #include <qtqml_tracepoints_p.h> @@ -73,7 +75,7 @@ struct ActiveOCRestorer }; } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator) : phase(Startup) , compilationUnit(compilationUnit) @@ -100,7 +102,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR } } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compilationUnit(compilationUnit) , propertyCaches(&compilationUnit->propertyCaches) @@ -373,7 +375,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const propertyType = QMetaType::Int; } else { // ### This should be resolved earlier at compile time and the binding value should be changed accordingly. - QVariant value = binding->valueAsString(compilationUnit.data()); + QVariant value = compilationUnit->bindingValueAsString(binding); bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context); Q_ASSERT(ok); Q_UNUSED(ok); @@ -406,7 +408,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const switch (propertyType) { case QMetaType::QVariant: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) == n) { if (property->isVarProperty()) { _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromInt32(int(n))); @@ -438,11 +440,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const property->writeProperty(_qobject, &nullValue, propertyWriteFlags); } } else { - QString stringValue = binding->valueAsString(compilationUnit.data()); + QString stringValue = compilationUnit->bindingValueAsString(binding); if (property->isVarProperty()) { QV4::ScopedString s(scope, v4->newString(stringValue)); _vmeMetaObject->setVMEProperty(property->coreIndex(), s); } else { + // ### Qt 6: Doing the conversion here where we don't know the eventual target type is rather strange + // and caused for instance QTBUG-78943 QVariant value = QQmlStringConverters::variantFromString(stringValue); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -451,25 +455,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::String: { assertOrNull(binding->evaluatesToString()); - QString value = binding->valueAsString(compilationUnit.data()); + QString value = compilationUnit->bindingValueAsString(binding); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::StringList: { assertOrNull(binding->evaluatesToString()); - QStringList value(binding->valueAsString(compilationUnit.data())); + QStringList value(compilationUnit->bindingValueAsString(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::ByteArray: { assertType(QV4::CompiledData::Binding::Type_String); - QByteArray value(binding->valueAsString(compilationUnit.data()).toUtf8()); + QByteArray value(compilationUnit->bindingValueAsString(binding).toUtf8()); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Url: { assertType(QV4::CompiledData::Binding::Type_String); - QString string = binding->valueAsString(compilationUnit.data()); + QString string = compilationUnit->bindingValueAsString(binding); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(string)); @@ -481,7 +485,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::UInt: { assertType(QV4::CompiledData::Binding::Type_Number); - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); uint value = uint(d); property->writeProperty(_qobject, &value, propertyWriteFlags); break; @@ -489,7 +493,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QVariant::Int: { assertType(QV4::CompiledData::Binding::Type_Number); - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); int value = int(d); property->writeProperty(_qobject, &value, propertyWriteFlags); break; @@ -497,19 +501,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; case QMetaType::Float: { assertType(QV4::CompiledData::Binding::Type_Number); - float value = float(binding->valueAsNumber(compilationUnit->constants)); + float value = float(compilationUnit->bindingValueAsNumber(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Double: { assertType(QV4::CompiledData::Binding::Type_Number); - double value = binding->valueAsNumber(compilationUnit->constants); + double value = compilationUnit->bindingValueAsNumber(binding); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Color: { bool ok = false; - uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); + uint colorValue = QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); struct { void *data[4]; } buffer; if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { @@ -520,21 +524,21 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const #if QT_CONFIG(datestring) case QVariant::Date: { bool ok = false; - QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); + QDate value = QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Time: { bool ok = false; - QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QTime value = QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::DateTime: { bool ok = false; - QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QDateTime value = QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); // ### VME compatibility :( { const qint64 date = value.date().toJulianDay(); @@ -548,42 +552,42 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const #endif // datestring case QVariant::Point: { bool ok = false; - QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok).toPoint(); + QPoint value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok).toPoint(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::PointF: { bool ok = false; - QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QPointF value = QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Size: { bool ok = false; - QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok).toSize(); + QSize value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok).toSize(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::SizeF: { bool ok = false; - QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QSizeF value = QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Rect: { bool ok = false; - QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok).toRect(); + QRect value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok).toRect(); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::RectF: { bool ok = false; - QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QRectF value = QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -599,7 +603,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float xp; float yp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -611,7 +615,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float yp; float zy; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -624,7 +628,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float zy; float wp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -637,7 +641,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float yp; float zp; } vec; - bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); + bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec)); assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); @@ -651,12 +655,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const if (property->propType() == qMetaTypeId<QList<qreal> >()) { assertType(QV4::CompiledData::Binding::Type_Number); QList<qreal> value; - value.append(binding->valueAsNumber(compilationUnit->constants)); + value.append(compilationUnit->bindingValueAsNumber(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<int> >()) { assertType(QV4::CompiledData::Binding::Type_Number); - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); QList<int> value; value.append(int(n)); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -669,7 +673,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { assertType(QV4::CompiledData::Binding::Type_String); - QString urlString = binding->valueAsString(compilationUnit.data()); + QString urlString = compilationUnit->bindingValueAsString(binding); QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(urlString)); QList<QUrl> value; @@ -679,7 +683,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (property->propType() == qMetaTypeId<QList<QString> >()) { assertOrNull(binding->evaluatesToString()); QList<QString> value; - value.append(binding->valueAsString(compilationUnit.data())); + value.append(compilationUnit->bindingValueAsString(binding)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QJSValue>()) { @@ -687,7 +691,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = QJSValue(binding->valueAsBoolean()); } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) == n) { value = QJSValue(int(n)); } else @@ -695,14 +699,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { value = QJSValue::NullValue; } else { - value = QJSValue(binding->valueAsString(compilationUnit.data())); + value = QJSValue(compilationUnit->bindingValueAsString(binding)); } property->writeProperty(_qobject, &value, propertyWriteFlags); break; } // otherwise, try a custom type assignment - QString stringValue = binding->valueAsString(compilationUnit.data()); + QString stringValue = compilationUnit->bindingValueAsString(binding); QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); Q_ASSERT(converter); QVariant value = (*converter)(stringValue); @@ -735,7 +739,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(); @@ -816,7 +820,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); - QV4::CompiledData::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex); + QV4::ResolvedTypeReference *tr = resolvedType(binding->propertyNameIndex); Q_ASSERT(tr); QQmlType attachedType = tr->type; if (!attachedType.isValid()) { @@ -835,13 +839,14 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper // ### resolve this at compile time if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) { - QQmlScriptString ss(binding->valueAsScriptString(compilationUnit.data()), context->asQQmlContext(), _scopeObject); + QQmlScriptString ss(compilationUnit->bindingValueAsScriptString(binding), + context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; ss.d.data()->columnNumber = binding->location.column; ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number; - ss.d.data()->numberValue = binding->valueAsNumber(compilationUnit->constants); + ss.d.data()->numberValue = compilationUnit->bindingValueAsNumber(binding); QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite; @@ -938,7 +943,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper auto bindingTarget = _bindingTarget; auto valueTypeProperty = _valueTypeProperty; - auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) mutable -> bool { if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) return false; @@ -1133,7 +1138,10 @@ void QQmlObjectCreator::setupFunctions() if (!property->isVMEFunction()) continue; - function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); + if (runtimeFunction->isGenerator()) + function = QV4::GeneratorFunction::create(qmlContext, runtimeFunction); + else + function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction); _vmeMetaObject->setVmeMethod(property->coreIndex(), function); } } @@ -1184,8 +1192,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo instance = component; ddata = QQmlData::get(instance, /*create*/true); } else { - QV4::CompiledData::ResolvedTypeReference *typeRef - = resolvedType(obj->inheritedTypeNameIndex); + QV4::ResolvedTypeReference *typeRef = resolvedType(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType type = typeRef->type; @@ -1445,8 +1452,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 5aca60e2f0..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; @@ -85,7 +84,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); + QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); ~QQmlObjectCreator(); QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr); @@ -94,17 +93,17 @@ public: QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); void clear(); - QQmlComponentAttached **componentAttachment() const { return &sharedState->componentAttached; } + QQmlComponentAttached **componentAttachment() { return &sharedState->componentAttached; } - QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() const { return &sharedState->finalizeCallbacks; } + QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() { return &sharedState->finalizeCallbacks; } QList<QQmlError> errors; QQmlContextData *parentContextData() const { return parentContext.contextData(); } - QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; } + QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; } private: - QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); void init(QQmlContextData *parentContext); @@ -125,7 +124,7 @@ private: inline QV4::QmlContext *currentQmlContext(); Q_NEVER_INLINE void createQmlContext(); - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return compilationUnit->resolvedType(id); } @@ -141,7 +140,7 @@ private: QQmlEngine *engine; QV4::ExecutionEngine *v4; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; QQmlContextData *context; diff --git a/src/qml/qml/qqmlobjectorgadget.cpp b/src/qml/qml/qqmlobjectorgadget.cpp new file mode 100644 index 0000000000..1d4916d7d1 --- /dev/null +++ b/src/qml/qml/qqmlobjectorgadget.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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 "qqmlobjectorgadget_p.h" + +QT_BEGIN_NAMESPACE + +void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const +{ + if (ptr.isNull()) { + const QMetaObject *metaObject = _m.asT2(); + metaObject->d.static_metacall(nullptr, type, index, argv); + } + else if (ptr.isT1()) { + QMetaObject::metacall(ptr.asT1(), type, index, argv); + } + else { + const QMetaObject *metaObject = _m.asT1()->metaObject(); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); + metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/qml/qqmlobjectorgadget_p.h index bcd079adef..c5f5f58a3a 100644 --- a/src/qml/util/qqmllistaccessor_p.h +++ b/src/qml/qml/qqmlobjectorgadget_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLLISTACCESSOR_H -#define QQMLLISTACCESSOR_H +#ifndef QQMLOBJECTORGADGET_P_H +#define QQMLOBJECTORGADGET_P_H // // W A R N I N G @@ -51,33 +51,33 @@ // We mean it. // -#include <QtCore/QVariant> +#include <private/qqmlmetaobject_p.h> QT_BEGIN_NAMESPACE -class QQmlEngine; -class Q_AUTOTEST_EXPORT QQmlListAccessor +class QQmlObjectOrGadget: public QQmlMetaObject { public: - QQmlListAccessor(); - ~QQmlListAccessor(); + QQmlObjectOrGadget(QObject *obj) + : QQmlMetaObject(obj), + ptr(obj) + {} + QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget) + : QQmlMetaObject(propertyCache) + , ptr(gadget) + {} - QVariant list() const; - void setList(const QVariant &, QQmlEngine * = nullptr); - - bool isValid() const; - - int count() const; - QVariant at(int) const; - - enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; - Type type() const { return m_type; } + void metacall(QMetaObject::Call type, int index, void **argv) const; private: - Type m_type; - QVariant d; + QBiPointer<QObject, void> ptr; + +protected: + QQmlObjectOrGadget(const QMetaObject* metaObject) + : QQmlMetaObject(metaObject) + {} }; QT_END_NAMESPACE -#endif // QQMLLISTACCESSOR_H +#endif // QQMLOBJECTORGADGET_P_H diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index fc798a2c23..fe0946c6de 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -41,7 +41,6 @@ #include <private/qqmlpropertycache_p.h> #include <private/qqmldata_p.h> #include <private/qmetaobjectbuilder_p.h> -#include <private/qv8engine_p.h> #include <qqmlengine.h> #include <qdebug.h> diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index 8a45de9f76..bfd3611fb8 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -51,11 +51,14 @@ // We mean it. // +#include <functional> + #include <QtQml/qtqmlglobal.h> #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> #include <QtCore/qurl.h> +#include <QPointer> QT_BEGIN_NAMESPACE @@ -276,6 +279,7 @@ namespace QQmlPrivate const QMetaObject *instanceMetaObject; // new in version 1 int typeId; // new in version 2 int revision; // new in version 2 + std::function<QObject*(QQmlEngine *, QJSEngine *)> generalizedQobjectApi; // new in version 3 // If this is extended ensure "version" is bumped!!! }; @@ -319,6 +323,13 @@ namespace QQmlPrivate int Q_QML_EXPORT qmlregister(RegistrationType, void *); void Q_QML_EXPORT qmlunregister(RegistrationType, quintptr); + struct Q_QML_EXPORT RegisterSingletonFunctor + { + QObject *operator()(QQmlEngine *, QJSEngine *); + + QPointer<QObject> m_object; + bool alreadyCalled = false; + }; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index c8166695ba..5f57e0eca1 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1216,10 +1216,18 @@ bool QQmlPropertyPrivate::write(QObject *object, if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) { return property.writeProperty(object, const_cast<void *>(value.constData()), flags); } else if (property.isQObject()) { - QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType); + QVariant val = value; + int varType = variantType; + if (variantType == QMetaType::Nullptr) { + // This reflects the fact that you can assign a nullptr to a QObject pointer + // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject + varType = QMetaType::QObjectStar; + val = QVariant(QMetaType::QObjectStar, nullptr); + } + QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType); if (valMo.isNull()) return false; - QObject *o = *static_cast<QObject *const *>(value.constData()); + QObject *o = *static_cast<QObject *const *>(val.constData()); QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType); if (o) diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 544eab4c7f..285c34d7fa 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -56,14 +56,17 @@ #include <private/qobject_p.h> #include <private/qtqmlglobal_p.h> -#include <private/qqmlpropertycache_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qqmlcontext_p.h> #include <private/qqmlboundsignalexpressionpointer_p.h> +#include <private/qqmlpropertydata_p.h> QT_BEGIN_NAMESPACE class QQmlContext; class QQmlEnginePrivate; class QQmlJavaScriptExpression; +class QQmlMetaObject; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 73bfd7bbaa..69957ab282 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -42,10 +42,10 @@ #include <private/qqmlengine_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlvmemetaobject_p.h> -#include <private/qv8engine_p.h> #include <private/qmetaobject_p.h> #include <private/qmetaobjectbuilder_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> #include <private/qv4value_p.h> @@ -65,21 +65,6 @@ QT_BEGIN_NAMESPACE #define Q_INT16_MAX 32767 -class QQmlPropertyCacheMethodArguments -{ -public: - QQmlPropertyCacheMethodArguments *next; - - //for signal handler rewrites - QString *signalParameterStringForJS; - int parameterError:1; - int argumentsValid:1; - - QList<QByteArray> *names; - - int arguments[1]; -}; - // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick // to load static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p) @@ -113,8 +98,6 @@ static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags) flags.type = QQmlPropertyData::Flags::QmlBindingType; } else if (propType == qMetaTypeId<QJSValue>()) { flags.type = QQmlPropertyData::Flags::QJSValueType; - } else if (propType == qMetaTypeId<QQmlV4Handle>()) { - flags.type = QQmlPropertyData::Flags::V4HandleType; } else { QQmlMetaType::TypeCategory cat = QQmlMetaType::typeCategory(propType); @@ -141,24 +124,27 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p) return flags; } -void QQmlPropertyData::lazyLoad(const QMetaProperty &p) +static void populate(QQmlPropertyData *data, const QMetaProperty &p) { - setCoreIndex(p.propertyIndex()); - setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); Q_ASSERT(p.revision() <= Q_INT16_MAX); - setRevision(p.revision()); - - setFlags(fastFlagsForProperty(p)); + data->setCoreIndex(p.propertyIndex()); + data->setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); + data->setFlags(fastFlagsForProperty(p)); + data->setRevision(p.revision()); +} +void QQmlPropertyData::lazyLoad(const QMetaProperty &p) +{ + populate(this, p); int type = static_cast<int>(p.type()); if (type == QMetaType::QObjectStar) { setPropType(type); - _flags.type = Flags::QObjectDerivedType; + m_flags.type = Flags::QObjectDerivedType; } else if (type == QMetaType::QVariant) { setPropType(type); - _flags.type = Flags::QVariantType; + m_flags.type = Flags::QVariantType; } else if (type == QVariant::UserType || type == -1) { - _flags.notFullyResolved = true; + m_flags.notFullyResolved = true; } else { setPropType(type); } @@ -166,13 +152,9 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) void QQmlPropertyData::load(const QMetaProperty &p) { + populate(this, p); setPropType(p.userType()); - setCoreIndex(p.propertyIndex()); - setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); - setFlags(fastFlagsForProperty(p)); - flagsForPropertyType(propType(), _flags); - Q_ASSERT(p.revision() <= Q_INT16_MAX); - setRevision(p.revision()); + flagsForPropertyType(propType(), m_flags); } void QQmlPropertyData::load(const QMetaMethod &m) @@ -182,23 +164,23 @@ void QQmlPropertyData::load(const QMetaMethod &m) setPropType(m.returnType()); - _flags.type = Flags::FunctionType; - if (m.methodType() == QMetaMethod::Signal) - _flags.isSignal = true; - else if (m.methodType() == QMetaMethod::Constructor) { - _flags.isConstructor = true; + m_flags.type = Flags::FunctionType; + if (m.methodType() == QMetaMethod::Signal) { + m_flags.isSignal = true; + } else if (m.methodType() == QMetaMethod::Constructor) { + m_flags.isConstructor = true; setPropType(QMetaType::QObjectStar); } - if (m.parameterCount()) { - _flags.hasArguments = true; - if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - _flags.isV4Function = true; - } + const int paramCount = m.parameterCount(); + if (paramCount) { + m_flags.hasArguments = true; + if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) + m_flags.isV4Function = true; } if (m.attributes() & QMetaMethod::Cloned) - _flags.isCloned = true; + m_flags.isCloned = true; Q_ASSERT(m.revision() <= Q_INT16_MAX); setRevision(m.revision()); @@ -206,37 +188,14 @@ void QQmlPropertyData::load(const QMetaMethod &m) void QQmlPropertyData::lazyLoad(const QMetaMethod &m) { - setCoreIndex(m.methodIndex()); - setPropType(QMetaType::Void); - setArguments(nullptr); - _flags.type = Flags::FunctionType; - if (m.methodType() == QMetaMethod::Signal) - _flags.isSignal = true; - else if (m.methodType() == QMetaMethod::Constructor) { - _flags.isConstructor = true; - setPropType(QMetaType::QObjectStar); - } + load(m); const char *returnType = m.typeName(); if (!returnType) returnType = "\0"; if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - _flags.notFullyResolved = true; - } - - const int paramCount = m.parameterCount(); - if (paramCount) { - _flags.hasArguments = true; - if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) { - _flags.isV4Function = true; - } + m_flags.notFullyResolved = true; } - - if (m.attributes() & QMetaMethod::Cloned) - _flags.isCloned = true; - - Q_ASSERT(m.revision() <= Q_INT16_MAX); - setRevision(m.revision()); } /*! @@ -361,7 +320,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag data.setArguments(nullptr); QQmlPropertyData handler = data; - handler._flags.isSignalHandler = true; + handler.m_flags.isSignalHandler = true; if (types) { int argumentCount = *types; @@ -389,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); @@ -559,7 +519,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, data->setFlags(methodFlags); data->lazyLoad(m); - data->_flags.isDirect = !dynamicMetaObject; + data->m_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->setMetaObjectOffset(allowedRevisionCache.count() - 1); @@ -567,7 +527,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (data->isSignal()) { sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart]; *sigdata = *data; - sigdata->_flags.isSignalHandler = true; + sigdata->m_flags.isSignalHandler = true; } QQmlPropertyData *old = nullptr; @@ -609,7 +569,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, if (old) { // We only overload methods in the same class, exactly like C++ if (old->isFunction() && old->coreIndex() >= methodOffset) - data->_flags.isOverload = true; + data->m_flags.isOverload = true; data->markAsOverrideOf(old); } @@ -640,7 +600,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, data->lazyLoad(p); data->setTypeMinorVersion(typeMinorVersion); - data->_flags.isDirect = !dynamicMetaObject; + data->m_flags.isDirect = !dynamicMetaObject; Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX); data->setMetaObjectOffset(allowedRevisionCache.count() - 1); @@ -666,7 +626,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, } if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept - data->_flags.isDirect = false; + data->m_flags.isDirect = false; else data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset); if (old) @@ -677,7 +637,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, void QQmlPropertyCache::resolve(QQmlPropertyData *data) const { Q_ASSERT(data->notFullyResolved()); - data->_flags.notFullyResolved = false; + data->m_flags.notFullyResolved = false; const QMetaObject *mo = firstCppMetaObject(); if (data->isFunction()) { @@ -712,7 +672,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); } } - flagsForPropertyType(data->propType(), data->_flags); + flagsForPropertyType(data->propType(), data->m_flags); } } @@ -889,52 +849,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor) setOverrideIndexIsProperty(!predecessor->isFunction()); setOverrideIndex(predecessor->coreIndex()); - predecessor->_flags.isOverridden = true; -} - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &staticQtMetaObject; } -}; - -static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, - const QByteArray &name) -{ - for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { - QMetaEnum m = resolvedMetaObject->enumerator(i); - if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) - return true; - } - return false; -} - -static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) -{ - QByteArray scope; - QByteArray name; - int scopeIdx = scopedName.lastIndexOf("::"); - if (scopeIdx != -1) { - scope = scopedName.left(scopeIdx); - name = scopedName.mid(scopeIdx + 2); - } else { - name = scopedName; - } - - if (scope == "Qt") - return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name); - - if (isNamedEnumeratorInScope(metaObj, scope, name)) - return true; - - if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { - for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { - if (isNamedEnumeratorInScope(*related, scope, name)) - return true; - } - } - - return false; + predecessor->m_flags.isOverridden = true; } QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) @@ -954,7 +869,7 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString) { bool unnamedParameter = false; - const QSet<QString> &illegalNames = engine->v8Engine->illegalNames(); + const QSet<QString> &illegalNames = engine->illegalNames(); QString parameters; for (int i = 0; i < parameterNameList.count(); ++i) { @@ -1517,262 +1432,4 @@ QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const return QList<QByteArray>(); } -// Returns true if \a from is assignable to a property of type \a to -bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to) -{ - Q_ASSERT(!from.isNull() && !to.isNull()); - - struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) { - return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); - } }; - - const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2(); - if (tom == &QObject::staticMetaObject) return true; - - if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache - QQmlPropertyCache *fromp = from._m.asT1(); - QQmlPropertyCache *top = to._m.asT1(); - - while (fromp) { - if (fromp == top) return true; - fromp = fromp->parent(); - } - } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject - QQmlPropertyCache *fromp = from._m.asT1(); - - while (fromp) { - const QMetaObject *fromm = fromp->metaObject(); - if (fromm && I::equal(fromm, tom)) return true; - fromp = fromp->parent(); - } - } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache - const QMetaObject *fromm = from._m.asT2(); - - if (!tom) return false; - - while (fromm) { - if (I::equal(fromm, tom)) return true; - fromm = fromm->superClass(); - } - } else { // QMetaObject -> QMetaObject - const QMetaObject *fromm = from._m.asT2(); - - while (fromm) { - if (I::equal(fromm, tom)) return true; - fromm = fromm->superClass(); - } - } - - return false; -} - -void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) -{ - int offset; - - switch (type) { - case QMetaObject::ReadProperty: - case QMetaObject::WriteProperty: - case QMetaObject::ResetProperty: - case QMetaObject::QueryPropertyDesignable: - case QMetaObject::QueryPropertyEditable: - case QMetaObject::QueryPropertyScriptable: - case QMetaObject::QueryPropertyStored: - case QMetaObject::QueryPropertyUser: - offset = (*metaObject)->propertyOffset(); - while (*index < offset) { - *metaObject = (*metaObject)->superClass(); - offset = (*metaObject)->propertyOffset(); - } - break; - case QMetaObject::InvokeMetaMethod: - offset = (*metaObject)->methodOffset(); - while (*index < offset) { - *metaObject = (*metaObject)->superClass(); - offset = (*metaObject)->methodOffset(); - } - break; - default: - offset = 0; - Q_UNIMPLEMENTED(); - offset = INT_MAX; - } - - *index -= offset; -} - -QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const -{ - if (_m.isNull()) return nullptr; - if (_m.isT1()) return _m.asT1(); - else return e->cache(_m.asT2()); -} - -int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const -{ - Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); - - int type = data.propType(); - - const char *propTypeName = nullptr; - - if (type == QMetaType::UnknownType) { - // Find the return type name from the method info - QMetaMethod m; - - if (_m.isT1()) { - QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); - - while (data.coreIndex() < c->methodIndexCacheStart) - c = c->_parent; - - const QMetaObject *metaObject = c->createMetaObject(); - Q_ASSERT(metaObject); - m = metaObject->method(data.coreIndex()); - } else { - m = _m.asT2()->method(data.coreIndex()); - } - - type = m.returnType(); - propTypeName = m.typeName(); - } - - if (QMetaType::sizeOf(type) <= int(sizeof(int))) { - if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) - return QMetaType::Int; - - if (isNamedEnumerator(metaObject(), propTypeName)) - return QMetaType::Int; - - if (type == QMetaType::UnknownType) { - if (unknownTypeError) - *unknownTypeError = propTypeName; - } - } // else we know that it's a known type, as sizeOf(UnknownType) == 0 - - return type; -} - -int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const -{ - Q_ASSERT(!_m.isNull() && index >= 0); - - if (_m.isT1()) { - typedef QQmlPropertyCacheMethodArguments A; - - QQmlPropertyCache *c = _m.asT1(); - Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); - - while (index < c->methodIndexCacheStart) - c = c->_parent; - - QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); - - if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) - return static_cast<A *>(rv->arguments())->arguments; - - const QMetaObject *metaObject = c->createMetaObject(); - Q_ASSERT(metaObject); - QMetaMethod m = metaObject->method(index); - - int argc = m.parameterCount(); - if (!rv->arguments()) { - A *args = c->createArgumentsObject(argc, m.parameterNames()); - rv->setArguments(args); - } - A *args = static_cast<A *>(rv->arguments()); - - QList<QByteArray> argTypeNames; // Only loaded if needed - - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - - if (QMetaType::sizeOf(type) > int(sizeof(int))) { - // Cannot be passed as int - // We know that it's a known type, as sizeOf(UnknownType) == 0 - } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { - type = QMetaType::Int; - } else { - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { - type = QMetaType::Int; - } else if (type == QMetaType::UnknownType){ - if (unknownTypeError) - *unknownTypeError = argTypeNames.at(ii); - return nullptr; - } - - } - args->arguments[ii + 1] = type; - } - args->argumentsValid = true; - return static_cast<A *>(rv->arguments())->arguments; - - } else { - QMetaMethod m = _m.asT2()->method(index); - return methodParameterTypes(m, argStorage, unknownTypeError); - - } -} - -int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const -{ - Q_ASSERT(argStorage); - - int argc = m.parameterCount(); - argStorage->resize(argc + 1); - argStorage->operator[](0) = argc; - QList<QByteArray> argTypeNames; // Only loaded if needed - - for (int ii = 0; ii < argc; ++ii) { - int type = m.parameterType(ii); - if (QMetaType::sizeOf(type) > int(sizeof(int))) { - // Cannot be passed as int - // We know that it's a known type, as sizeOf(UnknownType) == 0 - } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { - type = QMetaType::Int; - } else { - if (argTypeNames.isEmpty()) - argTypeNames = m.parameterTypes(); - if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { - type = QMetaType::Int; - } else if (type == QMetaType::UnknownType) { - if (unknownTypeError) - *unknownTypeError = argTypeNames.at(ii); - return nullptr; - } - } - argStorage->operator[](ii + 1) = type; - } - - return argStorage->data(); -} - -void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const -{ - if (ptr.isNull()) { - const QMetaObject *metaObject = _m.asT2(); - metaObject->d.static_metacall(nullptr, type, index, argv); - } - else if (ptr.isT1()) { - QMetaObject::metacall(ptr.asT1(), type, index, argv); - } - else { - const QMetaObject *metaObject = _m.asT1()->metaObject(); - QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index); - metaObject->d.static_metacall(reinterpret_cast<QObject*>(ptr.asT2()), type, index, argv); - } -} - -int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, - QByteArray *unknownTypeError) const -{ - QMetaMethod m = _m.asT2()->constructor(index); - return methodParameterTypes(m, dummy, unknownTypeError); -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index c3c818eb77..bfd78eef88 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -57,339 +57,25 @@ #include "qqmlnotifier_p.h" #include <private/qqmlpropertyindex_p.h> -#include <private/qhashedstring_p.h> +#include <private/qlinkedstringhash_p.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> #include <private/qv4value_p.h> +#include <private/qqmlpropertydata_p.h> +#include <private/qqmlenumdata_p.h> +#include <private/qqmlenumvalue_p.h> #include <limits> QT_BEGIN_NAMESPACE class QCryptographicHash; -class QMetaProperty; -class QQmlEngine; class QJSEngine; -class QQmlPropertyData; class QMetaObjectBuilder; -class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; -template <typename T> class QQmlPropertyCacheCreator; -template <typename T> class QQmlPropertyCacheAliasCreator; - -// We have this somewhat awful split between RawData and Data so that RawData can be -// used in unions. In normal code, you should always use Data which initializes RawData -// to an invalid state on construction. -// ### We should be able to remove this split nowadays -class QQmlPropertyRawData -{ -public: - typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; - - struct Flags { - enum Types { - OtherType = 0, - FunctionType = 1, // Is an invokable - QObjectDerivedType = 2, // Property type is a QObject* derived type - EnumType = 3, // Property type is an enum - QListType = 4, // Property type is a QML list - QmlBindingType = 5, // Property type is a QQmlBinding* - QJSValueType = 6, // Property type is a QScriptValue - V4HandleType = 7, // Property type is a QQmlV4Handle - VarPropertyType = 8, // Property type is a "var" property of VMEMO - QVariantType = 9 // Property is a QVariant - }; - - // The _otherBits (which "pad" the Flags struct to align it nicely) are used - // to store the relative property index. It will only get used when said index fits. See - // trySetStaticMetaCallFunction for details. - // (Note: this padding is done here, because certain compilers have surprising behavior - // when an enum is declared in-between two bit fields.) - enum { BitsLeftInFlags = 10 }; - unsigned _otherBits : BitsLeftInFlags; // align to 32 bits - - // Can apply to all properties, except IsFunction - unsigned isConstant : 1; // Has CONST flag - unsigned isWritable : 1; // Has WRITE function - unsigned isResettable : 1; // Has RESET function - unsigned isAlias : 1; // Is a QML alias to another property - unsigned isFinal : 1; // Has FINAL flag - unsigned isOverridden : 1; // Is overridden by a extension property - unsigned isDirect : 1; // Exists on a C++ QMetaObject - - unsigned type : 4; // stores an entry of Types - - // Apply only to IsFunctions - unsigned isVMEFunction : 1; // Function was added by QML - unsigned hasArguments : 1; // Function takes arguments - unsigned isSignal : 1; // Function is a signal - unsigned isVMESignal : 1; // Signal was added by QML - unsigned isV4Function : 1; // Function takes QQmlV4Function* args - unsigned isSignalHandler : 1; // Function is a signal handler - unsigned isOverload : 1; // Function is an overload of another function - unsigned isCloned : 1; // The function was marked as cloned - unsigned isConstructor : 1; // The function was marked is a constructor - - // Internal QQmlPropertyCache flags - unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved - unsigned overrideIndexIsProperty: 1; - - inline Flags(); - inline bool operator==(const Flags &other) const; - inline void copyPropertyTypeFlags(Flags from); - }; - - Flags flags() const { return _flags; } - void setFlags(Flags f) - { - unsigned otherBits = _flags._otherBits; - _flags = f; - _flags._otherBits = otherBits; - } - - bool isValid() const { return coreIndex() != -1; } - - bool isConstant() const { return _flags.isConstant; } - bool isWritable() const { return _flags.isWritable; } - void setWritable(bool onoff) { _flags.isWritable = onoff; } - bool isResettable() const { return _flags.isResettable; } - bool isAlias() const { return _flags.isAlias; } - bool isFinal() const { return _flags.isFinal; } - bool isOverridden() const { return _flags.isOverridden; } - bool isDirect() const { return _flags.isDirect; } - bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } - bool isFunction() const { return _flags.type == Flags::FunctionType; } - bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; } - bool isEnum() const { return _flags.type == Flags::EnumType; } - bool isQList() const { return _flags.type == Flags::QListType; } - bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; } - bool isQJSValue() const { return _flags.type == Flags::QJSValueType; } - bool isV4Handle() const { return _flags.type == Flags::V4HandleType; } - bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; } - bool isQVariant() const { return _flags.type == Flags::QVariantType; } - bool isVMEFunction() const { return _flags.isVMEFunction; } - bool hasArguments() const { return _flags.hasArguments; } - bool isSignal() const { return _flags.isSignal; } - bool isVMESignal() const { return _flags.isVMESignal; } - bool isV4Function() const { return _flags.isV4Function; } - bool isSignalHandler() const { return _flags.isSignalHandler; } - bool isOverload() const { return _flags.isOverload; } - void setOverload(bool onoff) { _flags.isOverload = onoff; } - bool isCloned() const { return _flags.isCloned; } - bool isConstructor() const { return _flags.isConstructor; } - - bool hasOverride() const { return overrideIndex() >= 0; } - bool hasRevision() const { return revision() != 0; } - - bool isFullyResolved() const { return !_flags.notFullyResolved; } - - int propType() const { Q_ASSERT(isFullyResolved()); return _propType; } - void setPropType(int pt) - { - Q_ASSERT(pt >= 0); - Q_ASSERT(pt <= std::numeric_limits<qint16>::max()); - _propType = quint16(pt); - } - - int notifyIndex() const { return _notifyIndex; } - void setNotifyIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _notifyIndex = qint16(idx); - } - - bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; } - void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; } - - int overrideIndex() const { return _overrideIndex; } - void setOverrideIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _overrideIndex = qint16(idx); - } - - int coreIndex() const { return _coreIndex; } - void setCoreIndex(int idx) - { - Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); - Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); - _coreIndex = qint16(idx); - } - - quint8 revision() const { return _revision; } - void setRevision(quint8 rev) - { - Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); - _revision = quint8(rev); - } - - /* If a property is a C++ type, then we store the minor - * version of this type. - * This is required to resolve property or signal revisions - * if this property is used as a grouped property. - * - * Test.qml - * property TextEdit someTextEdit: TextEdit {} - * - * Test { - * someTextEdit.preeditText: "test" //revision 7 - * someTextEdit.onEditingFinished: console.log("test") //revision 6 - * } - * - * To determine if these properties with revisions are available we need - * the minor version of TextEdit as imported in Test.qml. - * - */ - - quint8 typeMinorVersion() const { return _typeMinorVersion; } - void setTypeMinorVersion(quint8 rev) - { - Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); - _typeMinorVersion = quint8(rev); - } - - QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } - void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; } - - int metaObjectOffset() const { return _metaObjectOffset; } - void setMetaObjectOffset(int off) - { - Q_ASSERT(off >= std::numeric_limits<qint16>::min()); - Q_ASSERT(off <= std::numeric_limits<qint16>::max()); - _metaObjectOffset = qint16(off); - } - - StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; } - void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) - { - if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { - _flags._otherBits = relativePropertyIndex; - _staticMetaCallFunction = f; - } - } - quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; } - -private: - Flags _flags; - qint16 _coreIndex = 0; - quint16 _propType = 0; - - // The notify index is in the range returned by QObjectPrivate::signalIndex(). - // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex = 0; - qint16 _overrideIndex = 0; - - quint8 _revision = 0; - quint8 _typeMinorVersion = 0; - qint16 _metaObjectOffset = 0; - - QQmlPropertyCacheMethodArguments *_arguments = nullptr; - StaticMetaCallFunction _staticMetaCallFunction = nullptr; - - friend class QQmlPropertyData; - friend class QQmlPropertyCache; -}; - -#if QT_POINTER_SIZE == 4 -Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24); -#else // QT_POINTER_SIZE == 8 -Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32); -#endif - -class QQmlPropertyData : public QQmlPropertyRawData -{ -public: - enum WriteFlag { - BypassInterceptor = 0x01, - DontRemoveBinding = 0x02, - RemoveBindingOnAliasWrite = 0x04 - }; - Q_DECLARE_FLAGS(WriteFlags, WriteFlag) - - inline QQmlPropertyData(); - inline QQmlPropertyData(const QQmlPropertyRawData &); - - inline bool operator==(const QQmlPropertyRawData &); - - static Flags flagsForProperty(const QMetaProperty &); - void load(const QMetaProperty &); - void load(const QMetaMethod &); - QString name(QObject *) const; - QString name(const QMetaObject *) const; - - void markAsOverrideOf(QQmlPropertyData *predecessor); - - inline void readProperty(QObject *target, void *property) const - { - void *args[] = { property, nullptr }; - readPropertyWithArgs(target, args); - } - - inline void readPropertyWithArgs(QObject *target, void *args[]) const - { - if (hasStaticMetaCallFunction()) - staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); - else if (isDirect()) - target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); - else - QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); - } - - bool writeProperty(QObject *target, void *value, WriteFlags flags) const - { - int status = -1; - void *argv[] = { value, nullptr, &status, &flags }; - if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction()) - staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); - else if (flags.testFlag(BypassInterceptor) && isDirect()) - target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); - else - QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); - return true; - } - - static Flags defaultSignalFlags() - { - Flags f; - f.isSignal = true; - f.type = Flags::FunctionType; - f.isVMESignal = true; - return f; - } - - static Flags defaultSlotFlags() - { - Flags f; - f.type = Flags::FunctionType; - f.isVMEFunction = true; - return f; - } - -private: - friend class QQmlPropertyCache; - void lazyLoad(const QMetaProperty &); - void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return _flags.notFullyResolved; } -}; - -struct QQmlEnumValue -{ - QQmlEnumValue() {} - QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {} - QString namedValue; - int value = -1; -}; - -struct QQmlEnumData -{ - QString name; - QVector<QQmlEnumValue> values; -}; - class QQmlPropertyCacheMethodArguments; + class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount { public: @@ -399,28 +85,26 @@ public: void update(const QMetaObject *); void invalidate(const QMetaObject *); - // Used by qmlpuppet. Remove as soon Creator requires Qt 5.5. - void invalidate(void *, const QMetaObject *mo) { invalidate(mo); } QQmlPropertyCache *copy(); QQmlPropertyCache *copyAndAppend(const QMetaObject *, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndReserve(int propertyCount, int methodCount, int signalCount, int enumCount); - void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex, + void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int propType, int revision, int notifyIndex); - void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex, + 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; @@ -488,6 +172,10 @@ public: static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); QByteArray checksum(bool *ok); + + int allowedRevision(int index) const { return allowedRevisionCache[index]; } + void setAllowedRevision(int index, int allowed) { allowedRevisionCache[index] = allowed; } + private: friend class QQmlEnginePrivate; friend class QQmlCompiler; @@ -495,19 +183,18 @@ private: template <typename T> friend class QQmlPropertyCacheAliasCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; - friend struct QQmlMetaTypeData; inline QQmlPropertyCache *copy(int reserve); void append(const QMetaObject *, int typeMinorVersion, - QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(), - QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(), - QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); + QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), + QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names); typedef QVector<QQmlPropertyData> IndexCache; - typedef QStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; + typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; typedef QVector<int> AllowedRevisionCache; QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const; @@ -556,172 +243,6 @@ private: QByteArray _checksum; }; -typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr; - -// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. -// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but -// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a -// QObject type used in assignment or when we don't have a QQmlEngine etc. -// -// This class does NOT reference the propertycache. -class QQmlEnginePrivate; -class Q_QML_EXPORT QQmlMetaObject -{ -public: - typedef QVarLengthArray<int, 9> ArgTypeStorage; - - inline QQmlMetaObject(); - inline QQmlMetaObject(QObject *); - inline QQmlMetaObject(const QMetaObject *); - inline QQmlMetaObject(QQmlPropertyCache *); - inline QQmlMetaObject(const QQmlMetaObject &); - - inline QQmlMetaObject &operator=(const QQmlMetaObject &); - - inline bool isNull() const; - - inline const char *className() const; - inline int propertyCount() const; - - inline bool hasMetaObject() const; - inline const QMetaObject *metaObject() const; - - QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const; - - int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const; - int *methodParameterTypes(int index, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const; - - static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to); - - // static_metacall (on Gadgets) doesn't call the base implementation and therefore - // we need a helper to find the correct meta object and property/method index. - static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index); - -protected: - QBiPointer<QQmlPropertyCache, const QMetaObject> _m; - int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage, - QByteArray *unknownTypeError) const; - -}; - -class QQmlObjectOrGadget: public QQmlMetaObject -{ -public: - QQmlObjectOrGadget(QObject *obj) - : QQmlMetaObject(obj), - ptr(obj) - {} - QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget) - : QQmlMetaObject(propertyCache) - , ptr(gadget) - {} - - void metacall(QMetaObject::Call type, int index, void **argv) const; - -private: - QBiPointer<QObject, void> ptr; - -protected: - QQmlObjectOrGadget(const QMetaObject* metaObject) - : QQmlMetaObject(metaObject) - {} - -}; - -class QQmlStaticMetaObject : public QQmlObjectOrGadget { -public: - QQmlStaticMetaObject(const QMetaObject* metaObject) - : QQmlObjectOrGadget(metaObject) - {} - int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; -}; - -QQmlPropertyRawData::Flags::Flags() - : _otherBits(0) - , isConstant(false) - , isWritable(false) - , isResettable(false) - , isAlias(false) - , isFinal(false) - , isOverridden(false) - , isDirect(false) - , type(OtherType) - , isVMEFunction(false) - , hasArguments(false) - , isSignal(false) - , isVMESignal(false) - , isV4Function(false) - , isSignalHandler(false) - , isOverload(false) - , isCloned(false) - , isConstructor(false) - , notFullyResolved(false) - , overrideIndexIsProperty(false) -{} - -bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const -{ - return isConstant == other.isConstant && - isWritable == other.isWritable && - isResettable == other.isResettable && - isAlias == other.isAlias && - isFinal == other.isFinal && - isOverridden == other.isOverridden && - type == other.type && - isVMEFunction == other.isVMEFunction && - hasArguments == other.hasArguments && - isSignal == other.isSignal && - isVMESignal == other.isVMESignal && - isV4Function == other.isV4Function && - isSignalHandler == other.isSignalHandler && - isOverload == other.isOverload && - isCloned == other.isCloned && - isConstructor == other.isConstructor && - notFullyResolved == other.notFullyResolved && - overrideIndexIsProperty == other.overrideIndexIsProperty; -} - -void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from) -{ - switch (from.type) { - case QObjectDerivedType: - case EnumType: - case QListType: - case QmlBindingType: - case QJSValueType: - case V4HandleType: - case QVariantType: - type = from.type; - } -} - -QQmlPropertyData::QQmlPropertyData() -{ - setCoreIndex(-1); - setPropType(0); - setNotifyIndex(-1); - setOverrideIndex(-1); - setRevision(0); - setMetaObjectOffset(-1); - setArguments(nullptr); - trySetStaticMetaCallFunction(nullptr, 0); -} - -QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d) -{ - *(static_cast<QQmlPropertyRawData *>(this)) = d; -} - -bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other) -{ - return flags() == other.flags() && - propType() == other.propType() && - coreIndex() == other.coreIndex() && - notifyIndex() == other.notifyIndex() && - revision() == other.revision(); -} - inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const { if (p && Q_UNLIKELY(p->notFullyResolved())) @@ -880,124 +401,6 @@ bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const return false; } -QQmlMetaObject::QQmlMetaObject() -{ -} - -QQmlMetaObject::QQmlMetaObject(QObject *o) -{ - if (o) { - QQmlData *ddata = QQmlData::get(o, false); - if (ddata && ddata->propertyCache) _m = ddata->propertyCache; - else _m = o->metaObject(); - } -} - -QQmlMetaObject::QQmlMetaObject(const QMetaObject *m) -: _m(m) -{ -} - -QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m) -: _m(m) -{ -} - -QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o) -: _m(o._m) -{ -} - -QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o) -{ - _m = o._m; - return *this; -} - -bool QQmlMetaObject::isNull() const -{ - return _m.isNull(); -} - -const char *QQmlMetaObject::className() const -{ - if (_m.isNull()) { - return nullptr; - } else if (_m.isT1()) { - return _m.asT1()->className(); - } else { - return _m.asT2()->className(); - } -} - -int QQmlMetaObject::propertyCount() const -{ - if (_m.isNull()) { - return 0; - } else if (_m.isT1()) { - return _m.asT1()->propertyCount(); - } else { - return _m.asT2()->propertyCount(); - } -} - -bool QQmlMetaObject::hasMetaObject() const -{ - return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject()); -} - -const QMetaObject *QQmlMetaObject::metaObject() const -{ - if (_m.isNull()) return nullptr; - if (_m.isT1()) return _m.asT1()->createMetaObject(); - else return _m.asT2(); -} - -class QQmlPropertyCacheVector -{ -public: - QQmlPropertyCacheVector() {} - QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) - : data(std::move(other.data)) {} - QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { - QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data)); - data.swap(moved); - return *this; - } - - ~QQmlPropertyCacheVector() { clear(); } - void resize(int size) { return data.resize(size); } - int count() const { return data.count(); } - void clear() - { - for (int i = 0; i < data.count(); ++i) { - if (QQmlPropertyCache *cache = data.at(i).data()) - cache->release(); - } - data.clear(); - } - - void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } - QQmlPropertyCache *at(int index) const { return data.at(index).data(); } - void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { - if (QQmlPropertyCache *oldCache = data.at(index).data()) { - if (replacement.data() == oldCache) - return; - oldCache->release(); - } - data[index] = replacement.data(); - replacement->addref(); - } - - void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } - bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } -private: - Q_DISABLE_COPY(QQmlPropertyCacheVector) - QVector<QFlagPointer<QQmlPropertyCache>> data; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) - QT_END_NAMESPACE #endif // QQMLPROPERTYCACHE_P_H diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index fb54da5b73..034ebfc743 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -45,6 +45,35 @@ QT_BEGIN_NAMESPACE QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); + +int QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(QV4::CompiledData::BuiltinType type) +{ + switch (type) { + case QV4::CompiledData::BuiltinType::Var: return QMetaType::QVariant; + case QV4::CompiledData::BuiltinType::Variant: return QMetaType::QVariant; + case QV4::CompiledData::BuiltinType::Int: return QMetaType::Int; + case QV4::CompiledData::BuiltinType::Bool: return QMetaType::Bool; + case QV4::CompiledData::BuiltinType::Real: return QMetaType::Double; + case QV4::CompiledData::BuiltinType::String: return QMetaType::QString; + case QV4::CompiledData::BuiltinType::Url: return QMetaType::QUrl; + case QV4::CompiledData::BuiltinType::Color: return QMetaType::QColor; + case QV4::CompiledData::BuiltinType::Font: return QMetaType::QFont; + case QV4::CompiledData::BuiltinType::Time: return QMetaType::QTime; + case QV4::CompiledData::BuiltinType::Date: return QMetaType::QDate; + case QV4::CompiledData::BuiltinType::DateTime: return QMetaType::QDateTime; + case QV4::CompiledData::BuiltinType::Rect: return QMetaType::QRectF; + case QV4::CompiledData::BuiltinType::Point: return QMetaType::QPointF; + case QV4::CompiledData::BuiltinType::Size: return QMetaType::QSizeF; + case QV4::CompiledData::BuiltinType::Vector2D: return QMetaType::QVector2D; + case QV4::CompiledData::BuiltinType::Vector3D: return QMetaType::QVector3D; + case QV4::CompiledData::BuiltinType::Vector4D: return QMetaType::QVector4D; + case QV4::CompiledData::BuiltinType::Matrix4x4: return QMetaType::QMatrix4x4; + case QV4::CompiledData::BuiltinType::Quaternion: return QMetaType::QQuaternion; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: break; + }; + return QMetaType::UnknownType; +} + QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache) : referencingObjectIndex(referencingObjectIndex) @@ -64,7 +93,9 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); + instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache) + .property(instantiatingPropertyName, ¬InRevision, + QQmlPropertyResolver::IgnoreRevision); return instantiatingProperty != nullptr; } diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 901602d17b..39778aa328 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -52,9 +52,22 @@ #include <private/qqmlvaluetype_p.h> #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, @@ -82,6 +95,8 @@ struct QQmlPropertyCacheCreatorBase Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) public: static QAtomicInt classIndexCounter; + + static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type); }; template <typename ObjectContainer> @@ -95,12 +110,14 @@ public: QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports); - QQmlCompileError buildMetaObjects(); + QQmlJS::DiagnosticMessage buildMetaObjects(); protected: - QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); - QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); + QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); + 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); } @@ -126,18 +143,27 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() { QQmlBindingInstantiationContext context; return buildMetaObjectRecursively(/*root object*/0, context); } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) { + auto isAddressable = [](const QUrl &url) { + const QString fileName = url.fileName(); + return !fileName.isEmpty() && fileName.front().isUpper(); + }; + const CompiledObject *obj = objectContainer->objectAt(objectIndex); + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 + || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 + || (((obj->flags & QV4::CompiledData::Object::IsComponent) + || (objectIndex == 0 && isAddressable(objectContainer->url()))) + && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType); - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0; if (!needVMEMetaObject) { auto binding = obj->bindingsBegin(); auto end = obj->bindingsEnd(); @@ -153,8 +179,8 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); - if (error.isSet()) + QQmlJS::DiagnosticMessage error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + if (error.isValid()) return error; } } else { @@ -168,16 +194,16 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje QQmlRefPointer<QQmlPropertyCache> baseTypeCache; { - QQmlCompileError error; + QQmlJS::DiagnosticMessage error; baseTypeCache = propertyCacheForObject(obj, context, &error); - if (error.isSet()) + if (error.isValid()) return error; } if (baseTypeCache) { if (needVMEMetaObject) { - QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); - if (error.isSet()) + QQmlJS::DiagnosticMessage error = createMetaObject(objectIndex, obj, baseTypeCache); + if (error.isValid()) return error; } else { propertyCaches->set(objectIndex, baseTypeCache); @@ -198,18 +224,18 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje if (!context.resolveInstantiatingProperty()) pendingGroupPropertyBindings->append(context); - QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); - if (error.isSet()) + QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context); + if (error.isValid()) return error; } } - QQmlCompileError noError; + QQmlJS::DiagnosticMessage noError; return noError; } template <typename ObjectContainer> -inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const +inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const { if (context.instantiatingProperty) { return context.instantiatingPropertyCache(enginePrivate); @@ -219,15 +245,15 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine if (typeRef->isFullyDynamicType) { if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); return nullptr; } if (obj->signalCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); return nullptr; } if (obj->functionCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); + *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); return nullptr; } } @@ -239,22 +265,13 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine Q_ASSERT(typeRef); QQmlType qmltype = typeRef->type; if (!qmltype.isValid()) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } + imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex), + &qmltype, nullptr, nullptr, nullptr); } const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); if (!attachedMo) { - *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); + *error = qQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); return nullptr; } return enginePrivate->cache(attachedMo); @@ -263,7 +280,7 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) { QQmlRefPointer<QQmlPropertyCache> cache; cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), @@ -273,33 +290,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj propertyCaches->set(objectIndex, cache); propertyCaches->setNeedsVMEMetaObject(objectIndex); - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } -}; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - QByteArray newClassName; if (objectIndex == /*root object*/0) { @@ -322,18 +312,18 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj int varPropCount = 0; - QmlIR::PropertyResolver resolver(baseTypeCache); + QQmlPropertyResolver resolver(baseTypeCache); auto p = obj->propertiesBegin(); auto pend = obj->propertiesEnd(); for ( ; p != pend; ++p) { - if (p->type == QV4::CompiledData::Property::Var) + if (p->builtinType() == QV4::CompiledData::BuiltinType::Var) varPropCount++; bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); if (d && d->isFinal()) - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); } auto a = obj->aliasesBegin(); @@ -342,7 +332,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj bool notInRevision = false; QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); if (d && d->isFinal()) - return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); + return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); } int effectivePropertyIndex = cache->propertyIndexCacheStart; @@ -427,29 +417,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj auto end = s->parametersEnd(); for ( ; param != end; ++param, ++i) { names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - 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; } } @@ -459,7 +433,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj QString signalName = stringAt(s->nameIndex); if (seenSignals.contains(signalName)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); + return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); seenSignals.insert(signalName); cache->appendSignal(signalName, flags, effectiveMethodIndex++, @@ -475,19 +449,28 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj const QString slotName = stringAt(function->nameIndex); if (seenSignals.contains(slotName)) - return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); + return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); // Note: we don't append slotName to the seenSignals list, since we don't // 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); } @@ -501,21 +484,23 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj int propertTypeMinorVersion = 0; QQmlPropertyData::Flags propertyFlags; - if (p->type == QV4::CompiledData::Property::Var) { - propertyType = QMetaType::QVariant; + const QV4::CompiledData::BuiltinType type = p->builtinType(); + + if (type == QV4::CompiledData::BuiltinType::Var) propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; - } else if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - if (p->type == QV4::CompiledData::Property::Variant) + + if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) { + propertyType = metaTypeForPropertyType(type); + + if (type == QV4::CompiledData::BuiltinType::Variant) propertyFlags.type = QQmlPropertyData::Flags::QVariantType; } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); + Q_ASSERT(!p->isBuiltinType); QQmlType qmltype; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) { - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); + if (!imports->resolveType(stringAt(p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) { + return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); } Q_ASSERT(qmltype.isValid()); @@ -526,27 +511,27 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj auto compilationUnit = tdata->compilationUnit(); - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = compilationUnit->metaTypeId; - } else { + if (p->isList) { propertyType = compilationUnit->listMetaTypeId; + } else { + propertyType = compilationUnit->metaTypeId; } } else { - if (p->type == QV4::CompiledData::Property::Custom) { + if (p->isList) { + propertyType = qmltype.qListTypeId(); + } else { propertyType = qmltype.typeId(); propertTypeMinorVersion = qmltype.minorVersion(); - } else { - propertyType = qmltype.qListTypeId(); } } - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; - else + if (p->isList) propertyFlags.type = QQmlPropertyData::Flags::QListType; + else + propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; } - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + if (!p->isReadOnly && !p->isList) propertyFlags.isWritable = true; @@ -559,11 +544,40 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj effectiveSignalIndex++; } - QQmlCompileError noError; + QQmlJS::DiagnosticMessage noError; return noError; } 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: @@ -571,13 +585,13 @@ public: QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); - void appendAliasPropertiesToMetaObjects(); + void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv); - QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); + QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv); private: - void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags); + void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv); + QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv); void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; @@ -596,7 +610,7 @@ inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCre } template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects() +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv) { // skip the root object (index 0) as that one does not have a first object index originating // from a binding. @@ -606,15 +620,15 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie continue; const auto rootBinding = component.bindingsBegin(); - appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); + appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv); } const int rootObjectIndex = 0; - appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); + appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv); } template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) +inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv) { QVector<int> objectsWithAliases; collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); @@ -654,7 +668,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (allAliasTargetsExist(object)) { - appendAliasesToPropertyCache(component, objectIndex); + appendAliasesToPropertyCache(component, objectIndex, enginePriv); } else { pendingObjects.append(objectIndex); } @@ -688,9 +702,8 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias( - const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, - QQmlPropertyData::Flags *propertyFlags) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, + QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv) { *type = 0; bool writable = false; @@ -714,7 +727,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property const QV4::CompiledData::Alias *targetAlias = &(*nextAlias); if (seenAliases.contains(targetAlias)) { - return QQmlCompileError(targetAlias->location, + return qQmlCompileError(targetAlias->location, QQmlPropertyCacheCreatorBase::tr("Cyclic alias")); } @@ -722,7 +735,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property lastAlias = targetAlias; } while (lastAlias->aliasToLocalAlias); - return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags); + return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv); } const int targetObjectIndex = objectForId(component, alias.targetObjectId); @@ -736,7 +749,8 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property // Can be caused by the alias target not being a valid id or property. E.g.: // property alias dataValue: dataVal // invalidAliasComponent { id: dataVal } - return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + return qQmlCompileError(targetObject.location, + QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); } if (typeRef->type.isValid()) @@ -753,45 +767,62 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); Q_ASSERT(targetCache); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); Q_ASSERT(targetProperty); - *type = targetProperty->propType(); + // for deep aliases, valueTypeIndex is always set + if (!QQmlValueTypeFactory::isValueType(targetProperty->propType()) && valueTypeIndex != -1) { + // deep alias property + *type = targetProperty->propType(); + targetCache = enginePriv->propertyCacheForType(*type); + Q_ASSERT(targetCache); + targetProperty = targetCache->property(valueTypeIndex); - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QVariant::Int; - else - *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + *type = targetProperty->propType(); + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + } else { - if (targetProperty->isEnum()) { - *type = QVariant::Int; + // value type or primitive type or enum + *type = targetProperty->propType(); + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); } else { - // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); - if (targetProperty->isVarProperty()) - propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + if (targetProperty->isVarProperty()) + propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + } } } } - propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable; + propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable; propertyFlags->isResettable = resettable; - return QQmlCompileError(); + return QQmlJS::DiagnosticMessage(); } template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( - const CompiledObject &component, int objectIndex) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( + const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv) { const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (!object.aliasCount()) - return QQmlCompileError(); + return QQmlJS::DiagnosticMessage(); QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); Q_ASSERT(propertyCache); @@ -808,8 +839,8 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAl int type = 0; int minorVersion = 0; QQmlPropertyData::Flags propertyFlags; - QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); - if (error.isSet()) + QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv); + if (error.isValid()) return error; const QString propertyName = objectContainer->stringAt(alias->nameIndex); @@ -821,7 +852,7 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAl type, minorVersion, effectiveSignalIndex++); } - return QQmlCompileError(); + return QQmlJS::DiagnosticMessage(); } template <typename ObjectContainer> diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h index c8700c3ca5..62f09bdfff 100644 --- a/src/qml/jsruntime/qv4serialize_p.h +++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QV4SERIALIZE_P_H -#define QV4SERIALIZE_P_H +#ifndef QQMLPROPERTYCACHEMETODARGUMENTS_P_H +#define QQMLPROPERTYCACHEMETODARGUMENTS_P_H // // W A R N I N G @@ -51,26 +51,27 @@ // We mean it. // +#include <QtCore/qlist.h> #include <QtCore/qbytearray.h> -#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE -namespace QV4 { - -class Serialize { +class QString; +class QQmlPropertyCacheMethodArguments +{ public: + QQmlPropertyCacheMethodArguments *next; - static QByteArray serialize(const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const QByteArray &, ExecutionEngine *); + //for signal handler rewrites + QString *signalParameterStringForJS; + int parameterError:1; + int argumentsValid:1; -private: - static void serialize(QByteArray &, const Value &, ExecutionEngine *); - static ReturnedValue deserialize(const char *&, ExecutionEngine *); -}; + QList<QByteArray> *names; -} + int arguments[1]; +}; QT_END_NAMESPACE -#endif // QV8WORKER_P_H +#endif // QQMLPROPERTYCACHEMETODARGUMENTS_P_H diff --git a/src/qml/debugger/qqmlmemoryprofiler_p.h b/src/qml/qml/qqmlpropertycachevector_p.h index 12a31a851f..1dff7c61a6 100644 --- a/src/qml/debugger/qqmlmemoryprofiler_p.h +++ b/src/qml/qml/qqmlpropertycachevector_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLMEMORYPROFILER_H -#define QQMLMEMORYPROFILER_H +#ifndef QQMLPROPERTYCACHEVECTOR_P_H +#define QQMLPROPERTYCACHEVECTOR_P_H // // W A R N I N G @@ -51,84 +51,54 @@ // We mean it. // -#include <private/qtqmlglobal_p.h> -#include <QUrl> +#include <private/qflagpointer_p.h> +#include <private/qqmlpropertycache_p.h> QT_BEGIN_NAMESPACE -#if !QT_CONFIG(qml_debug) - -#define QML_MEMORY_SCOPE_URL(url) -#define QML_MEMORY_SCOPE_STRING(s) - -#else - -class Q_QML_PRIVATE_EXPORT QQmlMemoryScope +class QQmlPropertyCacheVector { public: - explicit QQmlMemoryScope(const QUrl &url) - : pushed(false) - { - if (Q_UNLIKELY(openLibrary())) - init(url.path().toUtf8().constData()); + QQmlPropertyCacheVector() {} + QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other) + : data(std::move(other.data)) {} + QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) { + QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data)); + data.swap(moved); + return *this; } - explicit QQmlMemoryScope(const char *string) - : pushed(false) + ~QQmlPropertyCacheVector() { clear(); } + void resize(int size) { return data.resize(size); } + int count() const { return data.count(); } + void clear() { - if (Q_UNLIKELY(openLibrary())) - init(string); + for (int i = 0; i < data.count(); ++i) { + if (QQmlPropertyCache *cache = data.at(i).data()) + cache->release(); + } + data.clear(); } - ~QQmlMemoryScope() - { - if (Q_UNLIKELY(pushed)) - done(); - } - - enum LibraryState - { - Unloaded, - Failed, - Loaded - }; - - static bool openLibrary() - { - if (Q_LIKELY(state == Loaded)) - return true; - if (state == Failed) - return false; - - return doOpenLibrary(); + void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); } + QQmlPropertyCache *at(int index) const { return data.at(index).data(); } + void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { + if (QQmlPropertyCache *oldCache = data.at(index).data()) { + if (replacement.data() == oldCache) + return; + oldCache->release(); + } + data[index] = replacement.data(); + replacement->addref(); } + void setNeedsVMEMetaObject(int index) { data[index].setFlag(); } + bool needsVMEMetaObject(int index) const { return data.at(index).flag(); } private: - Q_NEVER_INLINE void init(const char *string); - Q_NEVER_INLINE void done(); - Q_NEVER_INLINE static bool doOpenLibrary(); - - static LibraryState state; - - bool pushed; -}; - -class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler -{ -public: - static void enable(); - static void disable(); - static bool isEnabled(); - - static void clear(); - static void stats(int *allocCount, int *bytesAllocated); - static void save(const char *filename); + Q_DISABLE_COPY(QQmlPropertyCacheVector) + QVector<QFlagPointer<QQmlPropertyCache>> data; }; -#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url) -#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) - -#endif - QT_END_NAMESPACE -#endif // QQMLMEMORYPROFILER_H + +#endif // QQMLPROPERTYCACHEVECTOR_P_H diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h new file mode 100644 index 0000000000..dec696226e --- /dev/null +++ b/src/qml/qml/qqmlpropertydata_p.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** 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 QQMLPROPERTYDATA_P_H +#define QQMLPROPERTYDATA_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/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyCacheMethodArguments; +class QQmlPropertyData +{ +public: + enum WriteFlag { + BypassInterceptor = 0x01, + DontRemoveBinding = 0x02, + RemoveBindingOnAliasWrite = 0x04 + }; + Q_DECLARE_FLAGS(WriteFlags, WriteFlag) + + typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; + + struct Flags { + enum Types { + OtherType = 0, + FunctionType = 1, // Is an invokable + QObjectDerivedType = 2, // Property type is a QObject* derived type + EnumType = 3, // Property type is an enum + QListType = 4, // Property type is a QML list + QmlBindingType = 5, // Property type is a QQmlBinding* + QJSValueType = 6, // Property type is a QScriptValue + // Gap, used to be V4HandleType + VarPropertyType = 8, // Property type is a "var" property of VMEMO + QVariantType = 9 // Property is a QVariant + }; + + // The _otherBits (which "pad" the Flags struct to align it nicely) are used + // to store the relative property index. It will only get used when said index fits. See + // trySetStaticMetaCallFunction for details. + // (Note: this padding is done here, because certain compilers have surprising behavior + // when an enum is declared in-between two bit fields.) + enum { BitsLeftInFlags = 10 }; + unsigned otherBits : BitsLeftInFlags; // align to 32 bits + + // Can apply to all properties, except IsFunction + unsigned isConstant : 1; // Has CONST flag + unsigned isWritable : 1; // Has WRITE function + unsigned isResettable : 1; // Has RESET function + unsigned isAlias : 1; // Is a QML alias to another property + unsigned isFinal : 1; // Has FINAL flag + unsigned isOverridden : 1; // Is overridden by a extension property + unsigned isDirect : 1; // Exists on a C++ QMetaObject + + unsigned type : 4; // stores an entry of Types + + // Apply only to IsFunctions + unsigned isVMEFunction : 1; // Function was added by QML + unsigned hasArguments : 1; // Function takes arguments + unsigned isSignal : 1; // Function is a signal + unsigned isVMESignal : 1; // Signal was added by QML + unsigned isV4Function : 1; // Function takes QQmlV4Function* args + unsigned isSignalHandler : 1; // Function is a signal handler + unsigned isOverload : 1; // Function is an overload of another function + unsigned isCloned : 1; // The function was marked as cloned + unsigned isConstructor : 1; // The function was marked is a constructor + + // Internal QQmlPropertyCache flags + unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved + unsigned overrideIndexIsProperty: 1; + + inline Flags(); + inline bool operator==(const Flags &other) const; + inline void copyPropertyTypeFlags(Flags from); + }; + + inline bool operator==(const QQmlPropertyData &) const; + + Flags flags() const { return m_flags; } + void setFlags(Flags f) + { + unsigned otherBits = m_flags.otherBits; + m_flags = f; + m_flags.otherBits = otherBits; + } + + bool isValid() const { return coreIndex() != -1; } + + bool isConstant() const { return m_flags.isConstant; } + bool isWritable() const { return m_flags.isWritable; } + void setWritable(bool onoff) { m_flags.isWritable = onoff; } + bool isResettable() const { return m_flags.isResettable; } + bool isAlias() const { return m_flags.isAlias; } + bool isFinal() const { return m_flags.isFinal; } + bool isOverridden() const { return m_flags.isOverridden; } + bool isDirect() const { return m_flags.isDirect; } + bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } + bool isFunction() const { return m_flags.type == Flags::FunctionType; } + bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; } + bool isEnum() const { return m_flags.type == Flags::EnumType; } + bool isQList() const { return m_flags.type == Flags::QListType; } + bool isQmlBinding() const { return m_flags.type == Flags::QmlBindingType; } + bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; } + bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; } + bool isQVariant() const { return m_flags.type == Flags::QVariantType; } + bool isVMEFunction() const { return m_flags.isVMEFunction; } + bool hasArguments() const { return m_flags.hasArguments; } + bool isSignal() const { return m_flags.isSignal; } + bool isVMESignal() const { return m_flags.isVMESignal; } + bool isV4Function() const { return m_flags.isV4Function; } + bool isSignalHandler() const { return m_flags.isSignalHandler; } + bool isOverload() const { return m_flags.isOverload; } + void setOverload(bool onoff) { m_flags.isOverload = onoff; } + bool isCloned() const { return m_flags.isCloned; } + bool isConstructor() const { return m_flags.isConstructor; } + + bool hasOverride() const { return overrideIndex() >= 0; } + bool hasRevision() const { return revision() != 0; } + + bool isFullyResolved() const { return !m_flags.notFullyResolved; } + + int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; } + void setPropType(int pt) + { + Q_ASSERT(pt >= 0); + Q_ASSERT(pt <= std::numeric_limits<qint16>::max()); + m_propType = quint16(pt); + } + + int notifyIndex() const { return m_notifyIndex; } + void setNotifyIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_notifyIndex = qint16(idx); + } + + bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; } + void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; } + + int overrideIndex() const { return m_overrideIndex; } + void setOverrideIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_overrideIndex = qint16(idx); + } + + int coreIndex() const { return m_coreIndex; } + void setCoreIndex(int idx) + { + Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); + Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); + m_coreIndex = qint16(idx); + } + + quint8 revision() const { return m_revision; } + void setRevision(quint8 rev) + { + Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); + m_revision = quint8(rev); + } + + /* If a property is a C++ type, then we store the minor + * version of this type. + * This is required to resolve property or signal revisions + * if this property is used as a grouped property. + * + * Test.qml + * property TextEdit someTextEdit: TextEdit {} + * + * Test { + * someTextEdit.preeditText: "test" //revision 7 + * someTextEdit.onEditingFinished: console.log("test") //revision 6 + * } + * + * To determine if these properties with revisions are available we need + * the minor version of TextEdit as imported in Test.qml. + * + */ + + quint8 typeMinorVersion() const { return m_typeMinorVersion; } + void setTypeMinorVersion(quint8 rev) + { + Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); + m_typeMinorVersion = quint8(rev); + } + + QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } + void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; } + + int metaObjectOffset() const { return m_metaObjectOffset; } + void setMetaObjectOffset(int off) + { + Q_ASSERT(off >= std::numeric_limits<qint16>::min()); + Q_ASSERT(off <= std::numeric_limits<qint16>::max()); + m_metaObjectOffset = qint16(off); + } + + StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; } + void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) + { + if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { + m_flags.otherBits = relativePropertyIndex; + m_staticMetaCallFunction = f; + } + } + quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; } + + static Flags flagsForProperty(const QMetaProperty &); + void load(const QMetaProperty &); + void load(const QMetaMethod &); + QString name(QObject *) const; + QString name(const QMetaObject *) const; + + void markAsOverrideOf(QQmlPropertyData *predecessor); + + inline void readProperty(QObject *target, void *property) const + { + void *args[] = { property, nullptr }; + readPropertyWithArgs(target, args); + } + + inline void readPropertyWithArgs(QObject *target, void *args[]) const + { + if (hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); + else if (isDirect()) + target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); + else + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); + } + + bool writeProperty(QObject *target, void *value, WriteFlags flags) const + { + int status = -1; + void *argv[] = { value, nullptr, &status, &flags }; + if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction()) + staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); + else if (flags.testFlag(BypassInterceptor) && isDirect()) + target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); + else + QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); + return true; + } + + static Flags defaultSignalFlags() + { + Flags f; + f.isSignal = true; + f.type = Flags::FunctionType; + f.isVMESignal = true; + return f; + } + + static Flags defaultSlotFlags() + { + Flags f; + f.type = Flags::FunctionType; + f.isVMEFunction = true; + return f; + } + +private: + friend class QQmlPropertyCache; + void lazyLoad(const QMetaProperty &); + void lazyLoad(const QMetaMethod &); + bool notFullyResolved() const { return m_flags.notFullyResolved; } + + Flags m_flags; + qint16 m_coreIndex = -1; + quint16 m_propType = 0; + + // The notify index is in the range returned by QObjectPrivate::signalIndex(). + // This is different from QMetaMethod::methodIndex(). + qint16 m_notifyIndex = -1; + qint16 m_overrideIndex = -1; + + quint8 m_revision = 0; + quint8 m_typeMinorVersion = 0; + qint16 m_metaObjectOffset = -1; + + QQmlPropertyCacheMethodArguments *m_arguments = nullptr; + StaticMetaCallFunction m_staticMetaCallFunction = nullptr; +}; + +#if QT_POINTER_SIZE == 4 + Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24); +#else // QT_POINTER_SIZE == 8 + Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32); +#endif + +bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const +{ + return flags() == other.flags() && + propType() == other.propType() && + coreIndex() == other.coreIndex() && + notifyIndex() == other.notifyIndex() && + revision() == other.revision(); +} + +QQmlPropertyData::Flags::Flags() + : otherBits(0) + , isConstant(false) + , isWritable(false) + , isResettable(false) + , isAlias(false) + , isFinal(false) + , isOverridden(false) + , isDirect(false) + , type(OtherType) + , isVMEFunction(false) + , hasArguments(false) + , isSignal(false) + , isVMESignal(false) + , isV4Function(false) + , isSignalHandler(false) + , isOverload(false) + , isCloned(false) + , isConstructor(false) + , notFullyResolved(false) + , overrideIndexIsProperty(false) +{} + +bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const +{ + return isConstant == other.isConstant && + isWritable == other.isWritable && + isResettable == other.isResettable && + isAlias == other.isAlias && + isFinal == other.isFinal && + isOverridden == other.isOverridden && + type == other.type && + isVMEFunction == other.isVMEFunction && + hasArguments == other.hasArguments && + isSignal == other.isSignal && + isVMESignal == other.isVMESignal && + isV4Function == other.isV4Function && + isSignalHandler == other.isSignalHandler && + isOverload == other.isOverload && + isCloned == other.isCloned && + isConstructor == other.isConstructor && + notFullyResolved == other.notFullyResolved && + overrideIndexIsProperty == other.overrideIndexIsProperty; +} + +void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from) +{ + switch (from.type) { + case QObjectDerivedType: + case EnumType: + case QListType: + case QmlBindingType: + case QJSValueType: + case QVariantType: + type = from.type; + } +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYDATA_P_H diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp new file mode 100644 index 0000000000..90eaca0b90 --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qqmlpropertyresolver_p.h" + +QT_BEGIN_NAMESPACE + +QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision, + RevisionCheck check) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + + // Find the first property + while (d && d->isFunction()) + d = cache->overrideData(d); + + if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else { + return d; + } +} + + +QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + if (notInRevision) *notInRevision = false; + + while (d && !(d->isFunction())) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return nullptr; + } else if (d && d->isSignal()) { + return d; + } + + if (name.endsWith(QLatin1String("Changed"))) { + QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); + + d = property(propName, notInRevision); + if (d) + return cache->signal(d->notifyIndex()); + } + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyresolver_p.h b/src/qml/qml/qqmlpropertyresolver_p.h new file mode 100644 index 0000000000..df857f242e --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QQMLPROPERTYRESOLVER_P_H +#define QQMLPROPERTYRESOLVER_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/qtqmlglobal_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlrefcount_p.h> + +QT_BEGIN_NAMESPACE + +struct Q_QML_EXPORT QQmlPropertyResolver +{ + QQmlPropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) + : cache(cache) + {} + + QQmlPropertyData *property(int index) const + { + return cache->property(index); + } + + enum RevisionCheck { + CheckRevision, + IgnoreRevision + }; + + QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, + RevisionCheck check = CheckRevision) const; + + // This code must match the semantics of QQmlPropertyPrivate::findSignalByName + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; + + QQmlRefPointer<QQmlPropertyCache> cache; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYRESOLVER_P_H diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index d20efe616b..6959b05105 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -40,12 +40,29 @@ #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 -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +static bool isPrimitiveType(int typeId) +{ + switch (typeId) { +#define HANDLE_PRIMITIVE(Type, id, T) \ + case QMetaType::Type: +QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE); +#undef HANDLE_PRIMITIVE + return true; + default: + return false; + } +} + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) : enginePrivate(enginePrivate) , compilationUnit(compilationUnit) , imports(imports) @@ -56,7 +73,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, c bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); } -QVector<QQmlCompileError> QQmlPropertyValidator::validate() +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validate() { return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); } @@ -79,7 +96,8 @@ struct BindingFinder } }; -QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( + int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const { const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); @@ -92,7 +110,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); if (!propertyCache) - return QVector<QQmlCompileError>(); + return QVector<QQmlJS::DiagnosticMessage>(); QQmlCustomParser *customParser = nullptr; if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { @@ -121,7 +139,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, groupProperties.insert(pos, binding); } - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QString defaultPropertyName; QQmlPropertyData *defaultProperty = nullptr; @@ -134,7 +152,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, 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) { @@ -164,7 +182,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, pd = propertyResolver.signal(name, ¬InRevision); } else { pd = propertyResolver.property(name, ¬InRevision, - QmlIR::PropertyResolver::CheckRevision); + QQmlPropertyResolver::CheckRevision); } if (notInRevision) { @@ -202,7 +220,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, = pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); - const QVector<QQmlCompileError> subObjectValidatorErrors + const QVector<QQmlJS::DiagnosticMessage> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, populatingValueTypeGroupProperty); if (!subObjectValidatorErrors.isEmpty()) @@ -259,12 +277,12 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, } if (binding->type < QV4::CompiledData::Binding::Type_Script) { - QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); - if (bindingError.isSet()) + QQmlJS::DiagnosticMessage bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingError.isValid()) return recordError(bindingError); } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); - if (bindingError.isSet()) + QQmlJS::DiagnosticMessage bindingError = validateObjectBinding(pd, name, binding); + if (bindingError.isValid()) return recordError(bindingError); } else if (binding->isGroupProperty()) { if (QQmlValueTypeFactory::isValueType(pd->propType())) { @@ -276,11 +294,21 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, return recordError(binding->location, tr("Invalid grouped property access")); } } else { - if (!enginePrivate->propertyCacheForType(pd->propType())) { + const int typeId = pd->propType(); + if (isPrimitiveType(typeId)) { + return recordError( + binding->location, + tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".") + .arg(name) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) + ); + } + + if (!enginePrivate->propertyCacheForType(typeId)) { return recordError(binding->location, tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(pd->propType()))) + .arg(QString::fromLatin1(QMetaType::typeName(typeId))) ); } } @@ -315,30 +343,30 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, customParser->validator = nullptr; customParser->engine = nullptr; customParser->imports = (QQmlImports*)nullptr; - QVector<QQmlCompileError> parserErrors = customParser->errors(); + QVector<QQmlJS::DiagnosticMessage> parserErrors = customParser->errors(); if (!parserErrors.isEmpty()) return parserErrors; } (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - QVector<QQmlCompileError> noError; + QVector<QQmlJS::DiagnosticMessage> noError; return noError; } -QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const { if (property->isQList()) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); + return qQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); } - QQmlCompileError noError; + QQmlJS::DiagnosticMessage noError; if (property->isEnum()) { if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) return noError; - QString value = binding->valueAsString(compilationUnit.data()); + QString value = compilationUnit->bindingValueAsString(binding); QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); bool ok; if (p.isFlagType()) { @@ -347,7 +375,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache p.enumerator().keyToValue(value.toUtf8().constData(), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); + return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); } return noError; } @@ -364,7 +392,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache enginePrivate->warning(warning); return noError; } - return QQmlCompileError(binding->valueLocation, error); + return qQmlCompileError(binding->valueLocation, error); }; switch (property->propType()) { @@ -396,7 +424,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::UInt: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); if (double(uint(d)) == d) return noError; } @@ -405,7 +433,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Int: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = binding->valueAsNumber(compilationUnit->constants); + double d = compilationUnit->bindingValueAsNumber(binding); if (double(int(d)) == d) return noError; } @@ -426,7 +454,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Color: { bool ok = false; - QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: color expected")); } @@ -435,7 +463,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache #if QT_CONFIG(datestring) case QVariant::Date: { bool ok = false; - QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: date expected")); } @@ -443,7 +471,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Time: { bool ok = false; - QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: time expected")); } @@ -451,7 +479,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::DateTime: { bool ok = false; - QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: datetime expected")); } @@ -460,7 +488,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache #endif // datestring case QVariant::Point: { bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -468,7 +496,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::PointF: { bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -476,7 +504,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Size: { bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } @@ -484,7 +512,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::SizeF: { bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } @@ -492,7 +520,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::Rect: { bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: rect expected")); } @@ -500,7 +528,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache break; case QVariant::RectF: { bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } @@ -517,7 +545,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float xp; float yp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 2D vector expected")); } } @@ -528,7 +556,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float yp; float zy; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 3D vector expected")); } } @@ -540,7 +568,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float zy; float wp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: 4D vector expected")); } } @@ -552,12 +580,13 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float yp; float zp; } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { + if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { return warnOrError(tr("Invalid property assignment: quaternion expected")); } } break; case QVariant::RegExp: + case QVariant::RegularExpression: return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required @@ -569,7 +598,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache } else if (property->propType() == qMetaTypeId<QList<int> >()) { bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); if (ok) { - double n = binding->valueAsNumber(compilationUnit->constants); + double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) != n) ok = false; } @@ -627,23 +656,23 @@ bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const return false; } -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const { - QVector<QQmlCompileError> errors; - errors.append(QQmlCompileError(location, description)); + QVector<QQmlJS::DiagnosticMessage> errors; + errors.append(qQmlCompileError(location, description)); return errors; } -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const +QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QQmlJS::DiagnosticMessage &error) const { - QVector<QQmlCompileError> errors; + QVector<QQmlJS::DiagnosticMessage> errors; errors.append(error); return errors; } -QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const { - QQmlCompileError noError; + QQmlJS::DiagnosticMessage noError; if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); @@ -667,59 +696,73 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * } if (!isValueSource && !isPropertyInterceptor) { - return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); + return qQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); } return noError; } - if (QQmlMetaType::isInterface(property->propType())) { + const int propType = property->propType(); + const auto rhsType = [&]() { + return stringAt(compilationUnit->objectAt(binding->value.objectIndex) + ->inheritedTypeNameIndex); + }; + + if (QQmlMetaType::isInterface(propType)) { // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) { + } else if (propType == QMetaType::QVariant || propType == qMetaTypeId<QJSValue>()) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType()); + const int listType = enginePrivate->listType(propType); if (!QQmlMetaType::isInterface(listType)) { QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); + return qQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); } } return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType())) { - auto typeName = QMetaType::typeName(property->propType()); - return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") - .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>")) - .arg(propertyName)); - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); + } else if (isPrimitiveType(propType)) { + auto typeName = QMetaType::typeName(propType); + return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting \"%3\"") + .arg(rhsType()) + .arg(propertyName) + .arg(typeName)); + } else if (QQmlValueTypeFactory::isValueType(propType)) { + return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") + .arg(rhsType()).arg(propertyName)); + } else if (propType == qMetaTypeId<QQmlScriptString>()) { + return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { // We want to use the raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability // Using -1 for the minor version ensures that we get the raw metaObject. - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(propType, -1); - // Will be true if the assigned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value if (propertyMetaObject) { + // Will be true if the assigned type inherits propertyMetaObject + // Determine isAssignable value + bool isAssignable = false; QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); while (c && !isAssignable) { isAssignable |= c == propertyMetaObject; c = c->parent(); } - } - if (!isAssignable) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") - .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType())))); + if (!isAssignable) { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") + .arg(rhsType()).arg(QLatin1String(QMetaType::typeName(propType)))); + } + } else { + return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".") + .arg(QLatin1String(QMetaType::typeName(propType)))); } + } return noError; } diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h index e9ae844ccb..74a1281927 100644 --- a/src/qml/compiler/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 @@ -58,32 +64,40 @@ class QQmlPropertyValidator { Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit); + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); - QVector<QQmlCompileError> validate(); + QVector<QQmlJS::DiagnosticMessage> validate(); private: - QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; + QVector<QQmlJS::DiagnosticMessage> validateObject( + int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, + bool populatingValueTypeGroupProperty = false) const; + QQmlJS::DiagnosticMessage validateLiteralBinding( + QQmlPropertyCache *propertyCache, QQmlPropertyData *property, + const QV4::CompiledData::Binding *binding) const; + QQmlJS::DiagnosticMessage validateObjectBinding( + QQmlPropertyData *property, const QString &propertyName, + const QV4::CompiledData::Binding *binding) const; bool canCoerce(int to, QQmlPropertyCache *fromMo) const; - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const; - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const; + Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError( + const QV4::CompiledData::Location &location, const QString &description) const; + Q_REQUIRED_RESULT QVector<QQmlJS::DiagnosticMessage> recordError( + const QQmlJS::DiagnosticMessage &error) const; QString stringAt(int index) const { return compilationUnit->stringAt(index); } - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return compilationUnit->resolvedType(id); } QQmlEnginePrivate *enginePrivate; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QQmlImports &imports; 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..6ac30d3ab5 --- /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.adopt(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.data()); + } + 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..ae268ca904 --- /dev/null +++ b/src/qml/qml/qqmlscriptdata.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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() +{ + 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/types/qquickworkerscript_p.h b/src/qml/qml/qqmlscriptdata_p.h index 1a8d2ab076..80b65b699c 100644 --- a/src/qml/types/qquickworkerscript_p.h +++ b/src/qml/qml/qqmlscriptdata_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_P_H +#ifndef QQMLSCRIPTDATA_P_H +#define QQMLSCRIPTDATA_P_H // // W A R N I N G @@ -51,74 +51,58 @@ // We mean it. // -#include <qqml.h> +#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 <QtQml/qqmlparserstatus.h> -#include <QtCore/qthread.h> -#include <QtQml/qjsvalue.h> #include <QtCore/qurl.h> QT_BEGIN_NAMESPACE - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread +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 { -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr); - ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - void run() override; - private: - QQuickWorkerScriptEnginePrivate *d; -}; + friend class QQmlTypeLoader; -class QQmlV4Function; -class QQmlV4Handle; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + QQmlScriptData(); - Q_INTERFACES(QQmlParserStatus) public: - QQuickWorkerScript(QObject *parent = nullptr); - ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); + QUrl url; + QString urlString; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; -public Q_SLOTS: - void sendMessage(QQmlV4Function*); + QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); -Q_SIGNALS: - void sourceChanged(); - void message(const QQmlV4Handle &messageObject); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; } protected: - void classBegin() override; - void componentComplete() override; - bool event(QEvent *) override; + void clear() override; // From QQmlCleanup private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; + 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 -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H +#endif // QQMLSCRIPTDATA_P_H diff --git a/src/qml/qml/qqmlsourcecoordinate_p.h b/src/qml/qml/qqmlsourcecoordinate_p.h new file mode 100644 index 0000000000..55e11fd147 --- /dev/null +++ b/src/qml/qml/qqmlsourcecoordinate_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 QQMLSOURCECOORDINATE_P_H +#define QQMLSOURCECOORDINATE_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 <limits> + +QT_BEGIN_NAMESPACE + +// These methods are needed because in some public methods we historically interpret -1 as the +// invalid line or column, even though all the lines and columns are 1-based. Also, the different +// integer ranges may turn certain large values into invalid ones on conversion. + +template<typename From, typename To> +To qmlConvertSourceCoordinate(From n); + +template<> +inline quint16 qmlConvertSourceCoordinate<int, quint16>(int n) +{ + return (n > 0 && n <= int(std::numeric_limits<quint16>::max())) ? quint16(n) : 0; +} + +template<> +inline quint32 qmlConvertSourceCoordinate<int, quint32>(int n) +{ + return n > 0 ? quint32(n) : 0u; +} + +// TODO: In Qt6, change behavior and make the invalid coordinate 0 for the following two methods. + +template<> +inline int qmlConvertSourceCoordinate<quint16, int>(quint16 n) +{ + return (n == 0u) ? -1 : int(n); +} + +template<> +inline int qmlConvertSourceCoordinate<quint32, int>(quint32 n) +{ + return (n == 0u || n > quint32(std::numeric_limits<int>::max())) ? -1 : int(n); +} + +QT_END_NAMESPACE + +#endif // QQMLSOURCECOORDINATE_P_H diff --git a/src/qml/qml/qqmlstaticmetaobject.cpp b/src/qml/qml/qqmlstaticmetaobject.cpp new file mode 100644 index 0000000000..218d0134fd --- /dev/null +++ b/src/qml/qml/qqmlstaticmetaobject.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** 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 "qqmlstaticmetaobject_p.h" + +QT_BEGIN_NAMESPACE + +int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy, + QByteArray *unknownTypeError) const +{ + QMetaMethod m = _m.asT2()->constructor(index); + return methodParameterTypes(m, dummy, unknownTypeError); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlstaticmetaobject_p.h b/src/qml/qml/qqmlstaticmetaobject_p.h new file mode 100644 index 0000000000..e1ca496080 --- /dev/null +++ b/src/qml/qml/qqmlstaticmetaobject_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 QQMLSTATICMETAOBJECT_P_H +#define QQMLSTATICMETAOBJECT_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/qqmlobjectorgadget_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlStaticMetaObject : public QQmlObjectOrGadget { +public: + QQmlStaticMetaObject(const QMetaObject* metaObject) + : QQmlObjectOrGadget(metaObject) + {} + int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const; +}; + +QT_END_NAMESPACE + +#endif // QQMLSTATICMETAOBJECT_P_H diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp new file mode 100644 index 0000000000..874bcd4bca --- /dev/null +++ b/src/qml/qml/qqmltype.cpp @@ -0,0 +1,885 @@ +/**************************************************************************** +** +** 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 "qqmltype_p_p.h" + +#include <QtQml/qjsvalue.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> + +#include <private/qqmlcustomparser_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlmetatypedata_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmltypedata_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) + : regType(type), iid(nullptr), typeId(0), listId(0), revision(0), + containsRevisionedAttributes(false), baseMetaObject(nullptr), + index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false), + haveSuperType(false) +{ + switch (type) { + case QQmlType::CppType: + extraData.cd = new QQmlCppTypeData; + extraData.cd->allocationSize = 0; + extraData.cd->newFunc = nullptr; + extraData.cd->parserStatusCast = -1; + extraData.cd->extFunc = nullptr; + extraData.cd->extMetaObject = nullptr; + extraData.cd->customParser = nullptr; + extraData.cd->attachedPropertiesFunc = nullptr; + extraData.cd->attachedPropertiesType = nullptr; + extraData.cd->propertyValueSourceCast = -1; + extraData.cd->propertyValueInterceptorCast = -1; + extraData.cd->registerEnumClassesUnscoped = true; + break; + case QQmlType::SingletonType: + case QQmlType::CompositeSingletonType: + extraData.sd = new QQmlSingletonTypeData; + extraData.sd->singletonInstanceInfo = nullptr; + break; + case QQmlType::InterfaceType: + extraData.cd = nullptr; + break; + case QQmlType::CompositeType: + extraData.fd = new QQmlCompositeTypeData; + break; + default: qFatal("QQmlTypePrivate Internal Error."); + } +} + +QQmlTypePrivate::~QQmlTypePrivate() +{ + qDeleteAll(scopedEnums); + for (const auto &metaObject : metaObjects) + free(metaObject.metaObject); + switch (regType) { + case QQmlType::CppType: + delete extraData.cd->customParser; + delete extraData.cd; + break; + case QQmlType::SingletonType: + case QQmlType::CompositeSingletonType: + delete extraData.sd->singletonInstanceInfo; + delete extraData.sd; + break; + case QQmlType::CompositeType: + delete extraData.fd; + break; + default: //Also InterfaceType, because it has no extra data + break; + } +} + +QQmlType::QQmlType() = default; +QQmlType::QQmlType(const QQmlType &) = default; +QQmlType::QQmlType(QQmlType &&) = default; +QQmlType &QQmlType::operator =(const QQmlType &other) = default; +QQmlType &QQmlType::operator =(QQmlType &&other) = default; +QQmlType::QQmlType(const QQmlTypePrivate *priv) : d(priv) {} +QQmlType::~QQmlType() = default; + +QHashedString QQmlType::module() const +{ + if (!d) + return QHashedString(); + return d->module; +} + +int QQmlType::majorVersion() const +{ + if (!d) + return -1; + return d->version_maj; +} + +int QQmlType::minorVersion() const +{ + if (!d) + return -1; + return d->version_min; +} + +bool QQmlType::availableInVersion(int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; + return vmajor == d->version_maj && vminor >= d->version_min; +} + +bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const +{ + Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; + return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; +} + +QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +{ + Q_ASSERT(isComposite()); + if (!engine) + return QQmlType(); + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); + if (td.isNull() || !td->isComplete()) + return QQmlType(); + QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit(); + const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); + return QQmlMetaType::qmlType(mo); +} + +QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const +{ + // similar logic to resolveCompositeBaseType + Q_ASSERT(isComposite()); + if (!engine) + return nullptr; + QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); + if (td.isNull() || !td->isComplete()) + return nullptr; + QV4::ExecutableCompilationUnit *compilationUnit = td->compilationUnit(); + return compilationUnit->rootPropertyCache().data(); +} + +static bool isPropertyRevisioned(const QMetaObject *mo, int index) +{ + int i = index; + i -= mo->propertyOffset(); + if (i < 0 && mo->d.superdata) + return isPropertyRevisioned(mo->d.superdata, index); + + const QMetaObjectPrivate *mop = reinterpret_cast<const QMetaObjectPrivate*>(mo->d.data); + if (i >= 0 && i < mop->propertyCount) { + int handle = mop->propertyData + 3*i; + int flags = mo->d.data[handle + 2]; + + return (flags & Revisioned); + } + + return false; +} + +void QQmlTypePrivate::init() const +{ + if (isSetup) + return; + + QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); + if (isSetup) + return; + + const QMetaObject *mo = baseMetaObject; + if (!mo) { + // version 0 singleton type without metaobject information + return; + } + + if (regType == QQmlType::CppType) { + // Setup extended meta object + // XXX - very inefficient + if (extraData.cd->extFunc) { + QMetaObjectBuilder builder; + QQmlMetaType::clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, + extraData.cd->extMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = mo; + QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 }; + metaObjects << data; + } + } + + metaObjects.append(QQmlMetaType::proxyData( + mo, baseMetaObject, metaObjects.isEmpty() ? nullptr + : metaObjects.constLast().metaObject)); + + for (int ii = 0; ii < metaObjects.count(); ++ii) { + metaObjects[ii].propertyOffset = + metaObjects.at(ii).metaObject->propertyOffset(); + metaObjects[ii].methodOffset = + metaObjects.at(ii).metaObject->methodOffset(); + } + + // Check for revisioned details + { + const QMetaObject *mo = nullptr; + if (metaObjects.isEmpty()) + mo = baseMetaObject; + else + mo = metaObjects.constFirst().metaObject; + + for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { + if (isPropertyRevisioned(mo, ii)) + containsRevisionedAttributes = true; + } + + for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { + if (mo->method(ii).revision() != 0) + containsRevisionedAttributes = true; + } + } + + isSetup = true; + lock.unlock(); +} + +void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const +{ + const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite()) + ? compositePropertyCache(engine) + : nullptr; + + const QMetaObject *metaObject = !isEnumFromCacheSetup + ? baseMetaObject // beware: It could be a singleton type without metaobject + : nullptr; + + if (!cache && !metaObject) + return; + + init(); + + QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); + + if (cache) { + insertEnumsFromPropertyCache(cache); + isEnumFromCacheSetup = true; + } + + if (metaObject) { + insertEnums(metaObject); + isEnumFromBaseSetup = true; + } +} + +void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const +{ + // Add any enum values defined by 'related' classes + if (metaObject->d.relatedMetaObjects) { + const auto *related = metaObject->d.relatedMetaObjects; + if (related) { + while (*related) + insertEnums(*related++); + } + } + + QSet<QString> localEnums; + const QMetaObject *localMetaObject = nullptr; + + // Add any enum values defined by this class, overwriting any inherited values + for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { + QMetaEnum e = metaObject->enumerator(ii); + const bool isScoped = e.isScoped(); + QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr; + + // We allow enums in sub-classes to overwrite enums from base-classes, such as + // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin). + // This is acceptable because the _use_ of the enum from the QML side requires qualification + // anyway, i.e. ListView.Center vs. Item.Center. + // However if a class defines two enums with the same value, then that must produce a warning + // because it represents a valid conflict. + if (e.enclosingMetaObject() != localMetaObject) { + localEnums.clear(); + localMetaObject = e.enclosingMetaObject(); + } + + for (int jj = 0; jj < e.keyCount(); ++jj) { + const QString key = QString::fromUtf8(e.key(jj)); + const int value = e.value(jj); + if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) { + if (localEnums.contains(key)) { + auto existingEntry = enums.find(key); + if (existingEntry != enums.end() && existingEntry.value() != value) { + qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData()); + createEnumConflictReport(metaObject, key); + } + } else { + localEnums.insert(key); + } + enums.insert(key, value); + } + if (isScoped) + scoped->insert(key, value); + } + + if (isScoped) { + scopedEnums << scoped; + scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1); + } + } +} + +void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const +{ + path.append(QString::fromUtf8(metaObject->className())); + + if (metaObject->d.relatedMetaObjects) { + const auto *related = metaObject->d.relatedMetaObjects; + if (related) { + while (*related) + createListOfPossibleConflictingItems(*related++, enumInfoList, path); + } + } + + for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { + const auto e = metaObject->enumerator(ii); + + for (int jj = 0; jj < e.keyCount(); ++jj) { + const QString key = QString::fromUtf8(e.key(jj)); + + EnumInfo enumInfo; + enumInfo.metaObjectName = QString::fromUtf8(metaObject->className()); + enumInfo.enumName = QString::fromUtf8(e.name()); + enumInfo.enumKey = key; + enumInfo.scoped = e.isScoped(); + enumInfo.path = path; + enumInfo.metaEnumScope = QString::fromUtf8(e.scope()); + enumInfoList.append(enumInfo); + } + } +} + +void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const +{ + QList<EnumInfo> enumInfoList; + + if (baseMetaObject) // prefer baseMetaObject if available + metaObject = baseMetaObject; + + if (!metaObject) { // If there is no metaObject at all return early + qWarning() << "No meta object information available. Skipping conflict analysis."; + return; + } + + createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList()); + + qWarning().noquote() << QLatin1String("Possible conflicting items:"); + // find items with conflicting key + for (const auto &i : qAsConst(enumInfoList)) { + if (i.enumKey == conflictingKey) + qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope " + << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->")); + } +} + +void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const +{ + const QMetaObject *cppMetaObject = cache->firstCppMetaObject(); + + while (cache && cache->metaObject() != cppMetaObject) { + + int count = cache->qmlEnumCount(); + for (int ii = 0; ii < count; ++ii) { + QStringHash<int> *scoped = new QStringHash<int>(); + QQmlEnumData *enumData = cache->qmlEnum(ii); + + for (int jj = 0; jj < enumData->values.count(); ++jj) { + const QQmlEnumValue &value = enumData->values.at(jj); + enums.insert(value.namedValue, value.value); + scoped->insert(value.namedValue, value.value); + } + scopedEnums << scoped; + scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1); + } + cache = cache->parent(); + } + insertEnums(cppMetaObject); +} + +void QQmlTypePrivate::setName(const QString &uri, const QString &element) +{ + module = uri; + elementName = element; + name = uri.isEmpty() ? element : (uri + QLatin1Char('/') + element); +} + +QByteArray QQmlType::typeName() const +{ + if (d) { + if (d->regType == SingletonType || d->regType == CompositeSingletonType) + return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); + else if (d->baseMetaObject) + return d->baseMetaObject->className(); + } + return QByteArray(); +} + +QString QQmlType::elementName() const +{ + if (!d) + return QString(); + return d->elementName; +} + +QString QQmlType::qmlTypeName() const +{ + if (!d) + return QString(); + return d->name; +} + +QObject *QQmlType::create() const +{ + if (!d || !isCreatable()) + return nullptr; + + d->init(); + + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize); + d->extraData.cd->newFunc(rv); + + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); + + return rv; +} + +void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const +{ + if (!d || !isCreatable()) + return; + + d->init(); + + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory); + d->extraData.cd->newFunc(rv); + + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); + + *out = rv; + *memory = ((char *)rv) + d->extraData.cd->allocationSize; +} + +QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const +{ + if (!d) + return nullptr; + if (d->regType != SingletonType && d->regType != CompositeSingletonType) + return nullptr; + return d->extraData.sd->singletonInstanceInfo; +} + +QQmlCustomParser *QQmlType::customParser() const +{ + if (!d) + return nullptr; + if (d->regType != CppType) + return nullptr; + return d->extraData.cd->customParser; +} + +QQmlType::CreateFunc QQmlType::createFunction() const +{ + if (!d || d->regType != CppType) + return nullptr; + return d->extraData.cd->newFunc; +} + +QString QQmlType::noCreationReason() const +{ + if (!d || d->regType != CppType) + return QString(); + return d->extraData.cd->noCreationReason; +} + +bool QQmlType::isCreatable() const +{ + return d && d->regType == CppType && d->extraData.cd->newFunc; +} + +QQmlType::ExtensionFunc QQmlType::extensionFunction() const +{ + if (!d || d->regType != CppType) + return nullptr; + return d->extraData.cd->extFunc; +} + +bool QQmlType::isExtendedType() const +{ + if (!d) + return false; + d->init(); + + return !d->metaObjects.isEmpty(); +} + +bool QQmlType::isSingleton() const +{ + return d && (d->regType == SingletonType || d->regType == CompositeSingletonType); +} + +bool QQmlType::isInterface() const +{ + return d && d->regType == InterfaceType; +} + +bool QQmlType::isComposite() const +{ + return d && d->isComposite(); +} + +bool QQmlType::isCompositeSingleton() const +{ + return d && d->regType == CompositeSingletonType; +} + +bool QQmlType::isQObjectSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback; +} + +bool QQmlType::isQJSValueSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback; +} + +int QQmlType::typeId() const +{ + return d ? d->typeId : -1; +} + +int QQmlType::qListTypeId() const +{ + return d ? d->listId : -1; +} + +const QMetaObject *QQmlType::metaObject() const +{ + if (!d) + return nullptr; + d->init(); + + if (d->metaObjects.isEmpty()) + return d->baseMetaObject; + else + return d->metaObjects.constFirst().metaObject; + +} + +const QMetaObject *QQmlType::baseMetaObject() const +{ + return d ? d->baseMetaObject : nullptr; +} + +bool QQmlType::containsRevisionedAttributes() const +{ + if (!d) + return false; + d->init(); + + return d->containsRevisionedAttributes; +} + +int QQmlType::metaObjectRevision() const +{ + return d ? d->revision : -1; +} + +QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const +{ + if (!d) + return nullptr; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesFunc; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesFunction(engine); +} + +const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const +{ + if (!d) + return nullptr; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesType; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesType(engine); +} + +#if QT_DEPRECATED_SINCE(5, 14) +/* +This is the id passed to qmlAttachedPropertiesById(). This is different from the index +for the case that a single class is registered under two or more names (eg. Item in +Qt 4.7 and QtQuick 1.0). +*/ +int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const +{ + if (!d) + return -1; + if (d->regType == CppType) + return d->extraData.cd->attachedPropertiesType ? d->index : -1; + + QQmlType base; + if (d->regType == CompositeType) + base = d->resolveCompositeBaseType(engine); + return base.attachedPropertiesId(engine); +} +#endif + +int QQmlType::parserStatusCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->parserStatusCast; +} + +int QQmlType::propertyValueSourceCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueSourceCast; +} + +int QQmlType::propertyValueInterceptorCast() const +{ + if (!d || d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueInterceptorCast; +} + +const char *QQmlType::interfaceIId() const +{ + if (!d || d->regType != InterfaceType) + return nullptr; + return d->iid; +} + +int QQmlType::index() const +{ + return d ? d->index : -1; +} + +QUrl QQmlType::sourceUrl() const +{ + return d ? d->sourceUrl() : QUrl(); +} + +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->enums.value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->enums.value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->enums.value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->scopedEnumIndex.value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->scopedEnumIndex.value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const +{ + Q_UNUSED(engine) + Q_ASSERT(ok); + *ok = true; + + if (d) { + Q_ASSERT(index > -1 && index < d->scopedEnums.count()); + int *rv = d->scopedEnums.at(index)->value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const +{ + Q_UNUSED(engine) + Q_ASSERT(ok); + *ok = true; + + if (d) { + Q_ASSERT(index > -1 && index < d->scopedEnums.count()); + int *rv = d->scopedEnums.at(index)->value(name); + if (rv) + return *rv; + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); + if (rv) { + int index = *rv; + Q_ASSERT(index > -1 && index < d->scopedEnums.count()); + rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length())); + if (rv) + return *rv; + } + } + + *ok = false; + return -1; +} + +int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const +{ + Q_ASSERT(ok); + if (d) { + *ok = true; + + d->initEnums(engine); + + int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); + if (rv) { + int index = *rv; + Q_ASSERT(index > -1 && index < d->scopedEnums.count()); + rv = d->scopedEnums.at(index)->value(QHashedStringRef(name)); + if (rv) + return *rv; + } + } + + *ok = false; + return -1; +} + +void QQmlType::refHandle(const QQmlTypePrivate *priv) +{ + if (priv) + priv->addref(); +} + +void QQmlType::derefHandle(const QQmlTypePrivate *priv) +{ + if (priv) + priv->release(); +} + +int QQmlType::refCount(const QQmlTypePrivate *priv) +{ + if (priv) + return priv->count(); + return -1; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h new file mode 100644 index 0000000000..ec27b38a73 --- /dev/null +++ b/src/qml/qml/qqmltype_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** 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 QQMLTYPE_P_H +#define QQMLTYPE_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 <functional> + +#include <private/qtqmlglobal_p.h> +#include <private/qqmlrefcount_p.h> + +#include <QtQml/qqmlprivate.h> +#include <QtQml/qjsvalue.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QHashedCStringRef; +class QQmlTypePrivate; +class QHashedString; +class QHashedStringRef; +class QQmlCustomParser; +class QQmlEnginePrivate; +class QQmlPropertyCache; + +namespace QV4 { +struct String; +} + +class Q_QML_PRIVATE_EXPORT QQmlType +{ +public: + QQmlType(); + QQmlType(const QQmlType &other); + QQmlType(QQmlType &&other); + QQmlType &operator =(const QQmlType &other); + QQmlType &operator =(QQmlType &&other); + explicit QQmlType(const QQmlTypePrivate *priv); + ~QQmlType(); + + bool operator ==(const QQmlType &other) const { + return d.data() == other.d.data(); + } + + bool isValid() const { return !d.isNull(); } + + QByteArray typeName() const; + QString qmlTypeName() const; + QString elementName() const; + + QHashedString module() const; + int majorVersion() const; + int minorVersion() const; + + bool availableInVersion(int vmajor, int vminor) const; + bool availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const; + + QObject *create() const; + void create(QObject **, void **, size_t) const; + + typedef void (*CreateFunc)(void *); + CreateFunc createFunction() const; + QQmlCustomParser *customParser() const; + + bool isCreatable() const; + typedef QObject *(*ExtensionFunc)(QObject *); + ExtensionFunc extensionFunction() const; + bool isExtendedType() const; + QString noCreationReason() const; + + bool isSingleton() const; + bool isInterface() const; + bool isComposite() const; + bool isCompositeSingleton() const; + bool isQObjectSingleton() const; + bool isQJSValueSingleton() const; + + int typeId() const; + int qListTypeId() const; + + const QMetaObject *metaObject() const; + const QMetaObject *baseMetaObject() const; + int metaObjectRevision() const; + bool containsRevisionedAttributes() const; + + QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; + const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED int attachedPropertiesId(QQmlEnginePrivate *engine) const; +#endif + + int parserStatusCast() const; + const char *interfaceIId() const; + int propertyValueSourceCast() const; + int propertyValueInterceptorCast() const; + + int index() const; + + struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo + { + QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr; + std::function<QObject *(QQmlEngine *, QJSEngine *)> qobjectCallback = {}; + const QMetaObject *instanceMetaObject = nullptr; + QString typeName; + QUrl url; // used by composite singletons + }; + SingletonInstanceInfo *singletonInstanceInfo() const; + + QUrl sourceUrl() const; + + int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; + int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; + + int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; + int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const; + int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const; + int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const; + int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const; + int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const; + + const QQmlTypePrivate *priv() const { return d.data(); } + static void refHandle(const QQmlTypePrivate *priv); + static void derefHandle(const QQmlTypePrivate *priv); + static int refCount(const QQmlTypePrivate *priv); + + enum RegistrationType { + CppType = 0, + SingletonType = 1, + InterfaceType = 2, + CompositeType = 3, + CompositeSingletonType = 4, + AnyRegistrationType = 255 + }; + +private: + friend uint qHash(const QQmlType &t, uint seed); + QQmlRefPointer<const QQmlTypePrivate> d; +}; + +inline uint qHash(const QQmlType &t, uint seed = 0) +{ + return qHash(reinterpret_cast<quintptr>(t.d.data()), seed); +} + +QT_END_NAMESPACE + +#endif // QQMLTYPE_P_H diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h new file mode 100644 index 0000000000..6a2d961de8 --- /dev/null +++ b/src/qml/qml/qqmltype_p_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QQMLTYPE_P_P_H +#define QQMLTYPE_P_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/qqmltype_p.h> +#include <private/qstringhash_p.h> +#include <private/qqmlproxymetaobject_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypePrivate : public QQmlRefCount +{ + Q_DISABLE_COPY_MOVE(QQmlTypePrivate) +public: + QQmlTypePrivate(QQmlType::RegistrationType type); + + void init() const; + void initEnums(QQmlEnginePrivate *engine) const; + void insertEnums(const QMetaObject *metaObject) const; + void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; + + QUrl sourceUrl() const + { + switch (regType) { + case QQmlType::CompositeType: + return extraData.fd->url; + case QQmlType::CompositeSingletonType: + return extraData.sd->singletonInstanceInfo->url; + default: + return QUrl(); + } + } + + bool isComposite() const + { + return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType; + } + + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; + + QQmlType::RegistrationType regType; + + struct QQmlCppTypeData + { + int allocationSize; + void (*newFunc)(void *); + QString noCreationReason; + int parserStatusCast; + QObject *(*extFunc)(QObject *); + const QMetaObject *extMetaObject; + QQmlCustomParser *customParser; + QQmlAttachedPropertiesFunc attachedPropertiesFunc; + const QMetaObject *attachedPropertiesType; + int propertyValueSourceCast; + int propertyValueInterceptorCast; + bool registerEnumClassesUnscoped; + }; + + struct QQmlSingletonTypeData + { + QQmlType::SingletonInstanceInfo *singletonInstanceInfo; + }; + + struct QQmlCompositeTypeData + { + QUrl url; + }; + + union extraData { + QQmlCppTypeData* cd; + QQmlSingletonTypeData* sd; + QQmlCompositeTypeData* fd; + } extraData; + + const char *iid; + QHashedString module; + QString name; + QString elementName; + int version_maj; + int version_min; + int typeId; + int listId; + int revision; + mutable bool containsRevisionedAttributes; + mutable QQmlType superType; + const QMetaObject *baseMetaObject; + + int index; + mutable volatile bool isSetup:1; + mutable volatile bool isEnumFromCacheSetup:1; + mutable volatile bool isEnumFromBaseSetup:1; + mutable bool haveSuperType:1; + mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects; + mutable QStringHash<int> enums; + mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums + mutable QList<QStringHash<int>*> scopedEnums; + + void setName(const QString &uri, const QString &element); + +private: + ~QQmlTypePrivate() override; + + struct EnumInfo { + QStringList path; + QString metaObjectName; + QString enumName; + QString enumKey; + QString metaEnumScope; + bool scoped; + }; + + void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const; + void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPE_P_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index 66d3afc7a0..9ff0e3fb9e 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -39,12 +39,11 @@ #include "qqmltypecompiler_p.h" -#include <private/qqmlirbuilder_p.h> #include <private/qqmlobjectcreator_p.h> #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> -#include <private/qqmldelegatecomponent_p.h> +#include <private/qqmlpropertyresolver_p.h> #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -56,7 +55,7 @@ QT_BEGIN_NAMESPACE QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) @@ -66,7 +65,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type { } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() +QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() { // Build property caches and VME meta object data @@ -83,8 +82,8 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() { QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, engine, this, imports()); - QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); - if (error.isSet()) { + QQmlJS::DiagnosticMessage error = propertyCacheBuilder.buildMetaObjects(); + if (error.isValid()) { recordError(error); return nullptr; } @@ -134,7 +133,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() return nullptr; } - if (!document->javaScriptCompilationUnit) { + if (!document->javaScriptCompilationUnit.unitData()) { // Compile JS binding expressions and signal handlers if necessary { // We can compile script strings ahead of time, but they must be compiled @@ -145,11 +144,11 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> 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->v8engine()->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); } @@ -159,22 +158,17 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile() QmlIR::QmlUnitGenerator qmlGenerator; qmlGenerator.generate(*document, dependencyHasher); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit; + if (!errors.isEmpty()) + return nullptr; + + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit + = QV4::ExecutableCompilationUnit::create(std::move( + document->javaScriptCompilationUnit)); compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = *resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount())); - - if (errors.isEmpty()) - return compilationUnit; - else - return nullptr; -} - -void QQmlTypeCompiler::recordError(QQmlError error) -{ - error.setUrl(url()); - errors << error; + return compilationUnit; } void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) @@ -187,9 +181,14 @@ void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, errors << error; } -void QQmlTypeCompiler::recordError(const QQmlCompileError &error) +void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message) { - recordError(error.location, error.description); + QQmlError error; + error.setDescription(message.message); + error.setLine(message.line); + error.setColumn(message.column); + error.setUrl(url()); + errors << error; } QString QQmlTypeCompiler::stringAt(int idx) const @@ -209,7 +208,7 @@ int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const { - return document->javaScriptCompilationUnit->unitData(); + return document->javaScriptCompilationUnit.unitData(); } const QQmlImports *QQmlTypeCompiler::imports() const @@ -253,11 +252,6 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const return &document->jsGenerator.stringTable; } -void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData) -{ - document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData; -} - QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const { return object->bindingAsString(document, scriptIndex); @@ -298,7 +292,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) , imports(typeCompiler->imports()) , customParsers(typeCompiler->customParserCache()) - , illegalNames(typeCompiler->enginePrivate()->v8engine()->illegalNames()) + , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames()) , propertyCaches(typeCompiler->propertyCaches()) { } @@ -333,18 +327,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedType(binding->propertyNameIndex); QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (!type.isValid()) { - if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { - if (type.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } + if (!type.isValid()) + imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr); const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); if (!attachedType) @@ -358,7 +342,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName)) continue; - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); propertyName.remove(0, 2); @@ -514,7 +498,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings() continue; const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression @@ -723,7 +707,7 @@ void QQmlAliasAnnotator::annotateBindingsToAliases() const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { @@ -755,7 +739,7 @@ void QQmlScriptStringScanner::scan() const QmlIR::Object *obj = qmlObjects.at(i); - QmlIR::PropertyResolver resolver(propertyCache); + QQmlPropertyResolver resolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { @@ -781,9 +765,26 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t { } +static bool isUsableComponent(const QMetaObject *metaObject) +{ + // The metaObject is a component we're interested in if it either is a QQmlComponent itself + // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include + // qqmldelegatecomponent_p.h because it belongs to QtQmlModels. + + if (metaObject == &QQmlComponent::staticMetaObject) + return true; + + for (; metaObject; metaObject = metaObject->superClass()) { + if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0) + return true; + } + + return false; +} + void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) { - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); @@ -802,15 +803,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI firstMetaObject = tr->type.metaObject(); else if (tr->compilationUnit) firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject(); - // 1: test for QQmlComponent - if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject) + if (isUsableComponent(firstMetaObject)) continue; - // 2: test for QQmlAbstractDelegateComponent - while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject) - firstMetaObject = firstMetaObject->superClass(); - if (firstMetaObject) - continue; - // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping + // if here, not a QQmlComponent, so needs wrapping QQmlPropertyData *pd = nullptr; if (binding->propertyNameIndex != quint32(0)) { @@ -846,7 +841,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::CompiledData::ResolvedTypeReference; + auto typeRef = new QV4::ResolvedTypeReference; typeRef->type = componentType; typeRef->majorVersion = componentType.majorVersion(); typeRef->minorVersion = componentType.minorVersion(); @@ -1012,17 +1007,17 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) for (int objectIndex: qAsConst(_objectsWithAliases)) { - QQmlCompileError error; + QQmlJS::DiagnosticMessage error; const auto result = resolveAliasesInObject(objectIndex, &error); - if (error.isSet()) { + if (error.isValid()) { recordError(error); return false; } if (result == AllAliasesResolved) { - QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); - if (error.isSet()) { + QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate); + if (error.isValid()) { recordError(error); return false; } @@ -1050,7 +1045,9 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) return true; } -QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error) +QQmlComponentAndAliasResolver::AliasResolutionResult +QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, + QQmlJS::DiagnosticMessage *error) { const QmlIR::Object * const obj = qmlObjects->at(objectIndex); if (!obj->aliasCount()) @@ -1068,7 +1065,9 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv const int idIndex = alias->idIndex; const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); if (targetObjectIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); break; } @@ -1096,11 +1095,13 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } else { QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); if (!targetCache) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(property.toString())); break; } - QmlIR::PropertyResolver resolver(targetCache); + QQmlPropertyResolver resolver(targetCache); QQmlPropertyData *targetProperty = resolver.property(property.toString()); @@ -1131,7 +1132,9 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv } if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(property.toString())); break; } @@ -1140,19 +1143,42 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv if (!subProperty.isEmpty()) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); if (!valueTypeMetaObject) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } + // could be a deep alias + bool isDeepAlias = subProperty.at(0).isLower(); + if (isDeepAlias) { + isDeepAlias = false; + for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) { + auto binding = *it; + if (compiler->stringAt(binding.propertyNameIndex) == property) { + resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex)); + QQmlPropertyData *actualProperty = resolver.property(subProperty.toString()); + if (actualProperty) { + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex()); + isDeepAlias = true; + } + } + } + } + if (!isDeepAlias) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + } else { - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + int valueTypeIndex = + valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); + } } else { if (targetProperty->isQObject()) alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; @@ -1214,7 +1240,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); - QmlIR::PropertyResolver propertyResolver(propertyCache); + QQmlPropertyResolver propertyResolver(propertyCache); QStringList deferredPropertyNames; { @@ -1253,7 +1279,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) continue; bool notInRevision = false; - pd = propertyResolver.property(name, ¬InRevision, QmlIR::PropertyResolver::CheckRevision); + pd = propertyResolver.property(name, ¬InRevision, + QQmlPropertyResolver::CheckRevision); } bool seenSubObjectWithId = false; @@ -1289,78 +1316,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); - const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors(); - if (!jsErrors.isEmpty()) { - for (const QQmlError &e : jsErrors) - compiler->recordError(e); - 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/compiler/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h index a49b97453f..40b0337848 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -81,7 +81,7 @@ struct QQmlTypeCompiler public: QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher); // --- interface used by QQmlPropertyCacheCreator @@ -91,15 +91,14 @@ public: QString stringAt(int idx) const; QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr; + QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr; // --- - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(); + QQmlRefPointer<QV4::ExecutableCompilationUnit> compile(); QList<QQmlError> compilationErrors() const { return errors; } - void recordError(QQmlError error); void recordError(const QV4::CompiledData::Location &location, const QString &description); - void recordError(const QQmlCompileError &error); + void recordError(const QQmlJS::DiagnosticMessage &error); int registerString(const QString &str); int registerConstant(QV4::ReturnedValue v); @@ -118,7 +117,6 @@ public: QQmlJS::MemoryPool *memoryPool(); QStringRef newStringRef(const QString &string); const QV4::Compiler::StringTableGenerator *stringPool() const; - void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData); const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } @@ -126,7 +124,7 @@ public: void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes->value(id); } @@ -154,15 +152,15 @@ struct QQmlCompilePass protected: void recordError(const QV4::CompiledData::Location &location, const QString &description) const { compiler->recordError(location, description); } - void recordError(const QQmlCompileError &error) + void recordError(const QQmlJS::DiagnosticMessage &error) { compiler->recordError(error); } - QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const + QV4::ResolvedTypeReference *resolvedType(int id) const { return compiler->resolvedType(id); } bool containsResolvedType(int id) const { return compiler->resolvedTypes->contains(id); } - QV4::CompiledData::ResolvedTypeReferenceMap::iterator insertResolvedType( - int id, QV4::CompiledData::ResolvedTypeReference *value) + QV4::ResolvedTypeReferenceMap::iterator insertResolvedType( + int id, QV4::ResolvedTypeReference *value) { return compiler->resolvedTypes->insert(id, value); } QQmlTypeCompiler *compiler; @@ -277,7 +275,7 @@ protected: AllAliasesResolved }; - AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); + AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlJS::DiagnosticMessage *error); QQmlEnginePrivate *enginePrivate; QQmlJS::MemoryPool *pool; @@ -311,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..cfdcf6aad5 --- /dev/null +++ b/src/qml/qml/qqmltypedata.cpp @@ -0,0 +1,846 @@ +/**************************************************************************** +** +** 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; + auto pendingImport = std::make_shared<PendingImport>(this, import); + if (!fetchQmldir(qmldirUrl, pendingImport, 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(engine); + + 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); + qDeleteAll(resolvedTypeCache); + 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 + auto implicitImport = std::make_shared<PendingImport>(); + implicitImport->uri = QLatin1String("."); + 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; + auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd(); + for ( ; it != end; ++it) { + if ((*it)->priority == 0) { + // This import was not resolved + for (auto keyIt = m_unresolvedImports.constBegin(), + keyEnd = m_unresolvedImports.constEnd(); + keyIt != keyEnd; ++keyIt) { + PendingImportPtr import = *keyIt; + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri)); + 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) { + qDeleteAll(*resolvedTypeCache); + resolvedTypeCache->clear(); + 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 || m_waitingOnMe.contains(ref.typeData.data())) { + // 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 5d3b1cedc5..62007effdd 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/qqmlmemoryprofiler_p.h> -#include <private/qqmltypecompiler_p.h> -#include <private/qqmlpropertyvalidator_p.h> -#include <private/qqmlpropertycachecreator_p.h> -#include <private/qv4module_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,830 +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 QQmlCompileError &error) -{ - QQmlError e; - e.setColumn(error.location.column); - e.setLine(error.location.line); - e.setDescription(error.description); - e.setUrl(url()); - setError(e); -} - -void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors) -{ - QList<QQmlError> finalErrors; - finalErrors.reserve(errors.count()); - for (const QQmlCompileError &error: errors) { - QQmlError e; - e.setColumn(error.location.column); - e.setLine(error.location.line); - e.setDescription(error.description); - 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.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status) -{ - while (true) { - int d = _p.load(); - 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.load() & TD_ASYNC_MASK; -} - -void QQmlDataBlob::ThreadData::setIsAsync(bool v) -{ - while (true) { - int d = _p.load(); - 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.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setProgress(quint8 v) -{ - while (true) { - int d = _p.load(); - 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) -{ - QML_MEMORY_SCOPE_URL(b->url()); -#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. @@ -1146,8 +282,6 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) return; } - QML_MEMORY_SCOPE_URL(blob->m_url); - if (QQmlFile::isSynchronous(blob->m_url)) { const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url); if (!QQml_isFileCaseCorrect(fileName)) { @@ -1277,7 +411,6 @@ void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface, void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); d.hasInlineSourceCode = true; @@ -1286,7 +419,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.fileInfo = QFileInfo(fileName); setData(blob, d); @@ -1294,7 +426,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1314,7 +445,6 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit) { - QML_MEMORY_SCOPE_URL(blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1338,6 +468,16 @@ void QQmlTypeLoader::shutdownThread() m_thread->shutdown(); } +QQmlTypeLoader::Blob::PendingImport::PendingImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) +{ + type = static_cast<QV4::CompiledData::Import::ImportType>(quint32(import->type)); + uri = blob->stringAt(import->uriIndex); + qualifier = blob->stringAt(import->qualifierIndex); + majorVersion = import->majorVersion; + minorVersion = import->minorVersion; + location = import->location; +} + QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader) : QQmlDataBlob(url, type, loader), m_importCache(loader) { @@ -1347,11 +487,11 @@ QQmlTypeLoader::Blob::~Blob() { } -bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors) { QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url); - data->setImport(this, import); + data->setImport(this, std::move(import)); data->setPriority(this, priority); if (data->status() == Error) { @@ -1367,26 +507,25 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData: return true; } -bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors) { QString qmldirIdentifier = data->urlString(); QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1); typeLoader()->setQmldirContent(qmldirIdentifier, data->content()); - if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), stringAt(import->uriIndex), stringAt(import->qualifierIndex), qmldirIdentifier, qmldirUrl, errors)) + if (!m_importCache.updateQmldirContent(typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors)) return false; - QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import); - if (it != m_unresolvedImports.end()) { - *it = data->priority(this); - } + if (!loadImportDependencies(import, qmldirIdentifier, errors)) + return false; + + import->priority = data->priority(this); // Release this reference at destruction m_qmldirs << data; - const QString &importQualifier = stringAt(import->qualifierIndex); - if (!importQualifier.isEmpty()) { + if (!import->qualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); @@ -1396,7 +535,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, script.nameSpace, importQualifier); + scriptImported(blob, import->location, script.nameSpace, import->qualifier); } } @@ -1405,36 +544,42 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors) { + return addImport(std::make_shared<PendingImport>(this, import), errors); +} + +bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr import, QList<QQmlError> *errors) +{ Q_ASSERT(errors); QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); - const QString &importUri = stringAt(import->uriIndex); - const QString &importQualifier = stringAt(import->qualifierIndex); if (import->type == QV4::CompiledData::Import::ImportScript) { - QUrl scriptUrl = finalUrl().resolved(QUrl(importUri)); + QUrl scriptUrl = finalUrl().resolved(QUrl(import->uri)); QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, importQualifier, QString()); + scriptImported(blob, import->location, import->qualifier, QString()); } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { QString qmldirFilePath; QString qmldirUrl; - if (QQmlMetaType::isLockedModule(importUri, import->majorVersion)) { + if (QQmlMetaType::isLockedModule(import->uri, import->majorVersion)) { //Locked modules are checked first, to save on filesystem checks - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), false, errors)) return false; - } else if (m_importCache.locateQmldir(importDatabase, importUri, import->majorVersion, import->minorVersion, + } else if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion, &qmldirFilePath, &qmldirUrl)) { // This is a local library import - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, qmldirFilePath, qmldirUrl, false, errors)) return false; - if (!importQualifier.isEmpty()) { + if (!loadImportDependencies(import, qmldirFilePath, errors)) + return false; + + if (!import->qualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); @@ -1444,18 +589,18 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); addDependency(blob.data()); - scriptImported(blob, import->location, script.nameSpace, importQualifier); + scriptImported(blob, import->location, script.nameSpace, import->qualifier); } } } else { // Is this a module? - if (QQmlMetaType::isAnyModule(importUri)) { - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (QQmlMetaType::isAnyModule(import->uri)) { + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), false, errors)) return false; } else { // We haven't yet resolved this import - m_unresolvedImports.insert(import, 0); + m_unresolvedImports << import; QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor(); @@ -1466,13 +611,13 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL : QQmlImportDatabase::Remote); if (!remotePathList.isEmpty()) { // Add this library and request the possible locations for it - if (!m_importCache.addLibraryImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, QString(), QString(), true, errors)) return false; // Probe for all possible locations int priority = 0; - const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(importUri, remotePathList, import->majorVersion, import->minorVersion); + const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion); for (const QString &qmldirPath : qmlDirPaths) { if (interceptor) { QUrl url = interceptor->intercept( @@ -1495,7 +640,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL bool incomplete = false; - QUrl importUrl(importUri); + QUrl importUrl(import->uri); QString path = importUrl.path(); path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir")); importUrl.setPath(path); @@ -1505,7 +650,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL incomplete = true; } - if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion, + if (!m_importCache.addFileImport(importDatabase, import->uri, import->qualifier, import->majorVersion, import->minorVersion, incomplete, errors)) return false; @@ -1523,7 +668,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) if (blob->type() == QQmlDataBlob::QmldirFile) { QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob); - const QV4::CompiledData::Import *import = data->import(this); + PendingImportPtr import = data->import(this); QList<QQmlError> errors; if (!qmldirDataAvailable(data, &errors)) { @@ -1538,16 +683,39 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) } } +bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors) +{ + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri); + for (const QString &implicitImports: qmldir.imports()) { + auto dependencyImport = std::make_shared<PendingImport>(); + dependencyImport->uri = implicitImports; + dependencyImport->qualifier = currentImport->qualifier; + dependencyImport->majorVersion = currentImport->majorVersion; + dependencyImport->minorVersion = currentImport->minorVersion; + if (!addImport(dependencyImport, errors)) + return false; + } + return true; +} + bool QQmlTypeLoader::Blob::isDebugging() const { return typeLoader()->engine()->handle()->debugger() != nullptr; } -bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::diskCacheDisabled() +{ + return disableDiskCache(); +} + +bool QQmlTypeLoader::Blob::diskCacheForced() { - bool resolve = true; + return forceDiskCache(); +} - const QV4::CompiledData::Import *import = data->import(this); +bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +{ + PendingImportPtr import = data->import(this); data->setImport(this, nullptr); int priority = data->priority(this); @@ -1555,10 +723,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa if (import) { // Do we need to resolve this import? - QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import); - if (it != m_unresolvedImports.end()) { - resolve = (*it == 0) || (*it > priority); - } + const bool resolve = (import->priority == 0) || (import->priority > priority); if (resolve) { // This is the (current) best resolution for this import @@ -1566,8 +731,7 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa return false; } - if (it != m_unresolvedImports.end()) - *it = priority; + import->priority = priority; return true; } } @@ -1575,63 +739,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 -{ - return m_parser.errors(uri); -} - -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) -{ - m_parser.setError(error); -} - -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. */ @@ -1785,17 +892,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. @@ -1868,8 +964,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) { - if (path.isEmpty()) + const QChar nullChar(QChar::Null); + if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar)) return false; + Q_ASSERT(path.endsWith(QLatin1Char('/'))); if (path.at(0) == QLatin1Char(':')) { // qrc resource @@ -2106,1215 +1204,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::CompiledData::CompilationUnit *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::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - { - 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(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::CompiledData::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::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, - &pendingGroupPropertyBindings, - engine, m_compiledData.data(), &m_importCache); - QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); - if (error.isSet()) { - setError(error); - return; - } - } - - QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> 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::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; - { - QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); - if (error.isSet()) { - setError(error); - return; - } - } - - QQmlEngine *const engine = typeLoader()->engine(); - - const auto dependencyHasher = [engine, &resolvedTypeCache, this](QCryptographicHash *hash) { - if (!resolvedTypeCache.addToHash(hash, engine)) - return false; - return ::addTypeReferenceChecksumsToHash(m_compositeSingletons, hash, engine); - }; - - // 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<QQmlCompileError> 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())); - QmlIR::IRLoader loader(unit, m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit.adopt(new 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()->v8Engine->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.loc.startLine); - e.setColumn(msg.loc.startColumn); - e.setDescription(msg.message); - errors << e; - } - setError(errors); - return false; - } - return true; -} - -void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) -{ - m_document.reset(new QmlIR::Document(isDebugging())); - QmlIR::IRLoader loader(unit->unitData(), m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = 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::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher) -{ - Q_ASSERT(m_compiledData.isNull()); - - const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && 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(); -} - -QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::CompiledData::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::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::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()); - } - QQmlCompileError 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::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading(); - 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; - } - - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit; - - if (m_isModule) { - QList<QQmlJS::DiagnosticMessage> diagnostics; - unit = QV4::ExecutionEngine::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; - unit = QV4::Script::precompile( - &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), - source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML); - // No need to addref on unit, it's initial refcount is 1 - source.clear(); - if (!errors.isEmpty()) { - setError(errors); - return; - } - if (!unit) { - unit.adopt(new QV4::CompiledData::CompilationUnit); - } - irUnit.javaScriptCompilationUnit = unit; - - QmlIR::QmlUnitGenerator qmlGenerator; - qmlGenerator.generate(irUnit); - } - - if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) { - QString errorString; - if (unit->saveToDisk(url(), &errorString)) { - QString error; - if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { - // ignore error, keep using the in-memory compilation unit. - } - } else { - qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString; - } - } - - initializeFromCompilationUnit(unit); -} - -void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) -{ - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - compilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString())); - initializeFromCompilationUnit(compilationUnit); -} - -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::CompiledData::CompilationUnit> &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::CompiledData::CompilationUnit> 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 987e47222d..612d6777ed 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -51,213 +51,27 @@ // 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 <QtCore/qcache.h> +#include <QtCore/qmutex.h> -#include <private/qv4value_p.h> -#include <private/qv4script_p.h> +#include <memory> QT_BEGIN_NAMESPACE -class QQmlScriptData; class QQmlScriptBlob; class QQmlQmldirData; -class QQmlTypeLoader; -class QQmlComponentPrivate; class QQmlTypeData; -class QQmlTypeLoader; class QQmlExtensionInterface; class QQmlProfiler; -struct QQmlCompileError; - -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 QQmlCompileError &error); - void setError(const QVector<QQmlCompileError> &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 { @@ -275,11 +89,31 @@ public: void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + struct PendingImport + { + QV4::CompiledData::Import::ImportType type = QV4::CompiledData::Import::ImportType::ImportLibrary; + + QString uri; + QString qualifier; + + int majorVersion = -1; + int minorVersion = -1; + + QV4::CompiledData::Location location; + + int priority = 0; + + PendingImport() = default; + PendingImport(Blob *blob, const QV4::CompiledData::Import *import); + }; + using PendingImportPtr = std::shared_ptr<PendingImport>; + protected: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); + bool addImport(PendingImportPtr import, QList<QQmlError> *errors); - bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors); - bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors); + bool fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors); + bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors); private: virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *); @@ -288,13 +122,18 @@ public: void dependencyComplete(QQmlDataBlob *) override; + bool loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors); + protected: virtual QString stringAt(int) const { return QString(); } bool isDebugging() const; + static bool diskCacheDisabled(); + static bool diskCacheForced(); + QQmlImports m_importCache; - QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; + QVector<PendingImportPtr> m_unresolvedImports; QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs; QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; @@ -418,209 +257,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::CompiledData::CompilationUnit *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(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit); - void continueLoadFromIR(); - void resolveTypes(); - QQmlCompileError buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache - ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::CompiledData::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::CompiledData::CompilationUnit> 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::CompiledData::CompilationUnit> 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::CompiledData::CompilationUnit> 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::CompiledData::CompilationUnit> &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..8e983db756 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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); + const auto parseErrors = m_parser.errors(uri); + for (const auto &parseError : parseErrors) { + 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(); +} + +QStringList QQmlTypeLoaderQmldirContent::imports() const +{ + return m_parser.imports(); +} + +QString QQmlTypeLoaderQmldirContent::pluginLocation() const +{ + return m_location; +} + +bool QQmlTypeLoaderQmldirContent::designerSupported() const +{ + return m_parser.designerSupported(); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h index 122c7fcb30..698643c7ec 100644 --- a/src/qml/types/qquickpackage_p.h +++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H +#ifndef QQMLTYPELOADERQMLDIRCONTENT_P_H +#define QQMLTYPELOADERQMLDIRCONTENT_P_H // // W A R N I N G @@ -51,51 +51,45 @@ // We mean it. // -#include <qqml.h> +#include <private/qqmldirparser_p.h> QT_BEGIN_NAMESPACE -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject +class QQmlError; +class QQmlTypeLoaderQmldirContent { - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) +private: + friend class QQmlTypeLoader; - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty<QObject> data READ data) + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); public: - QQuickPackage(QObject *parent=nullptr); - virtual ~QQuickPackage(); + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; - QQmlListProperty<QObject> data(); + bool hasContent() const { return m_hasContent; } + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; - QObject *part(const QString & = QString()); - bool hasPart(const QString &); + QString typeNamespace() const; - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + QStringList imports() const; -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); + QString pluginLocation() const; - QString name() const; - void setName(const QString &n); + bool designerSupported() const; - static QHash<QObject *, QQuickPackageAttached *> attached; private: - QString _name; + QQmlDirParser m_parser; + QString m_location; + bool m_hasContent = false; }; QT_END_NAMESPACE -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKPACKAGE_H +#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/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp new file mode 100644 index 0000000000..9c9bf3e48f --- /dev/null +++ b/src/qml/qml/qqmltypemodule.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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 "qqmltypemodule_p_p.h" + +#include <private/qqmltype_p_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeModule::QQmlTypeModule(const QString &module, int majorVersion) + : d(new QQmlTypeModulePrivate(module, majorVersion)) +{ +} + +QQmlTypeModule::~QQmlTypeModule() +{ + delete d; +} + +QString QQmlTypeModule::module() const +{ + // No need to lock. d->module is const + return d->module; +} + +int QQmlTypeModule::majorVersion() const +{ + // No need to lock. d->majorVersion is const + return d->majorVersion; +} + +int QQmlTypeModule::minimumMinorVersion() const +{ + return d->minMinorVersion.loadRelaxed(); +} + +int QQmlTypeModule::maximumMinorVersion() const +{ + return d->maxMinorVersion.loadRelaxed(); +} + +void QQmlTypeModule::addMinorVersion(int version) +{ + for (int oldVersion = d->minMinorVersion.loadRelaxed(); + oldVersion > version && !d->minMinorVersion.testAndSetOrdered(oldVersion, version); + oldVersion = d->minMinorVersion.loadRelaxed()) { + } + + for (int oldVersion = d->maxMinorVersion.loadRelaxed(); + oldVersion < version && !d->maxMinorVersion.testAndSetOrdered(oldVersion, version); + oldVersion = d->maxMinorVersion.loadRelaxed()) { + } +} + +void QQmlTypeModule::add(QQmlTypePrivate *type) +{ + QMutexLocker lock(&d->mutex); + addMinorVersion(type->version_min); + + QList<QQmlTypePrivate *> &list = d->typeHash[type->elementName]; + for (int ii = 0; ii < list.count(); ++ii) { + Q_ASSERT(list.at(ii)); + if (list.at(ii)->version_min < type->version_min) { + list.insert(ii, type); + return; + } + } + list.append(type); +} + +void QQmlTypeModule::remove(const QQmlTypePrivate *type) +{ + QMutexLocker lock(&d->mutex); + for (auto elementIt = d->typeHash.begin(); elementIt != d->typeHash.end();) { + QQmlMetaType::removeQQmlTypePrivate(elementIt.value(), type); + +#if 0 + if (list.isEmpty()) + elementIt = typeHash.erase(elementIt); + else + ++elementIt; +#else + ++elementIt; +#endif + } +} + +bool QQmlTypeModule::isLocked() const +{ + return d->locked.loadRelaxed() != 0; +} + +void QQmlTypeModule::lock() +{ + d->locked.storeRelaxed(1); +} + +QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const +{ + QMutexLocker lock(&d->mutex); + QList<QQmlTypePrivate *> *types = d->typeHash.value(name); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); + } + + return QQmlType(); +} + +QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const +{ + QMutexLocker lock(&d->mutex); + QList<QQmlTypePrivate *> *types = d->typeHash.value(name); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); + } + + return QQmlType(); +} + +void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const +{ + QMutexLocker lock(&d->mutex); + for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end(); + typeCandidates != end; ++typeCandidates) { + for (auto type: typeCandidates.value()) { + if (type->regType == QQmlType::CompositeSingletonType) + callback(QQmlType(type)); + } + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h new file mode 100644 index 0000000000..b84a91b5db --- /dev/null +++ b/src/qml/qml/qqmltypemodule_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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 QQMLTYPEMODULE_P_H +#define QQMLTYPEMODULE_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/qstring.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +class QQmlType; +class QQmlTypePrivate; +struct QQmlMetaTypeData; +class QHashedString; +class QHashedStringRef; + +namespace QV4 { +struct String; +} + +class QQmlTypeModulePrivate; +class QQmlTypeModule +{ +public: + QQmlTypeModule(const QString &uri = QString(), int majorVersion = 0); + ~QQmlTypeModule(); + + void add(QQmlTypePrivate *); + void remove(const QQmlTypePrivate *type); + + bool isLocked() const; + void lock(); + + QString module() const; + int majorVersion() const; + + void addMinorVersion(int minorVersion); + int minimumMinorVersion() const; + int maximumMinorVersion() const; + + QQmlType type(const QHashedStringRef &, int) const; + QQmlType type(const QV4::String *, int) const; + + void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const; + +private: + QQmlTypeModulePrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULE_P_H diff --git a/src/qml/qml/qqmltypemodule_p_p.h b/src/qml/qml/qqmltypemodule_p_p.h new file mode 100644 index 0000000000..b1dab1c4a0 --- /dev/null +++ b/src/qml/qml/qqmltypemodule_p_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 QQMLTYPEMODULE_P_P_H +#define QQMLTYPEMODULE_P_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/qqmltypemodule_p.h> +#include <private/qstringhash_p.h> +#include <private/qqmlmetatypedata_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeModulePrivate +{ +public: + QQmlTypeModulePrivate(QString module, int majorVersion) : + module(std::move(module)), majorVersion(majorVersion) + {} + + const QString module; + const int majorVersion = 0; + + // Can only ever decrease + QAtomicInt minMinorVersion = std::numeric_limits<int>::max(); + + // Can only ever increase + QAtomicInt maxMinorVersion = 0; + + // Bool. Can only be set to 1 once. + QAtomicInt locked = 0; + + typedef QStringHash<QList<QQmlTypePrivate *> > TypeHash; + TypeHash typeHash; + + QMutex mutex; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULE_P_P_H diff --git a/src/qml/qml/qqmltypemoduleversion.cpp b/src/qml/qml/qqmltypemoduleversion.cpp new file mode 100644 index 0000000000..bbbfa1a7b6 --- /dev/null +++ b/src/qml/qml/qqmltypemoduleversion.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 "qqmltypemoduleversion_p.h" + +#include <private/qqmltype_p.h> +#include <private/qqmltypemodule_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeModuleVersion::QQmlTypeModuleVersion() + : m_module(nullptr), m_minor(0) +{ +} + +QQmlTypeModuleVersion::QQmlTypeModuleVersion(QQmlTypeModule *module, int minor) + : m_module(module), m_minor(minor) +{ + Q_ASSERT(m_module); + Q_ASSERT(m_minor >= 0); +} + +QQmlTypeModuleVersion::QQmlTypeModuleVersion(const QQmlTypeModuleVersion &o) + : m_module(o.m_module), m_minor(o.m_minor) +{ +} + +QQmlTypeModuleVersion &QQmlTypeModuleVersion::operator=(const QQmlTypeModuleVersion &o) +{ + m_module = o.m_module; + m_minor = o.m_minor; + return *this; +} + +QQmlTypeModule *QQmlTypeModuleVersion::module() const +{ + return m_module; +} + +int QQmlTypeModuleVersion::minorVersion() const +{ + return m_minor; +} + +QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const +{ + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); +} + +QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const +{ + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypemoduleversion_p.h b/src/qml/qml/qqmltypemoduleversion_p.h new file mode 100644 index 0000000000..20f4709ecb --- /dev/null +++ b/src/qml/qml/qqmltypemoduleversion_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 QQMLTYPEMODULEVERSION_P_H +#define QQMLTYPEMODULEVERSION_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> + +QT_BEGIN_NAMESPACE + +class QQmlTypeModule; +class QQmlType; +class QHashedStringRef; + +namespace QV4 { +struct String; +} + +class QQmlTypeModuleVersion +{ +public: + QQmlTypeModuleVersion(); + QQmlTypeModuleVersion(QQmlTypeModule *, int); + QQmlTypeModuleVersion(const QQmlTypeModuleVersion &); + QQmlTypeModuleVersion &operator=(const QQmlTypeModuleVersion &); + + QQmlTypeModule *module() const; + int minorVersion() const; + + QQmlType type(const QHashedStringRef &) const; + QQmlType type(const QV4::String *) const; + +private: + QQmlTypeModule *m_module; + int m_minor; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEMODULEVERSION_P_H diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 28b5e7f0ad..b98fe77ed5 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -55,8 +55,9 @@ #include "qqmlcleanup_p.h" #include "qqmlmetatype_p.h" -#include <private/qhashedstring_p.h> +#include <private/qstringhash_p.h> #include <private/qqmlimport_p.h> +#include <private/qqmltypemoduleversion_p.h> #include <QtCore/qvector.h> diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index c5fa4a04ec..ef4a628a04 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -38,10 +38,11 @@ ****************************************************************************/ #include "qqmltypewrapper_p.h" -#include <private/qv8engine_p.h> #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> @@ -89,10 +90,8 @@ QObject* QQmlTypeWrapper::singletonObject() const if (!isSingleton()) return nullptr; - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - return siinfo->qobjectApi(e); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + return e->singletonInstance<QObject*>(d()->type()); } QVariant QQmlTypeWrapper::toVariant() const @@ -100,13 +99,12 @@ QVariant QQmlTypeWrapper::toVariant() const if (!isSingleton()) return QVariant::fromValue<QObject *>(d()->object); - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) - return QVariant::fromValue<QObject*>(qobjectSingleton); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + const QQmlType type = d()->type(); + if (type.isQJSValueSingleton()) + return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type)); - return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e)); + return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type)); } @@ -194,50 +192,51 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // singleton types are handled differently to other types. if (type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - // check for enum value - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (includeEnums && name->startsWithUpper()) { - bool ok = false; - int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return QV4::Value::fromInt32(value).asReturnedValue(); - - value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); - if (ok) { - Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); - enumWrapper->d()->typePrivate = type.priv(); - QQmlType::refHandle(enumWrapper->d()->typePrivate); - enumWrapper->d()->scopeEnumIndex = value; - return enumWrapper.asReturnedValue(); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + QJSValue scriptSingleton; + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + // check for enum value + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (includeEnums && name->startsWithUpper()) { + bool ok = false; + int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return QV4::Value::fromInt32(value).asReturnedValue(); + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + return enumWrapper.asReturnedValue(); + } } - } - // check for property. - bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); - if (hasProperty) - *hasProperty = ok; - - // Warn when attempting to access a lowercased enum value, singleton case - if (!ok && includeEnums && !name->startsWithUpper()) { - enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return throwLowercaseEnumError(v4, name, type); - } + // check for property. + bool ok; + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + if (hasProperty) + *hasProperty = ok; + + // Warn when attempting to access a lowercased enum value, singleton case + if (!ok && includeEnums && !name->startsWithUpper()) { + enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return throwLowercaseEnumError(v4, name, type); + } - return result; - } else if (!siinfo->scriptApi(e).isUndefined()) { - // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); - if (!!o) - return o->get(name); + return result; + } + } else if (type.isQJSValueSingleton()) { + QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + if (!scriptSingleton.isUndefined()) { + // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, scriptSingleton)); + if (!!o) + return o->get(name); + } } // Fall through to base implementation @@ -342,21 +341,22 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); return false; } else if (type.isSingleton()) { - QQmlEngine *e = scope.engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); - } else if (!siinfo->scriptApi(e).isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e))); - if (!apiprivate) { - QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - scope.engine->throwError(error); - return false; - } else { - return apiprivate->put(name, value); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine()); + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) + return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + + } else { + QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + if (!scriptSingleton.isUndefined()) { + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton)); + if (!apiprivate) { + QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); + scope.engine->throwError(error); + return false; + } else { + return apiprivate->put(name, value); + } } } } @@ -419,7 +419,7 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return Encode(false); QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); - CompiledData::CompilationUnit *cu = td->compilationUnit(); + ExecutableCompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); @@ -448,27 +448,25 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, if (type.isValid()) { if (type.isSingleton()) { - QQmlEngine *e = engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (!includeEnums || !name->startsWithUpper()) { - QQmlData *ddata = QQmlData::get(qobjectSingleton, false); - if (ddata && ddata->propertyCache) { - QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); - if (property) { - ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); - lookup->qobjectLookup.qmlTypeIc = This->internalClass(); - lookup->qobjectLookup.ic = val->objectValue()->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = property; - lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; - return lookup->getter(lookup, engine, *object); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (!includeEnums || !name->startsWithUpper()) { + QQmlData *ddata = QQmlData::get(qobjectSingleton, false); + if (ddata && ddata->propertyCache) { + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + if (property) { + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + lookup->qobjectLookup.qmlTypeIc = This->internalClass(); + lookup->qobjectLookup.ic = val->objectValue()->internalClass(); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + return lookup->getter(lookup, engine, *object); + } + // Fall through to base implementation } // Fall through to base implementation } @@ -478,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, } // Fall through to base implementation } + + if (name->startsWithUpper()) { + bool ok = false; + int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + lookup->qmlEnumValueLookup.ic = This->internalClass(); + lookup->qmlEnumValueLookup.encodedEnumValue + = QV4::Value::fromInt32(value).asReturnedValue(); + lookup->getter = QQmlTypeWrapper::lookupEnumValue; + return lookup->getter(lookup, engine, *object); + } + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper( + scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + + lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); + lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper + = static_cast<Heap::Object*>(enumWrapper->heapObject()); + lookup->getter = QQmlTypeWrapper::lookupScopedEnum; + return enumWrapper.asReturnedValue(); + } + // Fall through to base implementation + } // Fall through to base implementation } return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); @@ -509,22 +535,46 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin if (!type.isValid()) return revertLookup(); - if (!type.isSingleton()) + if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) return revertLookup(); - QQmlEngine *e = engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (!qobjectSingleton) - return revertLookup(); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); + Q_ASSERT(qobjectSingleton); Scope scope(engine); ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return l->qmlEnumValueLookup.encodedEnumValue; +} + +ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + Scope scope(engine); + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); + + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { + QQmlType::derefHandle(enumWrapper->d()->typePrivate); + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return enumWrapper.asReturnedValue(); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 5f3cb2523f..7dc3f55310 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -81,7 +81,7 @@ struct QQmlTypeWrapper : Object { QQmlType type() const; - QQmlTypePrivate *typePrivate; + const QQmlTypePrivate *typePrivate; QQmlTypeNameCache *typeNamespace; const QQmlImportRef *importNamespace; }; @@ -90,7 +90,7 @@ struct QQmlScopedEnumWrapper : Object { void init() { Object::init(); } void destroy(); int scopeEnumIndex; - QQmlTypePrivate *typePrivate; + const QQmlTypePrivate *typePrivate; QQmlType type() const; }; @@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base); protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 6fd0f0d37c..a4737b3da9 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -38,12 +38,14 @@ ****************************************************************************/ #include "qqmlvaluetype_p.h" -#include "qqmlmetatype_p.h" +#include <QtCore/qmutex.h> #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 @@ -63,32 +65,34 @@ struct QQmlValueTypeFactoryImpl QQmlValueType *valueTypes[QVariant::UserType]; QHash<int, QQmlValueType *> userTypes; QMutex mutex; + + QQmlValueType invalidValueType; }; QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { - std::fill_n(valueTypes, int(QVariant::UserType), nullptr); + std::fill_n(valueTypes, int(QVariant::UserType), &invalidValueType); +#if QT_CONFIG(qml_itemmodel) // See types wrapped in qqmlmodelindexvaluetype_p.h qRegisterMetaType<QItemSelectionRange>(); +#endif } QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() { - qDeleteAll(valueTypes, valueTypes + QVariant::UserType); + for (QQmlValueType *type : valueTypes) { + if (type != &invalidValueType) + delete type; + } qDeleteAll(userTypes); } -bool QQmlValueTypeFactoryImpl::isValueType(int idx) +bool isInternalType(int idx) { - if (idx >= QMetaType::User) - return valueType(idx) != nullptr; - - if (idx < 0) - return false; - // Qt internal types switch (idx) { + case QMetaType::UnknownType: case QMetaType::QStringList: case QMetaType::QObjectStar: case QMetaType::VoidStar: @@ -97,12 +101,20 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx) case QMetaType::QLocale: case QMetaType::QImage: // scarce type, keep as QVariant case QMetaType::QPixmap: // scarce type, keep as QVariant - return false; - default: return true; + default: + return false; } } +bool QQmlValueTypeFactoryImpl::isValueType(int idx) +{ + if (idx < 0 || isInternalType(idx)) + return false; + + return valueType(idx) != nullptr; +} + const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) { switch (t) { @@ -120,13 +132,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; @@ -158,15 +174,17 @@ QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx) } QQmlValueType *rv = valueTypes[idx]; - if (!rv) { + if (rv == &invalidValueType) { // No need for mutex protection - the most we can lose is a valueType instance // TODO: Investigate the performance/memory characteristics of // removing the preallocated array - if (const QMetaObject *mo = metaObjectForMetaType(idx)) { - rv = new QQmlValueType(idx, mo); - valueTypes[idx] = rv; - } + if (isInternalType(idx)) + rv = valueTypes[idx] = nullptr; + else if (const QMetaObject *mo = metaObjectForMetaType(idx)) + rv = valueTypes[idx] = new QQmlValueType(idx, mo); + else + rv = valueTypes[idx] = nullptr; } return rv; @@ -196,6 +214,13 @@ void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing"); } +QQmlValueType::QQmlValueType() : + _metaObject(nullptr), + gadgetPtr(nullptr), + metaType(QMetaType::UnknownType) +{ +} + QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) : gadgetPtr(QMetaType::create(typeId)) , metaType(typeId) @@ -213,7 +238,7 @@ QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) QQmlValueType::~QQmlValueType() { QObjectPrivate *op = QObjectPrivate::get(this); - Q_ASSERT(op->metaObject == this); + Q_ASSERT(op->metaObject == nullptr || op->metaObject == this); op->metaObject = nullptr; ::free(const_cast<QMetaObject *>(_metaObject)); metaType.destroy(gadgetPtr); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 89f1b71d61..cf53b8cb4a 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlValueType : public QObject, public QAbstractDynamicMetaObject { public: + QQmlValueType(); QQmlValueType(int userType, const QMetaObject *metaObject); ~QQmlValueType() override; void read(QObject *, int); @@ -90,7 +91,7 @@ public: class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory { public: - static bool isValueType(int); + static bool isValueType(int idx); static QQmlValueType *valueType(int idx); static const QMetaObject *metaObjectForMetaType(int type); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 7df5757b95..cf6553d129 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qqmlvaluetypewrapper_p.h" -#include <private/qv8engine_p.h> + #include <private/qqmlvaluetype_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlglobal_p.h> diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 6bc469c836..abdc686ec2 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -57,6 +57,8 @@ #include <private/qv4scopedvalue_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> QT_BEGIN_NAMESPACE @@ -162,8 +164,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() QQmlData *targetDData = QQmlData::get(target, /*create*/false); if (!targetDData) return; - int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex(); + QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); + int coreIndex = encodedIndex.coreIndex(); + int valueTypeIndex = encodedIndex.valueTypeIndex(); const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); + if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) { + // deep alias + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine()); + auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType()); + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + Q_ASSERT(newPropertyCache); + pd = newPropertyCache->property(valueTypeIndex); + } if (!pd) return; @@ -316,7 +329,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, - const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId) + const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), engine(engine), ctxt(QQmlData::get(obj, true)->outerContext), @@ -594,7 +607,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const QV4::Scope scope(engine); QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id)); if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) { - QVariant variant(qVariantFromValue(QList<QObject*>())); + QVariant variant(QVariant::fromValue(QList<QObject*>())); v = engine->newVariantObject(variant); md->set(engine, id, v); } @@ -638,205 +651,170 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= propOffset(); if (id < propertyCount) { - const QV4::CompiledData::Property::Type t = static_cast<QV4::CompiledData::Property::Type>(qint32(compiledObject->propertyTable()[id].type)); - bool needActivate = false; - - if (t == QV4::CompiledData::Property::Var) { - // the context can be null if accessing var properties from cpp after re-parenting an item. - QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); - if (ep) { - if (c == QMetaObject::ReadProperty) { - *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); - } else if (c == QMetaObject::WriteProperty) { - writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - } - } else if (c == QMetaObject::ReadProperty) { - // if the context was disposed, we just return an invalid variant from read. - *reinterpret_cast<QVariant *>(a[0]) = QVariant(); - } + const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id]; + const QV4::CompiledData::BuiltinType t = property.builtinType(); - } else { - int fallbackMetaType = QMetaType::UnknownType; + // the context can be null if accessing var properties from cpp after re-parenting an item. + QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); + + const int fallbackMetaType = QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(t); + + if (c == QMetaObject::ReadProperty) { switch (t) { - case QV4::CompiledData::Property::Font: - fallbackMetaType = QMetaType::QFont; + case QV4::CompiledData::BuiltinType::Int: + *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); break; - case QV4::CompiledData::Property::Time: - fallbackMetaType = QMetaType::QTime; + case QV4::CompiledData::BuiltinType::Bool: + *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); break; - case QV4::CompiledData::Property::Color: - fallbackMetaType = QMetaType::QColor; + case QV4::CompiledData::BuiltinType::Real: + *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); break; - case QV4::CompiledData::Property::Vector2D: - fallbackMetaType = QMetaType::QVector2D; + case QV4::CompiledData::BuiltinType::String: + *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); break; - case QV4::CompiledData::Property::Vector3D: - fallbackMetaType = QMetaType::QVector3D; + case QV4::CompiledData::BuiltinType::Url: + *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); break; - case QV4::CompiledData::Property::Vector4D: - fallbackMetaType = QMetaType::QVector4D; + case QV4::CompiledData::BuiltinType::Date: + *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); break; - case QV4::CompiledData::Property::Matrix4x4: - fallbackMetaType = QMetaType::QMatrix4x4; + case QV4::CompiledData::BuiltinType::DateTime: + *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); break; - case QV4::CompiledData::Property::Quaternion: - fallbackMetaType = QMetaType::QQuaternion; + case QV4::CompiledData::BuiltinType::Rect: + *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); break; - default: break; - } - - - if (c == QMetaObject::ReadProperty) { - switch (t) { - case QV4::CompiledData::Property::Int: - *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); - break; - case QV4::CompiledData::Property::Bool: - *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); - break; - case QV4::CompiledData::Property::Real: - *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); - break; - case QV4::CompiledData::Property::String: - *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); - break; - case QV4::CompiledData::Property::Url: - *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); - break; - case QV4::CompiledData::Property::Date: - *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); - break; - case QV4::CompiledData::Property::DateTime: - *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); - break; - case QV4::CompiledData::Property::Rect: - *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); - break; - case QV4::CompiledData::Property::Size: - *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); - break; - case QV4::CompiledData::Property::Point: - *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); - break; - case QV4::CompiledData::Property::Custom: - *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); - break; - case QV4::CompiledData::Property::Variant: + case QV4::CompiledData::BuiltinType::Size: + *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); + break; + case QV4::CompiledData::BuiltinType::Point: + *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); + break; + case QV4::CompiledData::BuiltinType::Variant: + *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); + break; + case QV4::CompiledData::BuiltinType::Font: + case QV4::CompiledData::BuiltinType::Time: + case QV4::CompiledData::BuiltinType::Color: + case QV4::CompiledData::BuiltinType::Vector2D: + case QV4::CompiledData::BuiltinType::Vector3D: + case QV4::CompiledData::BuiltinType::Vector4D: + case QV4::CompiledData::BuiltinType::Matrix4x4: + case QV4::CompiledData::BuiltinType::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + QVariant propertyAsVariant; + if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) + propertyAsVariant = v->d()->data(); + QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); + } + break; + case QV4::CompiledData::BuiltinType::Var: + if (ep) { *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); - break; - case QV4::CompiledData::Property::CustomList: { + } else { + // if the context was disposed, we just return an invalid variant from read. + *reinterpret_cast<QVariant *>(a[0]) = QVariant(); + } + break; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: + if (property.isList) { QList<QObject *> *list = readPropertyAsList(id); QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]); *p = QQmlListProperty<QObject>(object, list, - list_append, list_count, list_at, - list_clear); + list_append, list_count, list_at, + list_clear); p->dummy1 = this; p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id)); - break; + } else { + *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); } - case QV4::CompiledData::Property::Font: - case QV4::CompiledData::Property::Time: - case QV4::CompiledData::Property::Color: - case QV4::CompiledData::Property::Vector2D: - case QV4::CompiledData::Property::Vector3D: - case QV4::CompiledData::Property::Vector4D: - case QV4::CompiledData::Property::Matrix4x4: - case QV4::CompiledData::Property::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - QVariant propertyAsVariant; - if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) - propertyAsVariant = v->d()->data(); - QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); + } + + } else if (c == QMetaObject::WriteProperty) { + bool needActivate = false; + switch (t) { + case QV4::CompiledData::BuiltinType::Int: + needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); + writeProperty(id, *reinterpret_cast<int *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Bool: + needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); + writeProperty(id, *reinterpret_cast<bool *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Real: + needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); + writeProperty(id, *reinterpret_cast<double *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::String: + needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); + writeProperty(id, *reinterpret_cast<QString *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Url: + needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); + writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Date: + needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); + writeProperty(id, *reinterpret_cast<QDate *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::DateTime: + needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); + writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Rect: + needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); + writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Size: + needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); + writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Point: + needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); + writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Variant: + writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); + break; + case QV4::CompiledData::BuiltinType::Font: + case QV4::CompiledData::BuiltinType::Time: + case QV4::CompiledData::BuiltinType::Color: + case QV4::CompiledData::BuiltinType::Vector2D: + case QV4::CompiledData::BuiltinType::Vector3D: + case QV4::CompiledData::BuiltinType::Vector4D: + case QV4::CompiledData::BuiltinType::Matrix4x4: + case QV4::CompiledData::BuiltinType::Quaternion: + Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + if (!v) { + md->set(engine, id, engine->newVariantObject(QVariant())); + v = (md->data() + id)->as<QV4::VariantObject>(); + QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); } - break; - case QV4::CompiledData::Property::Var: - Q_UNREACHABLE(); + needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data()); + QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data()); } - - } else if (c == QMetaObject::WriteProperty) { - - switch(t) { - case QV4::CompiledData::Property::Int: - needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); - writeProperty(id, *reinterpret_cast<int *>(a[0])); - break; - case QV4::CompiledData::Property::Bool: - needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); - writeProperty(id, *reinterpret_cast<bool *>(a[0])); - break; - case QV4::CompiledData::Property::Real: - needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); - writeProperty(id, *reinterpret_cast<double *>(a[0])); - break; - case QV4::CompiledData::Property::String: - needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); - writeProperty(id, *reinterpret_cast<QString *>(a[0])); - break; - case QV4::CompiledData::Property::Url: - needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); - writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); - break; - case QV4::CompiledData::Property::Date: - needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); - writeProperty(id, *reinterpret_cast<QDate *>(a[0])); - break; - case QV4::CompiledData::Property::DateTime: - needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); - writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); - break; - case QV4::CompiledData::Property::Rect: - needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); - writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); - break; - case QV4::CompiledData::Property::Size: - needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); - writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); - break; - case QV4::CompiledData::Property::Point: - needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); - writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); - break; - case QV4::CompiledData::Property::Custom: - needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); - writeProperty(id, *reinterpret_cast<QObject **>(a[0])); - break; - case QV4::CompiledData::Property::Variant: + break; + case QV4::CompiledData::BuiltinType::Var: + if (ep) writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - break; - case QV4::CompiledData::Property::CustomList: + break; + case QV4::CompiledData::BuiltinType::InvalidBuiltin: + if (property.isList) { // Writing such a property is not supported. Content is added through the list property // methods. - break; - case QV4::CompiledData::Property::Font: - case QV4::CompiledData::Property::Time: - case QV4::CompiledData::Property::Color: - case QV4::CompiledData::Property::Vector2D: - case QV4::CompiledData::Property::Vector3D: - case QV4::CompiledData::Property::Vector4D: - case QV4::CompiledData::Property::Matrix4x4: - case QV4::CompiledData::Property::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); - if (!v) { - md->set(engine, id, engine->newVariantObject(QVariant())); - v = (md->data() + id)->as<QV4::VariantObject>(); - QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); - } - needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data()); - QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data()); - } - break; - case QV4::CompiledData::Property::Var: - Q_UNREACHABLE(); + } else { + needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); + writeProperty(id, *reinterpret_cast<QObject **>(a[0])); } - } - } + } - if (c == QMetaObject::WriteProperty && needActivate) { - activate(object, methodOffset() + id, nullptr); + if (needActivate) + activate(object, methodOffset() + id, nullptr); } return -1; @@ -891,17 +869,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (!targetDData->propertyCache) return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); - // Value type property + // Value type property or deep alias QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); - Q_ASSERT(valueType); + if (valueType) { - valueType->read(target, coreIndex); - int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); + valueType->read(target, coreIndex); + int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); - if (c == QMetaObject::WriteProperty) - valueType->write(target, coreIndex, nullptr); + if (c == QMetaObject::WriteProperty) + valueType->write(target, coreIndex, nullptr); - return rv; + return rv; + } else { + // deep alias + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + return QMetaObject::metacall(target, c, valueTypePropertyIndex, a); + } } else { return QMetaObject::metacall(target, c, coreIndex, a); @@ -951,21 +935,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. @@ -1000,7 +1001,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) @@ -1025,7 +1026,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) @@ -1065,7 +1066,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) { - if (compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var) { + if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index dbcc9d2884..5025987586 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -65,9 +65,7 @@ #include "qqmlguard_p.h" #include "qqmlcontext_p.h" -#include "qqmlpropertycache_p.h" -#include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> #include <private/qv4object_p.h> @@ -146,7 +144,7 @@ class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId); + QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId); ~QQmlVMEMetaObject() override; bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; @@ -228,7 +226,7 @@ public: // keep a reference to the compilation unit in order to still // do property access when the context has been invalidated. - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QV4::CompiledData::Object *compiledObject; }; diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 9877cc027f..4db0562c0e 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -39,8 +39,6 @@ #include "qqmlxmlhttprequest_p.h" -#include <private/qv8engine_p.h> - #include "qqmlengine.h" #include "qqmlengine_p.h" #include <private/qqmlrefcount_p.h> @@ -70,8 +68,6 @@ using namespace QV4; -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) - #define V4THROW_REFERENCE(string) \ do { \ ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \ @@ -99,7 +95,7 @@ struct QQmlXMLHttpRequestData { static inline QQmlXMLHttpRequestData *xhrdata(ExecutionEngine *v4) { - return (QQmlXMLHttpRequestData *)v4->v8Engine->xmlHttpRequestData(); + return (QQmlXMLHttpRequestData *)v4->xmlHttpRequestData(); } QQmlXMLHttpRequestData::QQmlXMLHttpRequestData() @@ -595,7 +591,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4) if (d->nodePrototype.isUndefined()) { ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>()); d->nodePrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->nodePrototype.value(); } @@ -644,7 +640,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine) p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr); d->elementPrototype.set(engine, p); - engine->v8Engine->freezeObject(p); + engine->freezeObject(p); } return d->elementPrototype.value(); } @@ -661,7 +657,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr); p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr); d->attrPrototype.set(engine, p); - engine->v8Engine->freezeObject(p); + engine->freezeObject(p); } return d->attrPrototype.value(); } @@ -717,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr); p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr); d->characterDataPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->characterDataPrototype.value(); } @@ -753,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr); p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr); d->textPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->textPrototype.value(); } @@ -768,7 +764,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4) ScopedObject pp(scope); p->setPrototypeUnchecked((pp = Text::prototype(v4))); d->cdataPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->cdataPrototype.value(); } @@ -786,7 +782,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4) p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr); p->defineAccessorProperty(QStringLiteral("documentElement"), method_documentElement, nullptr); d->documentPrototype.set(v4, p); - v4->v8Engine->freezeObject(p); + v4->freezeObject(p); } return d->documentPrototype.value(); } @@ -1651,7 +1647,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->qmlEngine()->networkAccessManager(), scope.engine); Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototypeUnchecked(proto); @@ -2073,6 +2069,4 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) QT_END_NAMESPACE -#endif // xmlstreamreader && qml_network - #include <qqmlxmlhttprequest.moc> diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 4422195e3d..d634a48443 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> @@ -47,7 +48,6 @@ #if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> #endif -#include <private/qv8engine_p.h> #include <private/qqmldelayedcallqueue_p.h> #include <QFileInfo> @@ -439,7 +439,7 @@ ReturnedValue QtObject::method_font(const FunctionObject *b, const Value *, cons QV4::ExecutionEngine *v4 = scope.engine; bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(argv[0]), v4, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, argv[0], v4, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified"); return scope.engine->fromVariant(v); @@ -559,7 +559,7 @@ ReturnedValue QtObject::method_matrix4x4(const FunctionObject *b, const Value *, if (argc == 1 && argv[0].isObject()) { bool ok = false; - QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(argv[0]), scope.engine, &ok); + QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, argv[0], scope.engine, &ok); if (!ok) THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array"); return scope.engine->fromVariant(v); @@ -1710,10 +1710,8 @@ ReturnedValue ConsoleObject::method_time(const FunctionObject *b, const Value *, if (argc != 1) THROW_GENERIC_ERROR("console.time(): Invalid arguments"); - QV8Engine *v8engine = scope.engine->v8Engine; - QString name = argv[0].toQStringNoThrow(); - v8engine->startTimer(name); + scope.engine->startTimer(name); return QV4::Encode::undefined(); } @@ -1723,11 +1721,9 @@ ReturnedValue ConsoleObject::method_timeEnd(const FunctionObject *b, const Value if (argc != 1) THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments"); - QV8Engine *v8engine = scope.engine->v8Engine; - QString name = argv[0].toQStringNoThrow(); bool wasRunning; - qint64 elapsed = v8engine->stopTimer(name, &wasRunning); + qint64 elapsed = scope.engine->stopTimer(name, &wasRunning); if (wasRunning) { qDebug("%s: %llims", qPrintable(name), elapsed); } @@ -1743,13 +1739,12 @@ ReturnedValue ConsoleObject::method_count(const FunctionObject *b, const Value * Scope scope(b); QV4::ExecutionEngine *v4 = scope.engine; - QV8Engine *v8engine = scope.engine->v8Engine; QV4::CppStackFrame *frame = v4->currentStackFrame; QString scriptName = frame->source(); - int value = v8engine->consoleCountHelper(scriptName, frame->lineNumber(), 0); + int value = v4->consoleCountHelper(scriptName, frame->lineNumber(), 0); QString message = name + QLatin1String(": ") + QString::number(value); QMessageLogger(qPrintable(scriptName), frame->lineNumber(), @@ -1972,29 +1967,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); QString context; - if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { - QString path = ctxt->urlString(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - int lastDot = path.lastIndexOf(QLatin1Char('.')); - int length = lastDot - (lastSlash + 1); - context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); - } else { - CppStackFrame *frame = scope.engine->currentStackFrame; - // The first non-empty source URL in the call stack determines the translation context. - while (frame && context.isEmpty()) { - if (CompiledData::CompilationUnit *unit = frame->v4Function->compilationUnit) { - QString fileName = unit->fileName(); - QUrl url(unit->fileName()); - if (url.isValid() && url.isRelative()) { - context = url.fileName(); - } else { - context = QQmlFile::urlToLocalFileOrQrc(fileName); - if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) - context = fileName; - } - context = QFileInfo(context).baseName(); + CppStackFrame *frame = scope.engine->currentStackFrame; + // The first non-empty source URL in the call stack determines the translation context. + while (frame && context.isEmpty()) { + if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) { + const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit); + QString fileName = unit->fileName(); + QUrl url(unit->fileName()); + if (url.isValid() && url.isRelative()) { + context = url.fileName(); + } else { + context = QQmlFile::urlToLocalFileOrQrc(fileName); + if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) + context = fileName; } - frame = frame->parent; + context = QFileInfo(context).baseName(); + } + frame = frame->parent; + } + + if (context.isEmpty()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { + QString path = ctxt->urlString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + int lastDot = path.lastIndexOf(QLatin1Char('.')); + int length = lastDot - (lastSlash + 1); + context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } } @@ -2173,8 +2171,7 @@ function. */ ReturnedValue QtObject::method_callLater(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QV8Engine *v8engine = b->engine()->v8Engine; - return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc); + return b->engine()->delayedCallQueue()->addUniquelyAndExecuteLater(b, thisObject, argv, argc); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp deleted file mode 100644 index d76344b613..0000000000 --- a/src/qml/qml/v8/qv8engine.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** 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 "qv8engine_p.h" - -#if QT_CONFIG(qml_sequence_object) -#include "qv4sequenceobject_p.h" -#endif - -#include "private/qjsengine_p.h" - -#include <private/qqmlbuiltinfunctions_p.h> -#include <private/qqmllist_p.h> -#include <private/qqmlengine_p.h> -#if QT_CONFIG(qml_xml_http_request) -#include <private/qqmlxmlhttprequest_p.h> -#endif -#if QT_CONFIG(qml_locale) -#include <private/qqmllocale_p.h> -#endif -#include <private/qqmlglobal_p.h> -#include <private/qqmlmemoryprofiler_p.h> -#include <private/qqmlplatform_p.h> -#include <private/qjsvalue_p.h> -#include <private/qqmltypewrapper_p.h> -#include <private/qqmlvaluetypewrapper_p.h> -#include <private/qqmllistwrapper_p.h> -#include <private/qv4scopedvalue_p.h> - -#include "qv4domerrors_p.h" -#include "qv4sqlerrors_p.h" - -#include <QtCore/qjsonarray.h> -#include <QtCore/qjsonobject.h> -#include <QtCore/qjsonvalue.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qdatastream.h> -#include <private/qsimd_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4objectiterator_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <private/qv4mm_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4globalobject_p.h> -#include <private/qv4regexpobject_p.h> -#include <private/qv4variantobject_p.h> -#include <private/qv4script_p.h> -#include <private/qv4include_p.h> -#include <private/qv4jsonobject_p.h> -#include <private/qv4identifiertable_p.h> - -Q_DECLARE_METATYPE(QList<int>) - - -// XXX TODO: Need to check all the global functions will also work in a worker script where the -// QQmlEngine is not available -QT_BEGIN_NAMESPACE - -template <typename ReturnType> -ReturnType convertJSValueToVariantType(const QJSValue &value) -{ - return value.toVariant().value<ReturnType>(); -} - -static void saveJSValue(QDataStream &stream, const void *data) -{ - const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data); - quint32 isNullOrUndefined = 0; - if (jsv->isNull()) - isNullOrUndefined |= 0x1; - if (jsv->isUndefined()) - isNullOrUndefined |= 0x2; - stream << isNullOrUndefined; - if (!isNullOrUndefined) - reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream); -} - -static void restoreJSValue(QDataStream &stream, void *data) -{ - QJSValue *jsv = reinterpret_cast<QJSValue*>(data); - - quint32 isNullOrUndefined; - stream >> isNullOrUndefined; - - if (isNullOrUndefined & 0x1) { - *jsv = QJSValue(QJSValue::NullValue); - } else if (isNullOrUndefined & 0x2) { - *jsv = QJSValue(); - } else { - QVariant v; - v.load(stream); - QJSValuePrivate::setVariant(jsv, v); - } -} - -QV8Engine::QV8Engine(QV4::ExecutionEngine *v4) - : m_engine(nullptr) - , m_v4Engine(v4) -#if QT_CONFIG(qml_xml_http_request) - , m_xmlHttpRequestData(nullptr) -#endif -{ -#ifndef Q_OS_WASM // wasm does not have working simd QTBUG-63924 -#ifdef Q_PROCESSOR_X86_32 - if (!qCpuHasFeature(SSE2)) { - qFatal("This program requires an X86 processor that supports SSE2 extension, at least a Pentium 4 or newer"); - } -#endif -#endif - - QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine"); - qMetaTypeId<QJSValue>(); - qMetaTypeId<QList<int> >(); - - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) - QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) - QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) - QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); - QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); - - m_delayedCallQueue.init(m_v4Engine); - - QV4::QObjectWrapper::initializeBindings(m_v4Engine); -} - -QV8Engine::~QV8Engine() -{ - qDeleteAll(m_extensionData); - m_extensionData.clear(); - -#if QT_CONFIG(qml_xml_http_request) - qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); - m_xmlHttpRequestData = nullptr; -#endif -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QV8Engine::networkAccessManager() -{ - return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); -} -#endif - -const QSet<QString> &QV8Engine::illegalNames() const -{ - return m_illegalNames; -} - -void QV8Engine::initializeGlobal() -{ - QV4::Scope scope(m_v4Engine); - QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions); - - QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine)); - m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); - -#if QT_CONFIG(qml_locale) - QQmlLocale::registerStringLocaleCompare(m_v4Engine); - QQmlDateExtension::registerExtension(m_v4Engine); - QQmlNumberExtension::registerExtension(m_v4Engine); -#endif - -#if QT_CONFIG(qml_xml_http_request) - qt_add_domexceptions(m_v4Engine); - m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); -#endif - - qt_add_sqlexceptions(m_v4Engine); - - { - for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) { - if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) { - QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i); - m_illegalNames.insert(id.toQString()); - } - } - } -} - -static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) -{ - if (object->as<QV4::QObjectWrapper>()) - return; - - QV4::Scope scope(v4); - - bool instanceOfObject = false; - QV4::ScopedObject p(scope, object->getPrototypeOf()); - while (p) { - if (p->d() == v4->objectPrototype()->d()) { - instanceOfObject = true; - break; - } - p = p->getPrototypeOf(); - } - if (!instanceOfObject) - return; - - QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen(); - if (object->internalClass() == frozen) - return; - object->setInternalClass(frozen); - - QV4::ScopedObject o(scope); - for (uint i = 0; i < frozen->size; ++i) { - if (!frozen->nameMap.at(i).isStringOrSymbol()) - continue; - o = *object->propertyData(i); - if (o) - freeze_recursive(v4, o); - } -} - -void QV8Engine::freezeObject(const QV4::Value &value) -{ - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject o(scope, value); - freeze_recursive(m_v4Engine, o); -} - -struct QV8EngineRegistrationData -{ - QV8EngineRegistrationData() : extensionCount(0) {} - - QMutex mutex; - int extensionCount; -}; -Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData); - -QMutex *QV8Engine::registrationMutex() -{ - return ®istrationData()->mutex; -} - -int QV8Engine::registerExtension() -{ - return registrationData()->extensionCount++; -} - -void QV8Engine::setExtensionData(int index, Deletable *data) -{ - if (m_extensionData.count() <= index) - m_extensionData.resize(index + 1); - - if (m_extensionData.at(index)) - delete m_extensionData.at(index); - - m_extensionData[index] = data; -} - -void QV8Engine::initQmlGlobalObject() -{ - initializeGlobal(); - freezeObject(*m_v4Engine->globalObject); -} - -void QV8Engine::setEngine(QQmlEngine *engine) -{ - m_engine = engine; - initQmlGlobalObject(); -} - -void QV8Engine::startTimer(const QString &timerName) -{ - if (!m_time.isValid()) - m_time.start(); - m_startedTimers[timerName] = m_time.elapsed(); -} - -qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning) -{ - if (!m_startedTimers.contains(timerName)) { - *wasRunning = false; - return 0; - } - *wasRunning = true; - qint64 startedAt = m_startedTimers.take(timerName); - return m_time.elapsed() - startedAt; -} - -int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column) -{ - const QString key = file + QString::number(line) + QString::number(column); - int number = m_consoleCount.value(key, 0); - number++; - m_consoleCount.insert(key, number); - return number; -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h deleted file mode 100644 index 23559618ef..0000000000 --- a/src/qml/qml/v8/qv8engine_p.h +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLV8ENGINE_P_H -#define QQMLV8ENGINE_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 <QtCore/qvariant.h> -#include <QtCore/qset.h> -#include <QtCore/qmutex.h> -#include <QtCore/qstack.h> -#include <QtCore/qstringlist.h> -#include <QtCore/QElapsedTimer> -#include <QtCore/QThreadStorage> - -#include <QtQml/qjsengine.h> -#include "private/qintrusivelist_p.h" - - -#include <private/qv4value_p.h> -#include <private/qv4identifier_p.h> -#include <private/qv4context_p.h> -#include <private/qv4stackframe_p.h> -#include <private/qqmldelayedcallqueue_p.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - struct ArrayObject; - struct ExecutionEngine; - struct QObjectMethod; -} - -#define V4_DEFINE_EXTENSION(dataclass, datafunction) \ - static inline dataclass *datafunction(QV4::ExecutionEngine *engine) \ - { \ - static int extensionId = -1; \ - if (extensionId == -1) { \ - QV8Engine::registrationMutex()->lock(); \ - if (extensionId == -1) \ - extensionId = QV8Engine::registerExtension(); \ - QV8Engine::registrationMutex()->unlock(); \ - } \ - dataclass *rv = (dataclass *)engine->v8Engine->extensionData(extensionId); \ - if (!rv) { \ - rv = new dataclass(engine); \ - engine->v8Engine->setExtensionData(extensionId, rv); \ - } \ - return rv; \ - } \ - -// Used to allow a QObject method take and return raw V4 handles without having to expose -// 48 in the public API. -// Use like this: -// class MyClass : public QObject { -// Q_OBJECT -// ... -// Q_INVOKABLE void myMethod(QQmlV4Function*); -// }; -// The QQmlV8Function - and consequently the arguments and return value - only remains -// valid during the call. If the return value isn't set within myMethod(), the will return -// undefined. - -class QQmlV4Function -{ -public: - int length() const { return callData->argc(); } - QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); } - void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; } - QV4::ExecutionEngine *v4engine() const { return e; } -private: - friend struct QV4::QObjectMethod; - QQmlV4Function(); - QQmlV4Function(const QQmlV4Function &); - QQmlV4Function &operator=(const QQmlV4Function &); - - QQmlV4Function(QV4::CallData *callData, QV4::Value *retVal, QV4::ExecutionEngine *e) - : callData(callData), retVal(retVal), e(e) - { - callData->thisObject = QV4::Encode::undefined(); - } - - QV4::CallData *callData; - QV4::Value *retVal; - QV4::ExecutionEngine *e; -}; - -class Q_QML_PRIVATE_EXPORT QQmlV4Handle -{ -public: - QQmlV4Handle() : d(QV4::Encode::undefined()) {} - explicit QQmlV4Handle(const QV4::Value &v) : d(v.asReturnedValue()) {} - explicit QQmlV4Handle(QV4::ReturnedValue v) : d(v) {} - - operator QV4::ReturnedValue() const { return d; } - -private: - quint64 d; -}; - -class QObject; -class QQmlEngine; -class QNetworkAccessManager; -class QQmlContextData; - -class Q_QML_PRIVATE_EXPORT QV8Engine -{ - friend class QJSEngine; -public: -// static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } - static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; } - - QV8Engine(QV4::ExecutionEngine *v4); - virtual ~QV8Engine(); - - // This enum should be in sync with QQmlEngine::ObjectOwnership - enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; - - struct Deletable { - virtual ~Deletable() {} - }; - - void initQmlGlobalObject(); - void setEngine(QQmlEngine *engine); - QQmlEngine *engine() { return m_engine; } - QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } - -#if QT_CONFIG(qml_xml_http_request) - void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } -#endif - - void freezeObject(const QV4::Value &value); - -#if QT_CONFIG(qml_network) - // Return the network access manager for this engine. By default this returns the network - // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 - // instance (like in WorkerScript). - virtual QNetworkAccessManager *networkAccessManager(); -#endif - - // Return the list of illegal id names (the names of the properties on the global object) - const QSet<QString> &illegalNames() const; - - static QMutex *registrationMutex(); - static int registerExtension(); - - inline Deletable *extensionData(int) const; - void setExtensionData(int, Deletable *); - -public: - // used for console.time(), console.timeEnd() - void startTimer(const QString &timerName); - qint64 stopTimer(const QString &timerName, bool *wasRunning); - - // used for console.count() - int consoleCountHelper(const QString &file, quint16 line, quint16 column); - -protected: - QQmlEngine *m_engine; - QQmlDelayedCallQueue m_delayedCallQueue; - - QV4::ExecutionEngine *m_v4Engine; - -#if QT_CONFIG(qml_xml_http_request) - void *m_xmlHttpRequestData; -#endif - - QVector<Deletable *> m_extensionData; - - QSet<QString> m_illegalNames; - - QElapsedTimer m_time; - QHash<QString, qint64> m_startedTimers; - - QHash<QString, quint32> m_consoleCount; - - void initializeGlobal(); - -private: - Q_DISABLE_COPY(QV8Engine) -}; - -inline QV8Engine::Deletable *QV8Engine::extensionData(int index) const -{ - if (index < m_extensionData.count()) - return m_extensionData[index]; - else - return nullptr; -} - - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlV4Handle) - -#endif // QQMLV8ENGINE_P_H diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri index 4592022939..fbcc47de0c 100644 --- a/src/qml/qml/v8/v8.pri +++ b/src/qml/qml/v8/v8.pri @@ -1,11 +1,9 @@ HEADERS += \ - $$PWD/qv8engine_p.h \ $$PWD/qv4domerrors_p.h \ $$PWD/qv4sqlerrors_p.h \ $$PWD/qqmlbuiltinfunctions_p.h SOURCES += \ - $$PWD/qv8engine.cpp \ $$PWD/qv4domerrors.cpp \ $$PWD/qv4sqlerrors.cpp \ $$PWD/qqmlbuiltinfunctions.cpp diff --git a/src/qml/qmldirparser/qmldirparser.pri b/src/qml/qmldirparser/qmldirparser.pri index 660e7b395a..fefe2e75be 100644 --- a/src/qml/qmldirparser/qmldirparser.pri +++ b/src/qml/qmldirparser/qmldirparser.pri @@ -2,10 +2,7 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD HEADERS += \ - $$PWD/qqmldirparser_p.h \ - $$PWD/qqmlerror.h \ - $$PWD/qqmlsourcecoordinate_p.h + $$PWD/qqmldirparser_p.h SOURCES += \ - $$PWD/qqmldirparser.cpp \ - $$PWD/qqmlerror.cpp + $$PWD/qqmldirparser.cpp diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp index e944b52e47..5bf33d3602 100644 --- a/src/qml/qmldirparser/qqmldirparser.cpp +++ b/src/qml/qmldirparser/qqmldirparser.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qqmldirparser_p.h" -#include "qqmlerror.h" #include <QtCore/QtDebug> @@ -263,6 +262,13 @@ bool QQmlDirParser::parse(const QString &source) } else { reportError(lineNumber, 0, QStringLiteral("invalid version %1, expected <major>.<minor>").arg(sections[2])); } + } else if (sections[0] == QLatin1String("import")) { + if (sectionCount != 2) { + reportError(lineNumber, 0, + QStringLiteral("import requires 2 arguments, but %1 were provided").arg(sectionCount - 1)); + continue; + } + _imports << sections[1]; } else if (sectionCount == 2) { // No version specified (should only be used for relative qmldir files) const Component entry(sections[0], sections[1], -1, -1); @@ -297,8 +303,8 @@ bool QQmlDirParser::parse(const QString &source) void QQmlDirParser::reportError(quint16 line, quint16 column, const QString &description) { QQmlJS::DiagnosticMessage error; - error.loc.startLine = line; - error.loc.startColumn = column; + error.line = line; + error.column = column; error.message = description; _errors.append(error); } @@ -311,27 +317,20 @@ bool QQmlDirParser::hasError() const return false; } -void QQmlDirParser::setError(const QQmlError &e) +void QQmlDirParser::setError(const QQmlJS::DiagnosticMessage &e) { _errors.clear(); - reportError(e.line(), e.column(), e.description()); + reportError(e.line, e.column, e.message); } -QList<QQmlError> QQmlDirParser::errors(const QString &uri) const +QList<QQmlJS::DiagnosticMessage> QQmlDirParser::errors(const QString &uri) const { - QUrl url(uri); - QList<QQmlError> errors; + QList<QQmlJS::DiagnosticMessage> errors; const int numErrors = _errors.size(); errors.reserve(numErrors); for (int i = 0; i < numErrors; ++i) { - const QQmlJS::DiagnosticMessage &msg = _errors.at(i); - QQmlError e; - QString description = msg.message; - description.replace(QLatin1String("$$URI$$"), uri); - e.setDescription(description); - e.setUrl(url); - e.setLine(msg.loc.startLine); - e.setColumn(msg.loc.startColumn); + QQmlJS::DiagnosticMessage e = _errors.at(i); + e.message.replace(QLatin1String("$$URI$$"), uri); errors << e; } return errors; @@ -362,6 +361,11 @@ QHash<QString, QQmlDirParser::Component> QQmlDirParser::dependencies() const return _dependencies; } +QStringList QQmlDirParser::imports() const +{ + return _imports; +} + QList<QQmlDirParser::Script> QQmlDirParser::scripts() const { return _scripts; diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index f7a91c8b81..37ca1ef2ce 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 QQmlError; class QQmlEngine; -class Q_QML_PRIVATE_EXPORT QQmlDirParser +class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlDirParser { public: QQmlDirParser(); @@ -70,8 +70,8 @@ public: bool parse(const QString &source); bool hasError() const; - void setError(const QQmlError &); - QList<QQmlError> errors(const QString &uri) const; + void setError(const QQmlJS::DiagnosticMessage &); + QList<QQmlJS::DiagnosticMessage> errors(const QString &uri) const; QString typeNamespace() const; void setTypeNamespace(const QString &s); @@ -136,6 +136,7 @@ public: QHash<QString,Component> components() const; QHash<QString,Component> dependencies() const; + QStringList imports() const; QList<Script> scripts() const; QList<Plugin> plugins() const; bool designerSupported() const; @@ -162,6 +163,7 @@ private: QString _typeNamespace; QHash<QString,Component> _components; // multi hash QHash<QString,Component> _dependencies; + QStringList _imports; QList<Script> _scripts; QList<Plugin> _plugins; bool _designerSupported; 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/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 090b830b3c..3e275c6359 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -53,7 +53,9 @@ #else # define QT_FEATURE_qml_debug -1 # define QT_FEATURE_qml_sequence_object 1 -# define QT_FEATURE_qml_tracing -1 +# define QT_FEATURE_qml_jit -1 +# define QT_FEATURE_qml_worker_script -1 +# define QT_FEATURE_qml_xml_http_request -1 #endif QT_BEGIN_NAMESPACE diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 60988d12e6..9ca0cf2abe 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -56,8 +56,7 @@ #ifndef QT_QML_BOOTSTRAPPED # include <QtQml/private/qtqml-config_p.h> #endif - -#define Q_QML_PRIVATE_API_VERSION 3 +#include <private/qqmlapiversion_p.h> #define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 513f7f2997..921d60caa1 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -43,6 +43,8 @@ #include <private/qqmlproperty_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlmetatype_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4persistent_p.h> #include <qqmlengine.h> #include <qqmlcontext.h> @@ -52,28 +54,50 @@ #include <QtCore/qfile.h> #include <QtCore/qdebug.h> #include <QtCore/qtimer.h> +#include <QtCore/qloggingcategory.h> #include <private/qobject_p.h> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) + class QQmlBindPrivate : public QObjectPrivate { public: - QQmlBindPrivate() : obj(nullptr), componentComplete(true), delayed(false), pendingEval(false) {} + QQmlBindPrivate() + : obj(nullptr) + , prevBind(QQmlAbstractBinding::Ptr()) + , prevIsVariant(false) + , componentComplete(true) + , delayed(false) + , pendingEval(false) + , restoreBinding(true) + , restoreValue(false) + , restoreModeExplicit(false) + , writingProperty(false) + {} ~QQmlBindPrivate() { } QQmlNullableValue<bool> when; QPointer<QObject> obj; QString propName; - QQmlNullableValue<QVariant> value; + QQmlNullableValue<QJSValue> value; QQmlProperty prop; QQmlAbstractBinding::Ptr prevBind; + QV4::PersistentValue v4Value; + QVariant prevValue; + bool prevIsVariant:1; bool componentComplete:1; bool delayed:1; bool pendingEval:1; + bool restoreBinding:1; + bool restoreValue:1; + bool restoreModeExplicit:1; + bool writingProperty: 1; void validate(QObject *binding) const; + void clearPrev(); }; void QQmlBindPrivate::validate(QObject *binding) const @@ -174,6 +198,13 @@ QQmlBind::~QQmlBind() When the binding becomes inactive again, any direct bindings that were previously set on the property will be restored. + + \note By default, a previously set literal value is not restored when the Binding becomes + inactive. Rather, the last value set by the now inactive Binding is retained. You can customize + the restoration behavior for literal values as well as bindings using the \l restoreMode + property. The default will change in Qt 6.0. + + \sa restoreMode */ bool QQmlBind::when() const { @@ -216,7 +247,7 @@ void QQmlBind::setObject(QObject *obj) } d->obj = obj; if (d->componentComplete) { - d->prop = QQmlProperty(d->obj, d->propName); + setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); d->validate(this); } eval(); @@ -262,7 +293,7 @@ void QQmlBind::setProperty(const QString &p) } d->propName = p; if (d->componentComplete) { - d->prop = QQmlProperty(d->obj, d->propName); + setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); d->validate(this); } eval(); @@ -274,13 +305,13 @@ void QQmlBind::setProperty(const QString &p) The value to be set on the target object and property. This can be a constant (which isn't very useful), or a bound expression. */ -QVariant QQmlBind::value() const +QJSValue QQmlBind::value() const { Q_D(const QQmlBind); return d->value.value; } -void QQmlBind::setValue(const QVariant &v) +void QQmlBind::setValue(const QJSValue &v) { Q_D(QQmlBind); d->value = v; @@ -323,9 +354,71 @@ void QQmlBind::setDelayed(bool delayed) eval(); } +/*! + \qmlproperty enumeration QtQml::Binding::restoreMode + \since 5.14 + + This property can be used to describe if and how the original value should + be restored when the binding is disabled. + + The possible values are: + \list + \li Binding.RestoreNone The original value is not restored at all + \li Binding.RestoreBinding The original value is restored if it was another + binding. In that case the old binding is in effect again. + \li Binding.RestoreValue The original value is restored if it was a plain + value rather than a binding. + \li Binding.RestoreBindingOrValue The original value is always restored. + \endlist + + \warning The default value is Binding.RestoreBinding. This will change in + Qt 6.0 to Binding.RestoreBindingOrValue. + + If you rely on any specific behavior regarding the restoration of plain + values when bindings get disabled you should migrate to explicitly set the + restoreMode. + + Reliance on a restoreMode that doesn't restore the previous binding or value + for a specific property results in a run-time warning. +*/ +QQmlBind::RestorationMode QQmlBind::restoreMode() const +{ + Q_D(const QQmlBind); + unsigned result = RestoreNone; + if (d->restoreValue) + result |= RestoreValue; + if (d->restoreBinding) + result |= RestoreBinding; + return RestorationMode(result); +} + +void QQmlBind::setRestoreMode(RestorationMode newMode) +{ + Q_D(QQmlBind); + d->restoreModeExplicit = true; + if (newMode != restoreMode()) { + d->restoreValue = (newMode & RestoreValue); + d->restoreBinding = (newMode & RestoreBinding); + emit restoreModeChanged(); + } +} + void QQmlBind::setTarget(const QQmlProperty &p) { Q_D(QQmlBind); + + if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { + if (QObject *oldObject = d->prop.object()) { + QMetaProperty prop = oldObject->metaObject()->property(d->prop.index()); + if (prop.hasNotifySignal()) { + QByteArray signal('2' + prop.notifySignal().methodSignature()); + QObject::disconnect(oldObject, signal.constData(), + this, SLOT(targetValueChanged())); + } + } + p.connectNotifySignal(this, SLOT(targetValueChanged())); + } + d->prop = p; } @@ -340,7 +433,7 @@ void QQmlBind::componentComplete() Q_D(QQmlBind); d->componentComplete = true; if (!d->prop.isValid()) { - d->prop = QQmlProperty(d->obj, d->propName); + setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); d->validate(this); } eval(); @@ -358,6 +451,14 @@ void QQmlBind::prepareEval() } } +void QQmlBindPrivate::clearPrev() +{ + prevBind = nullptr; + v4Value.clear(); + prevValue.clear(); + prevIsVariant = false; +} + void QQmlBind::eval() { Q_D(QQmlBind); @@ -369,20 +470,91 @@ void QQmlBind::eval() if (!d->when) { //restore any previous binding if (d->prevBind) { - QQmlAbstractBinding::Ptr p = d->prevBind; - d->prevBind = nullptr; - QQmlPropertyPrivate::setBinding(p.data()); + if (d->restoreBinding) { + QQmlAbstractBinding::Ptr p = d->prevBind; + d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse. + QQmlPropertyPrivate::setBinding(p.data()); + } + } else if (!d->v4Value.isEmpty()) { + if (d->restoreValue) { + auto propPriv = QQmlPropertyPrivate::get(d->prop); + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef()); + d->clearPrev(); + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 6.0 the default is Binding.RestoreBinding." + << "In Qt >= 6.0 the default is Binding.RestoreBindingOrValue."; + } + } else if (d->prevIsVariant) { + if (d->restoreValue) { + d->prop.write(d->prevValue); + d->clearPrev(); + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 6.0 the default is Binding.RestoreBinding." + << "In Qt >= 6.0 the default is Binding.RestoreBindingOrValue."; + } } return; } //save any set binding for restoration - if (!d->prevBind) + if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) { + // try binding first d->prevBind = QQmlPropertyPrivate::binding(d->prop); + + if (!d->prevBind) { // nope, try a V4 value next + auto propPriv = QQmlPropertyPrivate::get(d->prop); + auto propData = propPriv->core; + if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); + Q_ASSERT(vmemo); + auto retVal = vmemo->vmeProperty(propData.coreIndex()); + d->v4Value = QV4::PersistentValue(vmemo->engine, retVal); + } else { // nope, use the meta object to get a QVariant + d->prevValue = d->prop.read(); + d->prevIsVariant = true; + } + } + } + QQmlPropertyPrivate::removeBinding(d->prop); } - d->prop.write(d->value.value); + d->writingProperty = true; + d->prop.write(d->value.value.toVariant()); + d->writingProperty = false; +} + +void QQmlBind::targetValueChanged() +{ + Q_D(QQmlBind); + if (d->writingProperty) + return; + + if (d->when.isValid() && !d->when) + return; + + QUrl url; + quint16 line = 0; + + const QQmlData *ddata = QQmlData::get(this, false); + if (ddata && ddata->outerContext) { + url = ddata->outerContext->url(); + line = ddata->lineNumber; + } + + qCInfo(lcBindingRemoval, + "The target property of the Binding element created at %s:%d was changed from " + "elsewhere. This does not overwrite the binding. The target property will still be " + "updated when the value of the Binding element changes.", + qPrintable(url.toString()), line); } QT_END_NAMESPACE diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index 5bf9ef85c6..7bf4fc4dfd 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -60,15 +60,27 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate; class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus { +public: + enum RestorationMode { + RestoreNone = 0x0, + RestoreBinding = 0x1, + RestoreValue = 0x2, + RestoreBindingOrValue = RestoreBinding | RestoreValue + }; + +private: Q_OBJECT Q_DECLARE_PRIVATE(QQmlBind) Q_INTERFACES(QQmlParserStatus) Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(QObject *target READ object WRITE setObject) Q_PROPERTY(QString property READ property WRITE setProperty) - Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(QJSValue value READ value WRITE setValue) Q_PROPERTY(bool when READ when WRITE setWhen) Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8) + Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode + NOTIFY restoreModeChanged REVISION 14) + Q_ENUM(RestorationMode) public: QQmlBind(QObject *parent=nullptr); @@ -83,12 +95,18 @@ public: QString property() const; void setProperty(const QString &); - QVariant value() const; - void setValue(const QVariant &); + QJSValue value() const; + void setValue(const QJSValue &); bool delayed() const; void setDelayed(bool); + RestorationMode restoreMode() const; + void setRestoreMode(RestorationMode); + +Q_SIGNALS: + void restoreModeChanged(); + protected: void setTarget(const QQmlProperty &) override; void classBegin() override; @@ -97,6 +115,9 @@ protected: private: void prepareEval(); void eval(); + +private Q_SLOTS: + void targetValueChanged(); }; QT_END_NAMESPACE diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index f601087690..1e801641e5 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -44,6 +44,7 @@ #include <private/qqmlboundsignal_p.h> #include <qqmlcontext.h> #include <private/qqmlcontext_p.h> +#include <private/qqmlvmemetaobject_p.h> #include <qqmlinfo.h> #include <QtCore/qdebug.h> @@ -66,7 +67,7 @@ public: bool ignoreUnknownSignals; bool componentcomplete; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding *> bindings; }; @@ -105,7 +106,7 @@ public: \qml MouseArea { Connections { - onClicked: foo(parameters) + function onClicked(mouse) { foo(mouse) } } } \endqml @@ -122,7 +123,7 @@ public: \qml Connections { target: area - onClicked: foo(parameters) + function onClicked(mouse) { foo(mouse) } } \endqml @@ -231,7 +232,7 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore) d->ignoreUnknownSignals = ignore; } -void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) +void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) { for (int ii = 0; ii < props.count(); ++ii) { const QV4::CompiledData::Binding *binding = props.at(ii); @@ -256,7 +257,7 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledDat } } -void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlConnectionsPrivate *p = static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object)); @@ -270,8 +271,76 @@ void QQmlConnections::connectSignals() if (!d->componentcomplete || (d->targetSet && !target())) return; - if (d->bindings.isEmpty()) + if (d->bindings.isEmpty()) { + connectSignalsToMethods(); + } else { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + qmlWarning(this) << tr("Implicitly defined onFoo properties in Connections are deprecated. " + "Use this syntax instead: function onFoo(<arguments>) { ... }"); +#endif + connectSignalsToBindings(); + } +} + +void QQmlConnections::connectSignalsToMethods() +{ + Q_D(QQmlConnections); + + QObject *target = this->target(); + QQmlData *ddata = QQmlData::get(this); + if (!ddata) return; + + QV4::ExecutionEngine *engine = ddata->context->engine->handle(); + + QQmlContextData *ctxtdata = ddata->outerContext; + for (int i = ddata->propertyCache->methodOffset(), + end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount(); + i < end; + ++i) { + + QQmlPropertyData *handler = ddata->propertyCache->method(i); + if (!handler || !handler->isVMEFunction()) + continue; + + const QString propName = handler->name(this); + + QQmlProperty prop(target, propName); + if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { + int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); + auto *signal = new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); + signal->setEnabled(d->enabled); + + QV4::Scope scope(engine); + QV4::ScopedContext global(scope, engine->rootContext()); + + QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(this); + Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this + + QV4::ScopedFunctionObject method(scope, vmeMetaObject->vmeMethod(handler->coreIndex())); + + QQmlBoundSignalExpression *expression = + ctxtdata ? new QQmlBoundSignalExpression( + target, signalIndex, ctxtdata, this, + method->as<QV4::FunctionObject>()->function()) + : nullptr; + + signal->takeExpression(expression); + d->boundsignals += signal; + } else if (!d->ignoreUnknownSignals + && propName.startsWith(QLatin1String("on")) && propName.length() > 2 + && propName.at(2).isUpper()) { + qmlWarning(this) << tr("Detected function \"%1\" in Connections element. " + "This is probably intended to be a signal handler but no " + "signal of the target matches the name.").arg(propName); + } + } +} + +// TODO: Drop this as soon as we can +void QQmlConnections::connectSignalsToBindings() +{ + Q_D(QQmlConnections); QObject *target = this->target(); QQmlData *ddata = QQmlData::get(this); QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index bd03d7e152..5d28e8e8be 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 1) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 3) Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) public: @@ -87,19 +87,23 @@ public: Q_SIGNALS: void targetChanged(); - Q_REVISION(1) void enabledChanged(); + Q_REVISION(3) void enabledChanged(); private: void connectSignals(); + void connectSignalsToMethods(); + void connectSignalsToBindings(); + void classBegin() override; void componentComplete() override; }; +// TODO: Drop this class as soon as we can class QQmlConnectionsParser : public QQmlCustomParser { public: - void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override; - void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override; + void applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; }; diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp deleted file mode 100644 index 676a524872..0000000000 --- a/src/qml/types/qqmldelegatecomponent.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qqmldelegatecomponent_p.h" -#include <QtQml/private/qqmladaptormodel_p.h> - -QT_BEGIN_NAMESPACE - -QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent) - : QQmlComponent(parent) -{ -} - -QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent() -{ -} - -QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const -{ - if (!adaptorModel) - return QVariant(); - return adaptorModel->value(adaptorModel->indexAt(row, column), role); -} - -/*! - \qmltype DelegateChoice - \instantiates QQmlDelegateChoice - \inqmlmodule Qt.labs.qmlmodels - \brief Encapsulates a delegate and when to use it. - - The DelegateChoice type wraps a delegate and defines the circumstances - in which it should be chosen. - - DelegateChoices can be nested inside a DelegateChooser. - - \sa DelegateChooser -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChoice::roleValue - This property holds the value used to match the role data for the role provided by \l DelegateChooser::role. -*/ -QVariant QQmlDelegateChoice::roleValue() const -{ - return m_value; -} - -void QQmlDelegateChoice::setRoleValue(const QVariant &value) -{ - if (m_value == value) - return; - m_value = value; - emit roleValueChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::row - This property holds the value used to match the row value of model elements. - With models that have only the index property (and thus only one column), this property - should be intended as an index, and set to the desired index value. - - \note Setting both row and index has undefined behavior. The two are equivalent and only - one should be used. - - \sa index -*/ - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::index - This property holds the value used to match the index value of model elements. - This is effectively an alias for \l row. - - \sa row -*/ -int QQmlDelegateChoice::row() const -{ - return m_row; -} - -void QQmlDelegateChoice::setRow(int r) -{ - if (m_row == r) - return; - m_row = r; - emit rowChanged(); - emit indexChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::column - This property holds the value used to match the column value of model elements. -*/ -int QQmlDelegateChoice::column() const -{ - return m_column; -} - -void QQmlDelegateChoice::setColumn(int c) -{ - if (m_column == c) - return; - m_column = c; - emit columnChanged(); - emit changed(); -} - -QQmlComponent *QQmlDelegateChoice::delegate() const -{ - return m_delegate; -} - -/*! - \qmlproperty Component QtQml.Models::DelegateChoice::delegate - This property holds the delegate to use if this choice matches the model item. -*/ -void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate); - if (adc) - disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - m_delegate = delegate; - adc = static_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - emit delegateChanged(); - emit changed(); -} - -bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const -{ - if (!m_value.isValid() && m_row < 0 && m_column < 0) - return true; - - const bool roleMatched = (m_value.isValid()) ? value == m_value : true; - const bool rowMatched = (m_row < 0 ) ? true : m_row == row; - const bool columnMatched = (m_column < 0 ) ? true : m_column == column; - return roleMatched && rowMatched && columnMatched; -} - -/*! - \qmltype DelegateChooser - \instantiates QQmlDelegateChooser - \inqmlmodule Qt.labs.qmlmodels - \brief Allows a view to use different delegates for different types of items in the model. - - The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required - by a view and used as a delegate. - DelegateChooser encapsulates a set of \l {DelegateChoice}s. - These choices are used to determine the delegate that will be instantiated for each - item in the model. - The selection of the choice is performed based on the value that a model item has for \l role, - and also based on index. - - DelegateChooser is commonly used when a view needs to display a set of delegates that are significantly - different from each other. For example, a typical phone settings view might include toggle switches, - sliders, radio buttons, and other visualizations based on the type of each setting. In this case, DelegateChooser - could provide an easy way to associate a different type of delegate with each setting: - - \qml \QtMinorVersion - import QtQuick 2.\1 - import QtQuick.Controls 2.\1 - import Qt.labs.qmlmodels 1.0 - - ListView { - width: 200; height: 400 - - ListModel { - id: listModel - ListElement { type: "info"; ... } - ListElement { type: "switch"; ... } - ListElement { type: "swipe"; ... } - ListElement { type: "switch"; ... } - } - - DelegateChooser { - id: chooser - role: "type" - DelegateChoice { roleValue: "info"; ItemDelegate { ... } } - DelegateChoice { roleValue: "switch"; SwitchDelegate { ... } } - DelegateChoice { roleValue: "swipe"; SwipeDelegate { ... } } - } - - model: listModel - delegate: chooser - } - \endqml - - \note This type is intended to transparently work only with TableView and any DelegateModel-based view. - Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support - this type of component to make it function as described. - - \sa DelegateChoice -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChooser::role - This property holds the role used to determine the delegate for a given model item. - - \sa DelegateChoice -*/ -void QQmlDelegateChooser::setRole(const QString &role) -{ - if (m_role == role) - return; - m_role = role; - emit roleChanged(); -} - -/*! - \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices - \default - - The list of DelegateChoices for the chooser. - - The list is treated as an ordered list, where the first DelegateChoice to match - will be used be a view. - - It should not generally be necessary to refer to the \c choices property, - as it is the default property for DelegateChooser and thus all child items are - automatically assigned to this property. -*/ - -QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices() -{ - return QQmlListProperty<QQmlDelegateChoice>(this, nullptr, - QQmlDelegateChooser::choices_append, - QQmlDelegateChooser::choices_count, - QQmlDelegateChooser::choices_at, - QQmlDelegateChooser::choices_clear); -} - -void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - q->m_choices.append(choice); - connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->delegateChanged(); -} - -int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.count(); -} - -QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.at(index); -} - -void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - for (QQmlDelegateChoice *choice : q->m_choices) - disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->m_choices.clear(); - q->delegateChanged(); -} - -QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const -{ - QVariant v; - if (!m_role.isNull()) - v = value(adaptorModel, row, column, m_role); - if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap - v = value(adaptorModel, row, column, QStringLiteral("modelData")); - if (v.isValid()) - v = v.toMap().value(m_role); - } - // loop through choices, finding first one that fits - for (int i = 0; i < m_choices.count(); ++i) { - const QQmlDelegateChoice *choice = m_choices.at(i); - if (choice->match(row, column, v)) - return choice->delegate(); - } - - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h deleted file mode 100644 index c925ed9a60..0000000000 --- a/src/qml/types/qqmldelegatecomponent_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 QQMLDELEGATECOMPONENT_P_H -#define QQMLDELEGATECOMPONENT_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/qtqmlglobal_p.h> -#include <qqmlcomponent.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -// TODO: consider making QQmlAbstractDelegateComponent public API -class QQmlAbstractDelegateComponentPrivate; -class QQmlAdaptorModel; -class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent -{ - Q_OBJECT -public: - QQmlAbstractDelegateComponent(QObject *parent = nullptr); - ~QQmlAbstractDelegateComponent() override; - - virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0; - -signals: - void delegateChanged(); - -protected: - QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const; - -private: - Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent) - Q_DISABLE_COPY(QQmlAbstractDelegateComponent) -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject -{ - Q_OBJECT - Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged) - Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged) - Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged) - Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged) - Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_CLASSINFO("DefaultProperty", "delegate") -public: - QVariant roleValue() const; - void setRoleValue(const QVariant &roleValue); - - int row() const; - void setRow(int r); - - int column() const; - void setColumn(int c); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *delegate); - - virtual bool match(int row, int column, const QVariant &value) const; - -signals: - void roleValueChanged(); - void rowChanged(); - void indexChanged(); - void columnChanged(); - void delegateChanged(); - void changed(); - -private: - QVariant m_value; - int m_row = -1; - int m_column = -1; - QQmlComponent *m_delegate = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent -{ - Q_OBJECT - Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged) - Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT) - Q_CLASSINFO("DefaultProperty", "choices") - -public: - QString role() const { return m_role; } - void setRole(const QString &role); - - virtual QQmlListProperty<QQmlDelegateChoice> choices(); - static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *); - static int choices_count(QQmlListProperty<QQmlDelegateChoice> *); - static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int); - static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *); - - QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override; - -signals: - void roleChanged(); - -private: - QString m_role; - QList<QQmlDelegateChoice *> m_choices; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateChoice) -QML_DECLARE_TYPE(QQmlDelegateChooser) - -#endif // QQMLDELEGATECOMPONENT_P_H diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp deleted file mode 100644 index f58bab7c09..0000000000 --- a/src/qml/types/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3557 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmldelegatemodel_p_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtQml/qqmlinfo.h> - -#include <private/qquickpackage_p.h> -#include <private/qmetaobjectbuilder_p.h> -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlcomponent_p.h> -#include <private/qqmlincubator_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <qv4objectiterator_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlDelegateModelItem; - -namespace QV4 { - -namespace Heap { - -struct DelegateModelGroupFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)); - - QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg); - uint flag; -}; - -struct QQmlDelegateModelGroupChange : Object { - void init() { Object::init(); } - - QQmlChangeSet::ChangeData change; -}; - -struct QQmlDelegateModelGroupChangeArray : Object { - void init(const QVector<QQmlChangeSet::Change> &changes); - void destroy() { - delete changes; - Object::destroy(); - } - - QVector<QQmlChangeSet::Change> *changes; -}; - - -} - -struct DelegateModelGroupFunction : QV4::FunctionObject -{ - V4_OBJECT2(DelegateModelGroupFunction, FunctionObject) - - static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) - { - return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); - } - - static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) - { - QV4::Scope scope(that->engine()); - QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QV4::ScopedValue v(scope, argc ? argv[0] : Value::undefinedValue()); - return f->d()->code(o->d()->item, f->d()->flag, v); - } -}; - -void Heap::DelegateModelGroupFunction::init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) -{ - QV4::Heap::FunctionObject::init(scope, QStringLiteral("DelegateModelGroupFunction")); - this->flag = flag; - this->code = code; -} - -} - -DEFINE_OBJECT_VTABLE(QV4::DelegateModelGroupFunction); - - - -class QQmlDelegateModelEngineData : public QV8Engine::Deletable -{ -public: - QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4); - ~QQmlDelegateModelEngineData(); - - QV4::ReturnedValue array(QV4::ExecutionEngine *engine, - const QVector<QQmlChangeSet::Change> &changes); - - QV4::PersistentValue changeProto; -}; - -V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) - - -void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) -{ - QQmlDelegateModelParts *parts = static_cast<QQmlDelegateModelParts *>(object()); - QQmlPartsModel *m = new QQmlPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast<QObject *>(m)); -} - -QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) -: QObject(parent), model(parent) -{ - new QQmlDelegateModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype DelegateModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQml.Models - \brief Encapsulates a model and delegate. - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a DelegateModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, DelegateModel is used together with \l Package to - provide delegates to multiple views, and with DelegateModelGroup to sort and filter - delegate items. - - The example below illustrates using a DelegateModel with a ListView. - - \snippet delegatemodel/delegatemodel.qml 0 -*/ - -QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegateChooser(nullptr) - , m_cacheMetaType(nullptr) - , m_context(ctxt) - , m_parts(nullptr) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_waitingToFetchMore(false) - , m_cacheItems(nullptr) - , m_items(nullptr) - , m_persistedItems(nullptr) -{ -} - -QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -int QQmlDelegateModelPrivate::adaptorModelCount() const -{ - // QQmlDelegateModel currently only support list models. - // So even if a model is a table model, only the first - // column will be used. - return m_adaptorModel.rowCount(); -} - -void QQmlDelegateModelPrivate::requestMoreIfNecessary() -{ - Q_Q(QQmlDelegateModel); - if (!m_waitingToFetchMore && m_adaptorModel.canFetchMore()) { - m_waitingToFetchMore = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - } -} - -void QQmlDelegateModelPrivate::init() -{ - Q_Q(QQmlDelegateModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQmlDelegateModel::QQmlDelegateModel() - : QQmlDelegateModel(nullptr, nullptr) -{ -} - -QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::~QQmlDelegateModel() -{ - Q_D(QQmlDelegateModel); - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setObject(nullptr, this); - - for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = nullptr; - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - cacheItem->contextData = nullptr; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - else if (cacheItem->incubationTask) - cacheItem->incubationTask->vdm = nullptr; - } -} - - -void QQmlDelegateModel::classBegin() -{ - Q_D(QQmlDelegateModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQmlDelegateModel::componentComplete() -{ - Q_D(QQmlDelegateModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlWarning(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - d->m_context->engine()->handle(), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup(); - - QVector<Compositor::Insert> inserts; - d->m_count = d->adaptorModelCount(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - d->requestMoreIfNecessary(); -} - -/*! - \qmlproperty model QtQml.Models::DelegateModel::model - This property holds the model providing data for the DelegateModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{QtQuick.XmlListModel::XmlListModel}{XmlListModel}. - - \sa {qml-data-models}{Data Models} - \keyword dm-model-property -*/ -QVariant QQmlDelegateModel::model() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.model(); -} - -void QQmlDelegateModelPrivate::connectToAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), - q, QQmlDelegateModel, SLOT(_q_modelReset())); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - q, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModel::setModel(const QVariant &model) -{ - Q_D(QQmlDelegateModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->connectToAbstractItemModel(); - - d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList<QByteArray>(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->adaptorModelCount()); - d->requestMoreIfNecessary(); - } -} - -/*! - \qmlproperty Component QtQml.Models::DelegateModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQmlDelegateModel::delegate() const -{ - Q_D(const QQmlDelegateModel); - return d->m_delegate; -} - -void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQmlDelegateModel); - if (d->m_transaction) { - qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); - return; - } - if (d->m_delegate == delegate) - return; - bool wasValid = d->m_delegate != nullptr; - d->m_delegate.setObject(delegate, this); - d->m_delegateValidated = false; - if (d->m_delegateChooser) - QObject::disconnect(d->m_delegateChooserChanged); - - d->m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) { - d->m_delegateChooser = adc; - d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, - [d](){ d->delegateChanged(); }); - } - } - d->delegateChanged(d->m_delegate, wasValid); -} - -/*! - \qmlproperty QModelIndex QtQml.Models::DelegateModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet delegatemodel/delegatemodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet delegatemodel/delegatemodel_rootindex/view.qml 0 - - If the \l {dm-model-property}{model} is a QAbstractItemModel subclass, - the delegate can also reference a \c hasModelChildren property (optionally - qualified by a \e model. prefix) that indicates whether the delegate's - model item has any child nodes. - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQmlDelegateModel::rootIndex() const -{ - Q_D(const QQmlDelegateModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQmlDelegateModel::setRootIndex(const QVariant &root) -{ - Q_D(QQmlDelegateModel); - - QModelIndex modelIndex = qvariant_cast<QModelIndex>(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) { - // The previous root index was invalidated, so we need to reconnect the model. - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - d->connectToAbstractItemModel(); - } - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->adaptorModelCount(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified \a index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::modelIndex(int idx) const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::parentModelIndex() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQml.Models::DelegateModel::count -*/ - -int QQmlDelegateModel::count() const -{ - Q_D(const QQmlDelegateModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) -{ - if (!object) - return QQmlDelegateModel::ReleaseFlags(0); - - QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object); - if (!cacheItem) - return QQmlDelegateModel::ReleaseFlags(0); - - if (!cacheItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - } - cacheItem->Dispose(); - return QQmlInstanceModel::Destroyed; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) -{ - Q_D(QQmlDelegateModel); - QQmlInstanceModel::ReleaseFlags stat = d->release(item); - return stat; -} - -// Cancel a requested async item -void QQmlDelegateModel::cancel(int index) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - d->emitDestroyingPackage(package); - else - d->emitDestroyingItem(object); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQmlDelegateModelPrivate::group_append( - QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlWarning(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQmlDelegateModelPrivate::group_count( - QQmlListProperty<QQmlDelegateModelGroup> *property) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return d->m_groupCount - 1; -} - -QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty<QQmlDelegateModelGroup> *property, int index) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : nullptr; -} - -/*! - \qmlproperty list<DelegateModelGroup> QtQml.Models::DelegateModel::groups - - This property holds a delegate model's group definitions. - - Groups define a sub-set of the items in a delegate model and can be used to filter - a model. - - For every group defined in a DelegateModel two attached properties are added to each - delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the - item belongs to the group and the second DelegateModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet delegatemodel/delegatemodelgroup.qml 0 - \keyword dm-groups-property -*/ - -QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups() -{ - Q_D(QQmlDelegateModel); - return QQmlListProperty<QQmlDelegateModelGroup>( - this, - d, - QQmlDelegateModelPrivate::group_append, - QQmlDelegateModelPrivate::group_count, - QQmlDelegateModelPrivate::group_at, - nullptr); -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::items - - This property holds default group to which all new items are added. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::items() -{ - Q_D(QQmlDelegateModel); - return d->m_items; -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::persistedItems - - This property holds delegate model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - DelegateModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQml.Models::DelegateModelGroup::create() function are automatically added - to this group. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() -{ - Q_D(QQmlDelegateModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQml.Models::DelegateModel::filterOnGroup - - This property holds name of the group that is used to filter the delegate model. - - Only items that belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQmlDelegateModel::filterGroup() const -{ - Q_D(const QQmlDelegateModel); - return d->m_filterGroup; -} - -void QQmlDelegateModel::setFilterGroup(const QString &group) -{ - Q_D(QQmlDelegateModel); - - if (d->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlDelegateModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQmlDelegateModelPrivate::updateFilterGroup() -{ - Q_Q(QQmlDelegateModel); - if (!m_cacheMetaType) - return; - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - auto partsCopy = m_parts->models; // deliberate; this may alter m_parts - for (QQmlPartsModel *model : qAsConst(partsCopy)) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQml.Models::DelegateModel::parts - - The \a parts property selects a DelegateModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - DelegateModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQmlDelegateModel::parts() -{ - Q_D(QQmlDelegateModel); - if (!d->m_parts) - d->m_parts = new QQmlDelegateModelParts(this); - return d->m_parts; -} - -const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr; -} - -void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -static bool isDoneIncubating(QQmlIncubator::Status status) -{ - return status == QQmlIncubator::Ready || status == QQmlIncubator::Error; -} - -void QQDMIncubationTask::statusChanged(Status status) -{ - if (vdm) { - vdm->incubatorStatusChanged(this, status); - } else if (isDoneIncubating(status)) { - Q_ASSERT(incubating); - // The model was deleted from under our feet, cleanup ourselves - delete incubating->object; - incubating->object = nullptr; - if (incubating->contextData) { - incubating->contextData->invalidate(); - Q_ASSERT(incubating->contextData->refCount == 1); - incubating->contextData = nullptr; - } - incubating->scriptRef = 0; - incubating->deleteLater(); - } -} - -void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) -{ - Q_Q(QQmlDelegateModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it) -{ - m_cache.insert(it.cacheIndex, item); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) -{ - int cidx = m_cache.lastIndexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - if (!isDoneIncubating(status)) - return; - - const QList<QQmlError> incubationTaskErrors = incubationTask->errors(); - - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = nullptr; - incubationTask->incubating = nullptr; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - cacheItem->referenceObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - else - emitCreatedItem(incubationTask, cacheItem->object); - cacheItem->releaseObject(); - } else if (status == QQmlIncubator::Error) { - qmlInfo(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Cannot create delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(cacheItem->object); - delete cacheItem->object; - cacheItem->object = nullptr; - cacheItem->scriptRef -= 1; - if (cacheItem->contextData) { - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - } - cacheItem->contextData = nullptr; - - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QQDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) -{ - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitInitPackage(incubationTask, package); - else - emitInitItem(incubationTask, cacheItem->object); -} - -QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode) -{ - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); - return nullptr; - } else if (!m_context || !m_context->isValid()) { - return nullptr; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return nullptr; - - cacheItem->groups = it->flags; - addCacheItem(cacheItem, it); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlComponent *delegate = m_delegate; - if (m_delegateChooser) { - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, index); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - if (!delegate) - return nullptr; - } - - QQmlContext *creationContext = delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QQDMIncubationTask(this, incubationMode); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data())); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQmlAdaptorModelProxyInterface *proxy - = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, /*stronglyReferencedByParent*/true); - QObject *proxied = proxy->proxiedObject(); - ctxt->contextObject = proxied; - // We don't own the proxied object. We need to clear it if it goes away. - QObject::connect(proxied, &QObject::destroyed, - cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed); - } - } - - QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate); - cp->incubateObject( - cacheItem->incubationTask, - delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1) - requestMoreIfNecessary(); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status()))) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return nullptr; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, object() may return 0. In this - case createdItem() will be emitted when the object is available. The object - at this stage does not have any references, so object() must be called again - to ensure a reference is held. Any call to object() which returns a valid object - must be matched by a call to release() in order to destroy the object. -*/ -QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return nullptr; - } - - return d->object(d->m_compositorGroup, index, incubationMode); -} - -QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) -{ - Q_D(QQmlDelegateModel); - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast<QObject*>(value); - if (!obj) - return QString(); - int from = dot+1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.midRef(from, dot - from).toUtf8()); - } - return value.toString(); - } - return QString(); -} - -QString QQmlDelegateModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlDelegateModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QQmlDelegateModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlDelegateModel); - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - Q_D(QQmlDelegateModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQmlDelegateModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Insert> inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQmlDelegateModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQmlDelegateModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQmlDelegateModel::event(QEvent *e) -{ - Q_D(QQmlDelegateModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_waitingToFetchMore = false; - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQmlInstanceModel::event(e); -} - -void QQmlDelegateModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - for (const Compositor::Change &change : changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector<Compositor::Change> changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) -{ - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < qMin<int>(count, Compositor::MaximumGroupCount); ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQmlDelegateModelPrivate::itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - for (const Compositor::Insert &insert : inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQmlChangeSet::Change(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList<QQmlDelegateModelItem *> items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQmlDelegateModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= index) { - const int newIndex = item->modelIndex() + count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -//### This method should be split in two. It will remove delegates, and it will re-render the list. -// When e.g. QQmlListModel::remove is called, the removal of the delegates should be done on -// QAbstractItemModel::rowsAboutToBeRemoved, and the re-rendering on -// QAbstractItemModel::rowsRemoved. Currently both are done on the latter signal. The problem is -// that the destruction of an item will emit a changed signal that ends up at the delegate, which -// in turn will try to load the data from the model (which should have already freed it), resulting -// in a use-after-free. See QTBUG-59256. -void QQmlDelegateModelPrivate::itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - for (const Compositor::Remove &remove : removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQmlChangeSet::Change(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex; - QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - if (!cacheItem->isObjectReferenced()) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - } - cacheItem->scriptRef -= 1; - } else { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, removed); -} - -void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQmlDelegateModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by removal of a previous item might have - // already invalidated this item in d->m_cache and deleted it - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= index + count) { - const int newIndex = item->modelIndex() - count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= index) { - item->setModelIndex(-1, -1, -1); - } - } - - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts) -{ - QHash<int, QList<QQmlDelegateModelItem *> > movedItems; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= from && item->modelIndex() < from + count) { - const int newIndex = item->modelIndex() - from + to; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) { - const int newIndex = item->modelIndex() + difference; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlDelegateModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove) -{ - Q_Q(QQmlDelegateModel); - if (!m_complete) - return; - - if (m_transaction) { - qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated."); - return; - } - - if (remove) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove( - 0, m_compositor.count(Compositor::Group(i))); - } - } - if (add) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert( - 0, m_compositor.count(Compositor::Group(i))); - } - } - emitChanges(); -} - -void QQmlDelegateModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context || !m_context->isValid()) - return; - - m_transaction = true; - QV4::ExecutionEngine *engine = m_context->engine()->handle(); - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache - for (QQmlDelegateModelItem *cacheItem : qAsConst(cacheCopy)) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQmlDelegateModel::_q_modelReset() -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->adaptorModelCount(); - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() != -1) - item->setModelIndex(-1, -1, -1); - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.invalidateModel(); - - if (d->m_complete && oldCount > 0) { - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQmlDelegateModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const -{ - for (int i = 0, c = parents.count(); i < c; ++i) { - for (QPersistentModelIndex parent = desc; parent.isValid(); parent = parent.parent()) { - if (parent == parents[i]) - return true; - } - } - - return false; -} - -void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) -{ - Q_D(QQmlDelegateModel); - if (!d->m_complete) - return; - - if (hint == QAbstractItemModel::VerticalSortHint) { - if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) { - return; - } - - // mark all items as changed - _q_itemsChanged(0, d->m_count, QVector<int>()); - - } else if (hint == QAbstractItemModel::HorizontalSortHint) { - // Ignored - } else { - // We don't know what's going on, so reset the model - _q_modelReset(); - } -} - -QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) -{ - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQmlDelegateModelAttached(obj); -} - -bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups) -{ - if (!m_context || !m_context->isValid()) - return false; - - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1); - if (!cacheItem) - return false; - if (!object.isObject()) - return false; - - QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, object); - if (!o) - return false; - - QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedValue propertyName(scope); - QV4::ScopedValue v(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(v); - if (propertyName->isNull()) - break; - cacheItem->setValue(propertyName->toQStringNoThrow(), scope.engine->toVariant(v, QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector<Compositor::Insert>(1, Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag))); - - before = m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v4Engine(engine) - , metaObject(nullptr) - , groupNames(groupNames) -{ -} - -QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() -{ - if (metaObject) - metaObject->release(); -} - -void QQmlDelegateModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQmlDelegateModelItemMetaType::initializePrototype() -{ - QV4::Scope scope(v4Engine); - - QV4::ScopedObject proto(scope, v4Engine->newObject()); - proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, nullptr); - proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups); - QV4::ScopedString s(scope); - QV4::ScopedProperty p(scope); - - s = v4Engine->newString(QStringLiteral("isUnresolved")); - QV4::ScopedFunctionObject f(scope); - QV4::ExecutionContext *global = scope.engine->rootContext(); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inPersistedItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("itemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("persistedItemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - modelItemProto.set(v4Engine, proto); -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - for (const QString &groupName : groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const -{ - int groupFlags = 0; - QV4::Scope scope(v4Engine); - - QV4::ScopedString s(scope, groups); - if (s) { - const QString groupName = s->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - return groupFlags; - } - - QV4::ScopedArrayObject array(scope, groups); - if (array) { - QV4::ScopedValue v(scope); - uint arrayLength = array->getLength(); - for (uint i = 0; i < arrayLength; ++i) { - v = array->get(i); - const QString groupName = v->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return b->engine()->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - - return o->d()->item->get(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QStringList groups; - for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { - if (o->d()->item->groups & (1 << i)) - groups.append(o->d()->item->metaType->groupNames.at(i - 1)); - } - - return scope.engine->fromVariant(groups); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - if (!argc) - THROW_TYPE_ERROR(); - - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(o->d()->item->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(argv[0]); - const int cacheIndex = model->m_cache.indexOf(o->d()->item); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode(bool(thisItem->groups & (1 << flag))); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_member(QQmlDelegateModelItem *cacheItem, uint flag, const QV4::Value &arg) -{ - if (!cacheItem->metaType->model) - return QV4::Encode::undefined(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - bool member = arg.toBoolean(); - uint groupFlag = (1 << flag); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return QV4::Encode::undefined(); - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode((int)thisItem->groupIndex(Compositor::Group(flag))); -} - -void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObject) -{ - if (!contextData) - return; - - for (QQmlContextData *ctxt = contextData->childContexts; ctxt; ctxt = ctxt->nextChild) { - if (ctxt->contextObject == childContextObject) - ctxt->contextObject = nullptr; - } -} - - -//--------------------------------------------------------------------------- - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelItemObject); - -void QV4::Heap::QQmlDelegateModelItemObject::destroy() -{ - item->Dispose(); - Object::destroy(); -} - - -QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, - int modelIndex, int row, int column) - : v4(metaType->v4Engine) - , metaType(metaType) - , contextData(nullptr) - , object(nullptr) - , attached(nullptr) - , incubationTask(nullptr) - , delegate(nullptr) - , poolTime(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) - , row(row) - , column(column) -{ - metaType->addref(); - - if (accessor->propertyCache) { - // The property cache in the accessor is common for all the model - // items in the model it wraps. It describes available model roles, - // together with revisioned properties like row, column and index, all - // which should be available in the delegate. We assign this cache to the - // model item so that the QML engine can use the revision information - // when resolving the properties (rather than falling back to just - // inspecting the QObject in the model item directly). - QQmlData *qmldata = QQmlData::get(this, true); - if (qmldata->propertyCache) - qmldata->propertyCache->release(); - qmldata->propertyCache = accessor->propertyCache.data(); - qmldata->propertyCache->addref(); - } -} - -QQmlDelegateModelItem::~QQmlDelegateModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask) { - if (metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - else - delete incubationTask; - } - - metaType->release(); - -} - -void QQmlDelegateModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn) -{ - const int prevIndex = index; - const int prevRow = row; - const int prevColumn = column; - - index = idx; - row = newRow; - column = newColumn; - - if (idx != prevIndex) - emit modelIndexChanged(); - if (row != prevRow) - emit rowChanged(); - if (column != prevColumn) - emit columnChanged(); -} - -void QQmlDelegateModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QQmlData *data = QQmlData::get(object); - Q_ASSERT(data); - if (data->ownContext) { - data->ownContext->clearContext(); - if (data->ownContext->contextObject == object) - data->ownContext->contextObject = nullptr; - data->ownContext = nullptr; - data->context = nullptr; - } - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = nullptr; - attached = nullptr; - } - - contextData->invalidate(); - contextData = nullptr; - object = nullptr; -} - -QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) -{ - QQmlData *d = QQmlData::get(object); - QQmlContextData *context = d ? d->context : nullptr; - for (context = context ? context->parent : nullptr; context; context = context->parent) { - if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( - context->contextObject)) { - return cacheItem; - } - } - return nullptr; -} - -int QQmlDelegateModelItem::groupIndex(Compositor::Group group) -{ - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : nullptr) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast<QMetaObject *>(this) = *metaObject; -} - -QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQmlDelegateModelAttached *attached = static_cast<QQmlDelegateModelAttached *>(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast<int *>(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast<bool *>(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast<bool *>(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) - : m_cacheItem(nullptr) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached( - QQmlDelegateModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - resetCurrentIndex(); - // Let m_previousIndex be equal to m_currentIndex - std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex)); - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -void QQmlDelegateModelAttached::resetCurrentIndex() -{ - if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i) - m_currentIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = it.index[i]; - } -} - -/*! - \qmlattachedproperty model QtQml.Models::DelegateModel::model - - This attached property holds the data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQmlDelegateModel *QQmlDelegateModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : nullptr; -} - -/*! - \qmlattachedproperty stringlist QtQml.Models::DelegateModel::groups - - This attached property holds the name of DelegateModelGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQmlDelegateModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQmlDelegateModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved - - This attached property indicates whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQmlDelegateModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inItems - - This attached property holds whether the item belongs to the default \l items DelegateModelGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::itemsIndex - - This attached property holds the index of the item in the default \l items DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -void QQmlDelegateModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQmlDelegateModelGroupPrivate::isChangedConnected() -{ - Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &)); -} - -void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) -{ - Q_Q(QQmlDelegateModelGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - QV4::Scope scope(v4); - QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes())); - QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts())); - emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -typedef QQmlDelegateModelGroupEmitterList::iterator GroupEmitterListIt; - -void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->createdPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->initPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items. - - The DelegateModelGroup type provides a means to address the model data of a - DelegateModel's delegate items, as well as sort and filter these delegate - items. - - The initial set of instantiable delegate items in a DelegateModel is represented - by its \l {QtQml.Models::DelegateModel::items}{items} group, which normally directly reflects - the contents of the model assigned to DelegateModel::model. This set can be changed to - the contents of any other member of DelegateModel::groups by assigning the \l name of that - DelegateModelGroup to the DelegateModel::filterOnGroup property. - - The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. Different groups can only be sorted independently if they are disjunct. Moving - an item in one group will also move it in all other groups it is a part of. - - Data from models can be supplemented by inserting data directly into a DelegateModelGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be instantiated directly from a DelegateModelGroup using the - create() function, making it possible to use DelegateModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ -QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ -} - -QQmlDelegateModelGroup::QQmlDelegateModelGroup( - const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QQmlDelegateModelGroup(parent) -{ - Q_D(QQmlDelegateModelGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQmlDelegateModelGroup::~QQmlDelegateModelGroup() -{ -} - -/*! - \qmlproperty string QtQml.Models::DelegateModelGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQmlDelegateModelGroup::name() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->name; -} - -void QQmlDelegateModelGroup::setName(const QString &name) -{ - Q_D(QQmlDelegateModelGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQml.Models::DelegateModelGroup::count - - This property holds the number of items in the group. -*/ - -int QQmlDelegateModelGroup::count() const -{ - Q_D(const QQmlDelegateModelGroup); - if (!d->model) - return 0; - return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQml.Models::DelegateModelGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQmlDelegateModelGroup::defaultInclude() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->defaultInclude; -} - -void QQmlDelegateModelGroup::setDefaultInclude(bool include) -{ - Q_D(QQmlDelegateModelGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQml.Models::DelegateModelGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - DelegateModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQml.Models::DelegateModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQml.Models::DelegateModel::items}{items} group. - \li \b {in<GroupName>} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {<groupName>Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QQmlV4Handle QQmlDelegateModelGroup::get(int index) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return QQmlV4Handle(QV4::Encode::undefined()); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context || !model->m_context->isValid()) { - return QQmlV4Handle(QV4::Encode::undefined()); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlWarning(this) << tr("get: index out of range"); - return QQmlV4Handle(QV4::Encode::undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQmlDelegateModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return QQmlV4Handle(QV4::Encode::undefined()); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->modelItemProto.isUndefined()) - model->m_cacheMetaType->initializePrototype(); - QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; - QV4::Scope scope(v4); - ++cacheItem->scriptRef; - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); - QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototypeOf(p); - - return QQmlV4Handle(o); -} - -bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const -{ - if (value.isNumber()) { - *index = value.toInt32(); - return true; - } - - if (!value.isObject()) - return false; - - QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value); - - if (object) { - QQmlDelegateModelItem * const cacheItem = object->d()->item; - if (QQmlDelegateModelPrivate *model = cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : nullptr) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a DelegateModel with the values from \a data that - correspond to roles in the model assigned to DelegateModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a DelegateModel can later be merged with an existing entry in - DelegateModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQmlDelegateModelGroup::insert(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlWarning(this) << tr("insert: index out of range"); - return; - } - if (++i == args->length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - if (v->as<QV4::ArrayObject>()) { - return; - } else if (v->as<QV4::Object>()) { - model->insert(before, v, groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index) - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQml.Models::DelegateModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQmlDelegateModelGroup::create(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - if (args->length() == 0) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->as<QV4::Object>()) { - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v, groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, QQmlIncubator::AsynchronousIfNested); - if (object) { - QVector<Compositor::Insert> inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->setReturnValue(QV4::QObjectWrapper::wrap(args->v4engine(), object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup - instead of being derived from a DelegateModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQmlDelegateModelGroup::resolve(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (args->length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlWarning(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlWarning(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlWarning(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector<Compositor::Remove>(1, Compositor::Remove(fromIt, 1, unresolvedFlags, 0)), - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, unresolvedFlags, 0))); - model->itemsInserted( - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag))); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector<Compositor::Remove>(1, Compositor::Remove(toIt, 1, resolvedFlags))); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQmlDelegateModelGroup::remove(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &index, &group)) { - qmlWarning(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->length()) { - v = (*args)[i]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->length() < 2) - return false; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->isNumber()) { - *count = v->toInt32(); - - if (++i == args->length()) - return false; - v = (*args)[i]; - } - - *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. - - \note The DelegateModel acts as a proxy model: it holds the delegates in a - different order than the \l{dm-model-property}{underlying model} has them. - Any subsequent changes to the underlying model will not undo whatever - reordering you have done via this function. -*/ - -void QQmlDelegateModelGroup::move(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - - if (args->length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &from, &fromGroup)) { - qmlWarning(this) << tr("move: invalid from index"); - return; - } - - v = (*args)[1]; - if (!d->parseIndex(v, &to, &toGroup)) { - qmlWarning(this) << tr("move: invalid to index"); - return; - } - - if (args->length() > 2) { - v = (*args)[2]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (count < 0) { - qmlWarning(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlWarning(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQml.Models::DelegateModelGroup::changed(array removed, array inserted) - - This signal is emitted when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. - - The corresponding handler is \c onChanged. -*/ - -//============================================================================ - -QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) - : QQmlInstanceModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQmlPartsModel::~QQmlPartsModel() -{ -} - -QString QQmlPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQmlPartsModel::setFilterGroup(const QString &group) -{ - if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup() -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup( - Compositor::Group group, const QQmlChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQmlPartsModel::count() const -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQmlPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QObject *QQmlPartsModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return nullptr; - } - - QObject *object = model->object(m_compositorGroup, index, incubationMode); - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) { - QObject *part = package->part(m_part); - if (!part) - return nullptr; - m_packaged.insertMulti(part, package); - return part; - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlWarning(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return nullptr; -} - -QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) -{ - QQmlInstanceModel::ReleaseFlags flags = nullptr; - - QHash<QObject *, QQuickPackage *>::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QString QQmlPartsModel::stringValue(int index, const QString &role) -{ - return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - Compositor::iterator it = model->m_compositor.find(model->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -int QQmlPartsModel::indexOf(QObject *item, QObject *) const -{ - QHash<QObject *, QQuickPackage *>::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) -{ - emit createdItem(index, package->part(m_part)); -} - -void QQmlPartsModel::initPackage(int index, QQuickPackage *package) -{ - if (m_modelUpdatePending) - m_pendingPackageInitializations << index; - else - emit initItem(index, package->part(m_part)); -} - -void QQmlPartsModel::destroyingPackage(QQuickPackage *package) -{ - QObject *item = package->part(m_part); - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); -} - -void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - m_modelUpdatePending = false; - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - QVector<int> pendingPackageInitializations; - qSwap(pendingPackageInitializations, m_pendingPackageInitializations); - for (int index : pendingPackageInitializations) { - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) - continue; - QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emit initItem(index, package->part(m_part)); - model->release(object); - } -} - -//============================================================================ - -struct QQmlDelegateModelGroupChange : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) - - static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->allocate<QQmlDelegateModelGroupChange>(); - } - - static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.index); - } - static QV4::ReturnedValue method_get_count(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.count); - } - static QV4::ReturnedValue method_get_moveId(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - if (that->d()->change.moveId < 0) - RETURN_UNDEFINED(); - return QV4::Encode(that->d()->change.moveId); - } -}; - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChange); - -struct QQmlDelegateModelGroupChangeArray : public QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChangeArray, QV4::Object) - V4_NEEDS_DESTROY -public: - static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) - { - return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes); - } - - quint32 count() const { return d()->changes->count(); } - const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); } - - static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty) - { - if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); - - if (index >= array->count()) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue().asReturnedValue(); - } - - const QQmlChangeSet::Change &change = array->at(index); - - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); - QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototypeOf(changeProto); - object->d()->change = change; - - if (hasProperty) - *hasProperty = true; - return object.asReturnedValue(); - } - - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - - if (id == array->engine()->id_length()->propertyKey()) { - if (hasProperty) - *hasProperty = true; - return QV4::Encode(array->count()); - } - - return Object::virtualGet(m, id, receiver, hasProperty); - } -}; - -void QV4::Heap::QQmlDelegateModelGroupChangeArray::init(const QVector<QQmlChangeSet::Change> &changes) -{ - Object::init(); - this->changes = new QVector<QQmlChangeSet::Change>(changes); - QV4::Scope scope(internalClass->engine); - QV4::ScopedObject o(scope, this); - o->setArrayType(QV4::Heap::ArrayData::Custom); -} - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChangeArray); - -QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), QQmlDelegateModelGroupChange::method_get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("count"), QQmlDelegateModelGroupChange::method_get_count, nullptr); - proto->defineAccessorProperty(QStringLiteral("moveId"), QQmlDelegateModelGroupChange::method_get_moveId, nullptr); - changeProto.set(v4, proto); -} - -QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() -{ -} - -QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV4::ExecutionEngine *v4, - const QVector<QQmlChangeSet::Change> &changes) -{ - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, QQmlDelegateModelGroupChangeArray::create(v4, changes)); - return o.asReturnedValue(); -} - -QT_END_NAMESPACE - -#include "moc_qqmldelegatemodel_p.cpp" diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h deleted file mode 100644 index 0ad8939732..0000000000 --- a/src/qml/types/qqmldelegatemodel_p.h +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLDATAMODEL_P_H -#define QQMLDATAMODEL_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/qtqmlglobal_p.h> -#include <private/qqmllistcompositor_p.h> -#include <private/qqmlobjectmodel_p.h> -#include <private/qqmlincubator_p.h> - -#include <QtCore/qabstractitemmodel.h> -#include <QtCore/qstringlist.h> - -#include <private/qv8engine_p.h> -#include <private/qqmlglobal_p.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -class QQmlChangeSet; -class QQuickPackage; -class QQmlV4Function; -class QQmlDelegateModelGroup; -class QQmlDelegateModelAttached; -class QQmlDelegateModelPrivate; - - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDelegateModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQmlDelegateModel(); - QQmlDelegateModel(QQmlContext *, QObject *parent=nullptr); - ~QQmlDelegateModel(); - - void classBegin() override; - void componentComplete() override; - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const override; - bool isValid() const override { return delegate() != nullptr; } - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - void cancel(int index) override; - QString stringValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQmlDelegateModelGroup *items(); - QQmlDelegateModelGroup *persistedItems(); - QQmlListProperty<QQmlDelegateModelGroup> groups(); - QObject *parts(); - - const QAbstractItemModel *abstractItemModel() const override; - - bool event(QEvent *) override; - - static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector<int> &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector<int> &); - void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint); - -private: - bool isDescendantOf(const QPersistentModelIndex &desc, const QList<QPersistentModelIndex> &parents) const; - - Q_DISABLE_COPY(QQmlDelegateModel) -}; - -class QQmlDelegateModelGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQmlDelegateModelGroup(QObject *parent = nullptr); - QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = nullptr); - ~QQmlDelegateModelGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QQmlV4Handle get(int index); - -public Q_SLOTS: - void insert(QQmlV4Function *); - void create(QQmlV4Function *); - void resolve(QQmlV4Function *); - void remove(QQmlV4Function *); - void addGroups(QQmlV4Function *); - void removeGroups(QQmlV4Function *); - void setGroups(QQmlV4Function *); - void move(QQmlV4Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QQmlV4Handle &removed, const QQmlV4Handle &inserted); -private: - Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) -}; - -class QQmlDelegateModelItem; -class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQmlDelegateModelAttached(QObject *parent); - QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); - ~QQmlDelegateModelAttached() {} - - void resetCurrentIndex(); - void setCacheItem(QQmlDelegateModelItem *item); - - QQmlDelegateModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { Q_EMIT unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQmlDelegateModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; - int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; - - friend class QQmlDelegateModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateModel) -QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDelegateModelGroup) - -#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h deleted file mode 100644 index 0028849828..0000000000 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ /dev/null @@ -1,450 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLDATAMODEL_P_P_H -#define QQMLDATAMODEL_P_P_H - -#include "qqmldelegatemodel_p.h" -#include <private/qv4qobjectwrapper_p.h> - -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlincubator.h> - -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlopenmetaobject_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. -// - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -typedef QQmlListCompositor Compositor; - -class QQmlDelegateModelAttachedMetaObject; -class QQmlAbstractDelegateComponent; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount -{ -public: - QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); - ~QQmlDelegateModelItemMetaType(); - - void initializeMetaObject(); - void initializePrototype(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const QV4::Value &groupNames) const; - - QPointer<QQmlDelegateModel> model; - const int groupCount; - QV4::ExecutionEngine * const v4Engine; - QQmlDelegateModelAttachedMetaObject *metaObject; - const QStringList groupNames; - QV4::PersistentValue modelItemProto; -}; - -class QQmlAdaptorModel; -class QQDMIncubationTask; - -class QQmlDelegateModelItem : public QObject -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(int row READ modelRow NOTIFY rowChanged REVISION 12) - Q_PROPERTY(int column READ modelColumn NOTIFY columnChanged REVISION 12) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) -public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, int modelIndex, - int row, int column); - ~QQmlDelegateModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - void childContextObjectDestroyed(QObject *childContextObject); - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void destroyObject(); - - static QQmlDelegateModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelRow() const { return row; } - int modelColumn() const { return column; } - int modelIndex() const { return index; } - virtual void setModelIndex(int idx, int newRow, int newColumn); - - virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - - static QV4::ReturnedValue get_model(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue set_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &); - static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - - QV4::ExecutionEngine *v4; - QQmlDelegateModelItemMetaType * const metaType; - QQmlContextDataRef contextData; - QPointer<QObject> object; - QPointer<QQmlDelegateModelAttached> attached; - QQDMIncubationTask *incubationTask; - QQmlComponent *delegate; - int poolTime; - int objectRef; - int scriptRef; - int groups; - int index; - -Q_SIGNALS: - void modelIndexChanged(); - Q_REVISION(12) void rowChanged(); - Q_REVISION(12) void columnChanged(); - -protected: - void objectDestroyed(QObject *); - int row; - int column; -}; - -namespace QV4 { -namespace Heap { -struct QQmlDelegateModelItemObject : Object { - inline void init(QQmlDelegateModelItem *item); - void destroy(); - QQmlDelegateModelItem *item; -}; - -} -} - -struct QQmlDelegateModelItemObject : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelItemObject, QV4::Object) - V4_NEEDS_DESTROY -}; - -void QV4::Heap::QQmlDelegateModelItemObject::init(QQmlDelegateModelItem *item) -{ - Object::init(); - this->item = item; -} - - - -class QQmlDelegateModelPrivate; -class QQDMIncubationTask : public QQmlIncubator -{ -public: - QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(nullptr) - , vdm(l) {} - - void statusChanged(Status) override; - void setInitialState(QObject *) override; - - QQmlDelegateModelItem *incubating = nullptr; - QQmlDelegateModelPrivate *vdm = nullptr; - int index[QQmlListCompositor::MaximumGroupCount]; -}; - - -class QQmlDelegateModelGroupEmitter -{ -public: - virtual ~QQmlDelegateModelGroupEmitter() {} - virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList<QQmlDelegateModelGroupEmitter, &QQmlDelegateModelGroupEmitter::emitterNode> QQmlDelegateModelGroupEmitterList; - -class QQmlDelegateModelGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - - QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { - return static_cast<QQmlDelegateModelGroupPrivate *>(QObjectPrivate::get(group)); } - - void setModel(QQmlDelegateModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV4::ExecutionEngine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QPointer<QQmlDelegateModel> model; - QQmlDelegateModelGroupEmitterList emitters; - QQmlChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQmlDelegateModelParts; - -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter -{ - Q_DECLARE_PUBLIC(QQmlDelegateModel) -public: - QQmlDelegateModelPrivate(QQmlContext *); - ~QQmlDelegateModelPrivate(); - - static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { - return static_cast<QQmlDelegateModelPrivate *>(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQmlAdaptorModel *model); - void connectToAbstractItemModel(); - void disconnectFromAbstractItemModel(); - - void requestMoreIfNecessary(); - QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); - QQmlDelegateModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); } - void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it); - void removeCacheItem(QQmlDelegateModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsInserted(const QVector<Compositor::Insert> &inserts); - void itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsRemoved(const QVector<Compositor::Remove> &removes); - void itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts); - void itemsChanged(const QVector<Compositor::Change> &changes); - void emitChanges(); - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - void delegateChanged(bool add = true, bool remove = true); - - bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups); - - int adaptorModelCount() const; - - static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group); - static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property); - static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index); - - void releaseIncubator(QQDMIncubationTask *incubationTask); - void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); - - QQmlAdaptorModel m_adaptorModel; - QQmlListCompositor m_compositor; - QQmlStrongJSQObjectReference<QQmlComponent> m_delegate; - QQmlAbstractDelegateComponent *m_delegateChooser; - QMetaObject::Connection m_delegateChooserChanged; - QQmlDelegateModelItemMetaType *m_cacheMetaType; - QPointer<QQmlContext> m_context; - QQmlDelegateModelParts *m_parts; - QQmlDelegateModelGroupEmitterList m_pendingParts; - - QList<QQmlDelegateModelItem *> m_cache; - QList<QQDMIncubationTask *> m_finishedIncubating; - QList<QByteArray> m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQmlListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - bool m_waitingToFetchMore : 1; - - union { - struct { - QQmlDelegateModelGroup *m_cacheItems; - QQmlDelegateModelGroup *m_items; - QQmlDelegateModelGroup *m_persistedItems; - }; - QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = nullptr); - ~QQmlPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *item) override; - QString stringValue(int index, const QString &role) override; - QList<QByteArray> watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *item, QObject *objectContext) const override; - - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - - void createdPackage(int index, QQuickPackage *package) override; - void initPackage(int index, QQuickPackage *package) override; - void destroyingPackage(QQuickPackage *package) override; - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQmlDelegateModel *m_model; - QHash<QObject *, QQuickPackage *> m_packaged; - QString m_part; - QString m_filterGroup; - QList<QByteArray> m_watchedRoles; - QVector<int> m_pendingPackageInitializations; // vector holds model indices - Compositor::Group m_compositorGroup; - bool m_inheritGroup; - bool m_modelUpdatePending = true; -}; - -class QMetaPropertyBuilder; - -class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQmlDelegateModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - void propertyCreated(int, QMetaPropertyBuilder &) override; - QVariant initialValue(int) override; -}; - -class QQmlDelegateModelParts : public QObject -{ -Q_OBJECT -public: - QQmlDelegateModelParts(QQmlDelegateModel *parent); - - QQmlDelegateModel *model; - QList<QQmlPartsModel *> models; -}; - -class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQmlDelegateModelAttachedMetaObject(); - - void objectDestroyed(QObject *) override; - int metaCall(QObject *, QMetaObject::Call, int _id, void **) override; - -private: - QQmlDelegateModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp deleted file mode 100644 index a23ec0f2b4..0000000000 --- a/src/qml/types/qqmlinstantiator.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 "qqmlinstantiator_p.h" -#include "qqmlinstantiator_p_p.h" -#include <QtQml/QQmlContext> -#include <QtQml/QQmlComponent> -#include <QtQml/QQmlInfo> -#include <QtQml/QQmlError> -#include <QtQml/private/qqmlobjectmodel_p.h> -#if QT_CONFIG(qml_delegate_model) -#include <QtQml/private/qqmldelegatemodel_p.h> -#endif - -QT_BEGIN_NAMESPACE - -QQmlInstantiatorPrivate::QQmlInstantiatorPrivate() - : componentComplete(true) - , effectiveReset(false) - , active(true) - , async(false) -#if QT_CONFIG(qml_delegate_model) - , ownModel(false) -#endif - , requestedIndex(-1) - , model(QVariant(1)) - , instanceModel(nullptr) - , delegate(nullptr) -{ -} - -QQmlInstantiatorPrivate::~QQmlInstantiatorPrivate() -{ - qDeleteAll(objects); -} - -void QQmlInstantiatorPrivate::clear() -{ - Q_Q(QQmlInstantiator); - if (!instanceModel) - return; - if (!objects.count()) - return; - - for (int i=0; i < objects.count(); i++) { - q->objectRemoved(i, objects[i]); - instanceModel->release(objects[i]); - } - objects.clear(); - q->objectChanged(); -} - -QObject *QQmlInstantiatorPrivate::modelObject(int index, bool async) -{ - requestedIndex = index; - QObject *o = instanceModel->object(index, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - requestedIndex = -1; - return o; -} - - -void QQmlInstantiatorPrivate::regenerate() -{ - Q_Q(QQmlInstantiator); - if (!componentComplete) - return; - - int prevCount = q->count(); - - clear(); - - if (!active || !instanceModel || !instanceModel->count() || !instanceModel->isValid()) { - if (prevCount) - q->countChanged(); - return; - } - - for (int i = 0; i < instanceModel->count(); i++) { - QObject *object = modelObject(i, async); - // If the item was already created we won't get a createdItem - if (object) - _q_createdItem(i, object); - } - if (q->count() != prevCount) - q->countChanged(); -} - -void QQmlInstantiatorPrivate::_q_createdItem(int idx, QObject* item) -{ - Q_Q(QQmlInstantiator); - if (objects.contains(item)) //Case when it was created synchronously in regenerate - return; - if (requestedIndex != idx) // Asynchronous creation, reference the object - (void)instanceModel->object(idx); - item->setParent(q); - if (objects.size() < idx + 1) { - int modelCount = instanceModel->count(); - if (objects.capacity() < modelCount) - objects.reserve(modelCount); - objects.resize(idx + 1); - } - if (QObject *o = objects.at(idx)) - instanceModel->release(o); - objects.replace(idx, item); - if (objects.count() == 1) - q->objectChanged(); - q->objectAdded(idx, item); -} - -void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlInstantiator); - - if (!componentComplete || effectiveReset) - return; - - if (reset) { - regenerate(); - if (changeSet.difference() != 0) - q->countChanged(); - return; - } - - int difference = 0; - QHash<int, QVector<QPointer<QObject> > > moved; - const QVector<QQmlChangeSet::Change> &removes = changeSet.removes(); - for (const QQmlChangeSet::Change &remove : removes) { - int index = qMin(remove.index, objects.count()); - int count = qMin(remove.index + remove.count, objects.count()) - index; - if (remove.isMove()) { - moved.insert(remove.moveId, objects.mid(index, count)); - objects.erase( - objects.begin() + index, - objects.begin() + index + count); - } else while (count--) { - QObject *obj = objects.at(index); - objects.remove(index); - q->objectRemoved(index, obj); - if (obj) - instanceModel->release(obj); - } - - difference -= remove.count; - } - - const QVector<QQmlChangeSet::Change> &inserts = changeSet.inserts(); - for (const QQmlChangeSet::Change &insert : inserts) { - int index = qMin(insert.index, objects.count()); - if (insert.isMove()) { - QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId); - objects = objects.mid(0, index) + movedObjects + objects.mid(index); - } else { - if (insert.index <= objects.size()) - objects.insert(insert.index, insert.count, nullptr); - for (int i = 0; i < insert.count; ++i) { - int modelIndex = index + i; - QObject* obj = modelObject(modelIndex, async); - if (obj) - _q_createdItem(modelIndex, obj); - } - } - difference += insert.count; - } - - if (difference != 0) - q->countChanged(); -} - -#if QT_CONFIG(qml_delegate_model) -void QQmlInstantiatorPrivate::makeModel() -{ - Q_Q(QQmlInstantiator); - QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q), q); - instanceModel = delegateModel; - ownModel = true; - delegateModel->setDelegate(delegate); - delegateModel->classBegin(); //Pretend it was made in QML - if (componentComplete) - delegateModel->componentComplete(); -} -#endif - - -/*! - \qmltype Instantiator - \instantiates QQmlInstantiator - \inqmlmodule QtQml - \brief Dynamically creates objects. - - A Instantiator can be used to control the dynamic creation of objects, or to dynamically - create multiple objects from a template. - - The Instantiator element will manage the objects it creates. Those objects are parented to the - Instantiator and can also be deleted by the Instantiator if the Instantiator's properties change. Objects - can also be destroyed dynamically through other means, and the Instantiator will not recreate - them unless the properties of the Instantiator change. - -*/ -QQmlInstantiator::QQmlInstantiator(QObject *parent) - : QObject(*(new QQmlInstantiatorPrivate), parent) -{ -} - -QQmlInstantiator::~QQmlInstantiator() -{ -} - -/*! - \qmlsignal QtQml::Instantiator::objectAdded(int index, QtObject object) - - This signal is emitted when an object is added to the Instantiator. The \a index - parameter holds the index which the object has been given, and the \a object - parameter holds the \l QtObject that has been added. - - The corresponding handler is \c onObjectAdded. -*/ - -/*! - \qmlsignal QtQml::Instantiator::objectRemoved(int index, QtObject object) - - This signal is emitted when an object is removed from the Instantiator. The \a index - parameter holds the index which the object had been given, and the \a object - parameter holds the \l QtObject that has been removed. - - Do not keep a reference to \a object if it was created by this Instantiator, as - in these cases it will be deleted shortly after the signal is handled. - - The corresponding handler is \c onObjectRemoved. -*/ -/*! - \qmlproperty bool QtQml::Instantiator::active - - When active is true, and the delegate component is ready, the Instantiator will - create objects according to the model. When active is false, no objects - will be created and any previously created objects will be destroyed. - - Default is true. -*/ -bool QQmlInstantiator::isActive() const -{ - Q_D(const QQmlInstantiator); - return d->active; -} - -void QQmlInstantiator::setActive(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->active) - return; - d->active = newVal; - emit activeChanged(); - d->regenerate(); -} - -/*! - \qmlproperty bool QtQml::Instantiator::asynchronous - - When asynchronous is true the Instantiator will attempt to create objects - asynchronously. This means that objects may not be available immediately, - even if active is set to true. - - You can use the objectAdded signal to respond to items being created. - - Default is false. -*/ -bool QQmlInstantiator::isAsync() const -{ - Q_D(const QQmlInstantiator); - return d->async; -} - -void QQmlInstantiator::setAsync(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->async) - return; - d->async = newVal; - emit asynchronousChanged(); -} - - -/*! - \qmlproperty int QtQml::Instantiator::count - - The number of objects the Instantiator is currently managing. -*/ - -int QQmlInstantiator::count() const -{ - Q_D(const QQmlInstantiator); - return d->objects.count(); -} - -/*! - \qmlproperty QtQml::Component QtQml::Instantiator::delegate - \default - - The component used to create all objects. - - Note that an extra variable, index, will be available inside instances of the - delegate. This variable refers to the index of the instance inside the Instantiator, - and can be used to obtain the object through the objectAt method of the Instantiator. - - If this property is changed, all instances using the old delegate will be destroyed - and new instances will be created using the new delegate. -*/ -QQmlComponent* QQmlInstantiator::delegate() -{ - Q_D(QQmlInstantiator); - return d->delegate; -} - -void QQmlInstantiator::setDelegate(QQmlComponent* c) -{ - Q_D(QQmlInstantiator); - if (c == d->delegate) - return; - - d->delegate = c; - emit delegateChanged(); - -#if QT_CONFIG(qml_delegate_model) - if (!d->ownModel) - return; - - if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(d->instanceModel)) - dModel->setDelegate(c); - if (d->componentComplete) - d->regenerate(); -#endif -} - -/*! - \qmlproperty variant QtQml::Instantiator::model - - This property can be set to any of the supported \l {qml-data-models}{data models}: - - \list - \li A number that indicates the number of delegates to be created by the repeater - \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass) - \li A string list - \li An object list - \endlist - - The type of model affects the properties that are exposed to the \l delegate. - - Default value is 1, which creates a single delegate instance. - - \sa {qml-data-models}{Data Models} -*/ - -QVariant QQmlInstantiator::model() const -{ - Q_D(const QQmlInstantiator); - return d->model; -} - -void QQmlInstantiator::setModel(const QVariant &v) -{ - Q_D(QQmlInstantiator); - if (d->model == v) - return; - - d->model = v; - //Don't actually set model until componentComplete in case it wants to create its delegates immediately - if (!d->componentComplete) - return; - - QQmlInstanceModel *prevModel = d->instanceModel; - QObject *object = qvariant_cast<QObject*>(v); - QQmlInstanceModel *vim = nullptr; - if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) { -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - delete d->instanceModel; - prevModel = nullptr; - d->ownModel = false; - } -#endif - d->instanceModel = vim; -#if QT_CONFIG(qml_delegate_model) - } else if (v != QVariant(0)){ - if (!d->ownModel) - d->makeModel(); - - if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->instanceModel)) { - d->effectiveReset = true; - dataModel->setModel(v); - d->effectiveReset = false; - } -#endif - } - - if (d->instanceModel != prevModel) { - if (prevModel) { - disconnect(prevModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - disconnect(prevModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - - if (d->instanceModel) { - connect(d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - } - - d->regenerate(); - emit modelChanged(); -} - -/*! - \qmlproperty QtObject QtQml::Instantiator::object - - This is a reference to the first created object, intended as a convenience - for the case where only one object has been created. -*/ -QObject *QQmlInstantiator::object() const -{ - Q_D(const QQmlInstantiator); - if (d->objects.count()) - return d->objects[0]; - return nullptr; -} - -/*! - \qmlmethod QtObject QtQml::Instantiator::objectAt(int index) - - Returns a reference to the object with the given \a index. -*/ -QObject *QQmlInstantiator::objectAt(int index) const -{ - Q_D(const QQmlInstantiator); - if (index >= 0 && index < d->objects.count()) - return d->objects[index]; - return nullptr; -} - -/*! - \internal -*/ -void QQmlInstantiator::classBegin() -{ - Q_D(QQmlInstantiator); - d->componentComplete = false; -} - -/*! - \internal -*/ -void QQmlInstantiator::componentComplete() -{ - Q_D(QQmlInstantiator); - d->componentComplete = true; -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - static_cast<QQmlDelegateModel*>(d->instanceModel)->componentComplete(); - d->regenerate(); - } else -#endif - { - QVariant realModel = d->model; - d->model = QVariant(0); - setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0 - //setModel calls regenerate - } -} - -QT_END_NAMESPACE - -#include "moc_qqmlinstantiator_p.cpp" diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qml/types/qqmlinstantiator_p.h deleted file mode 100644 index ca371adc23..0000000000 --- a/src/qml/types/qqmlinstantiator_p.h +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 QQMLINSTANTIATOR_P_H -#define QQMLINSTANTIATOR_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/qqmlcomponent.h> -#include <QtQml/qqmlparserstatus.h> -#include <QtQml/private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlInstantiatorPrivate; -class Q_QML_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(bool asynchronous READ isAsync WRITE setAsync NOTIFY asynchronousChanged) - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(QObject *object READ object NOTIFY objectChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - -public: - QQmlInstantiator(QObject *parent = nullptr); - ~QQmlInstantiator(); - - bool isActive() const; - void setActive(bool newVal); - - bool isAsync() const; - void setAsync(bool newVal); - - int count() const; - - QQmlComponent* delegate(); - void setDelegate(QQmlComponent* c); - - QVariant model() const; - void setModel(const QVariant &v); - - QObject *object() const; - - Q_INVOKABLE QObject *objectAt(int index) const; - - void classBegin() override; - void componentComplete() override; - -Q_SIGNALS: - void modelChanged(); - void delegateChanged(); - void countChanged(); - void objectChanged(); - void activeChanged(); - void asynchronousChanged(); - - void objectAdded(int index, QObject* object); - void objectRemoved(int index, QObject* object); - -private: - Q_DISABLE_COPY(QQmlInstantiator) - Q_DECLARE_PRIVATE(QQmlInstantiator) - Q_PRIVATE_SLOT(d_func(), void _q_createdItem(int, QObject *)) - Q_PRIVATE_SLOT(d_func(), void _q_modelUpdated(const QQmlChangeSet &, bool)) -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_H diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qml/types/qqmlinstantiator_p_p.h deleted file mode 100644 index 4c76d5c689..0000000000 --- a/src/qml/types/qqmlinstantiator_p_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 QQMLINSTANTIATOR_P_P_H -#define QQMLINSTANTIATOR_P_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 "qqmlinstantiator_p.h" -#include <QObject> -#include <private/qobject_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlobjectmodel_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlInstantiator) - -public: - QQmlInstantiatorPrivate(); - ~QQmlInstantiatorPrivate(); - - void clear(); - void regenerate(); -#if QT_CONFIG(qml_delegate_model) - void makeModel(); -#endif - void _q_createdItem(int, QObject *); - void _q_modelUpdated(const QQmlChangeSet &, bool); - QObject *modelObject(int index, bool async); - - static QQmlInstantiatorPrivate *get(QQmlInstantiator *instantiator) { return instantiator->d_func(); } - static const QQmlInstantiatorPrivate *get(const QQmlInstantiator *instantiator) { return instantiator->d_func(); } - - bool componentComplete:1; - bool effectiveReset:1; - bool active:1; - bool async:1; -#if QT_CONFIG(qml_delegate_model) - bool ownModel:1; -#endif - int requestedIndex; - QVariant model; - QQmlInstanceModel *instanceModel; - QQmlComponent *delegate; - QVector<QPointer<QObject> > objects; -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_P_H diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qml/types/qqmlitemmodels.qdoc deleted file mode 100644 index f6e1b0b1b9..0000000000 --- a/src/qml/types/qqmlitemmodels.qdoc +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \page qmodelindex-and-related-classes-in-qml.html - \title QModelIndex and related Classes in QML - - Since Qt 5.5, QModelIndex and QPersistentModelIndex are exposed in QML as - value-based types. Also exposed in a similar fashion are QModelIndexList, - QItemSelectionRange and QItemSelection. All objects from these types can - be passed back and forth between QML and C++ as \c var properties or plain - JavaScript variables. - - Below you will find an overview of the API exposed to QML for these classes. - For more information, refer to their C++ documentation. - - \note Since all these types are exposed as \l{Q_GADGET}{gadgets}, there are no property - change notification signals emitted. Therefore binding to their properties - may not give the expected results. This is especially true for QPersistentModelIndex. - - \section1 QModelIndex and QPersistentModelIndex Types - - \list - \li \b row : int - \li \b column : int - \li \b parent : QModelIndex - \li \b valid : bool - \li \b model : QAbstractItemModel - \li \b internalId : quint64 - \endlist - - All these properties are read-only, as are their C++ counterparts. - - \note The usual caveats apply to QModelIndex in QML. If the underlying model changes - or gets deleted, it may become dangerous to access its properties. Therefore, you - should not store any QModelIndex objects. You can, however, store QPersistentModelIndexe - objects in a safe way. - - \section1 QModelIndexList Type - - \l QModelIndexList is exposed in QML as a JavaScript array. Conversions are - automatically made from and to C++. In fact, any JavaScript array can be - converted back to QModelIndexList, with non-QModelIndex objects replaced by - invalid \l{QModelIndex}es. - - \note QModelIndex to QPersistentModelIndex conversion happens when accessing - the array elements because any QModelIndexList property retains reference - semantics when exposed this way. - - \section1 \l QItemSelectionRange Type - - \list - \li \b top : int - \li \b left : int - \li \b bottom : int - \li \b right : int - \li \b width : int - \li \b height : int - \li \b topLeft : QPersistentModelIndex - \li \b bottomRight : QPersistentModelIndex - \li \b parent : QModelIndex - \li \b valid : bool - \li \b empty : bool - \li \b model : QAbstractItemModel - \endlist - - All these properties are read-only, as are their C++ counterparts. In addition, - we also expose the following functions: - - \list - \li bool \b{contains}(QModelIndex \e index) - \li bool \b{contains}(int \e row, int \e column, QModelIndex \e parentIndex) - \li bool \b{intersects}(QItemSelectionRange \e other) - \li QItemSelectionRange \b{intersected}(QItemSelectionRange \e other) - \endlist - - \section1 QItemSelection Type - - Similarly to QModelIndexList, \l QItemSelection is exposed in QML as a JavaScript - array of QItemSelectionRange objects. Conversions are automatically made from and to C++. - In fact, any JavaScript array can be converted back to QItemSelection, with - non-QItemSelectionRange objects replaced by empty \l {QItemSelectionRange}s. - - - \sa ItemSelectionModel -*/ diff --git a/src/qml/types/qqmlitemselectionmodel.qdoc b/src/qml/types/qqmlitemselectionmodel.qdoc deleted file mode 100644 index 43da4f7a55..0000000000 --- a/src/qml/types/qqmlitemselectionmodel.qdoc +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \qmltype ItemSelectionModel - \instantiates QItemSelectionModel - \inqmlmodule QtQml.Models - \since 5.5 - \ingroup qtquick-models - - \brief Instantiates a QItemSelectionModel to be used in conjunction - with a QAbstractItemModel and any view supporting it. - - \sa QItemSelectionModel, {Models and Views in Qt Quick} -*/ - - -/*! - \qmlproperty QAbstractItemModel ItemSelectionModel::model - - This property's value must match the view's model. -*/ - -/*! - \qmlproperty bool ItemSelectionModel::hasSelection - \readonly - - It will trigger property binding updates every time \l selectionChanged() - is emitted, even though its value hasn't changed. - - \sa selection, selectedIndexes, select(), selectionChanged() -*/ - -/*! - \qmlproperty QModelIndex ItemSelectionModel::currentIndex - \readonly - - Use \l setCurrentIndex() to set its value. - - \sa setCurrentIndex(), currentChanged() -*/ - -/*! - \qmlproperty QModelIndexList ItemSelectionModel::selectedIndexes - \readonly - - Contains the list of all the indexes in the selection model. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isSelected(QModelIndex index) - - Returns \c true if the given model item \a index is selected. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isRowSelected(int row, QModelIndex parent) - - Returns \c true if all items are selected in the \a row with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same row, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isColumnSelected(int column, QModelIndex parent) - - Returns \c true if all items are selected in the \a column with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same column, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::rowIntersectsSelection(int row, QModelIndex parent) - - Returns \c true if there are any items selected in the \a row with the - given \a parent. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::columnIntersectsSelection(int column, QModelIndex parent) - - Returns \c true if there are any items selected in the \a column with the - given \a parent. -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedRows(int column) - - Returns the indexes in the given \a column for the rows where all columns - are selected. - - \sa selectedColumns() -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedColumns(int row) - - Returns the indexes in the given \a row for columns where all rows are - selected. - - \sa selectedRows() -*/ - -/*! - \qmlproperty object ItemSelectionModel::selection - \readonly - - Holds the selection ranges stored in the selection model. -*/ - -/*! - \qmlmethod void ItemSelectionModel::setCurrentIndex(QModelIndex index, SelectionFlags command) - - Sets the model item \a index to be the current item, and emits - currentChanged(). The current item is used for keyboard navigation and - focus indication; it is independent of any selected items, although a - selected item can also be the current item. - - Depending on the specified \a command, the \a index can also become part - of the current selection. - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. - - \sa select() -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QModelIndex index, SelectionFlags command) - \keyword itemselectionmodelselectindex - - Selects the model item \a index using the specified \a command, and emits - selectionChanged(). - - Valid values for the \a command parameter, are: - - \value NoUpdate No selection will be made. - \value Clear The complete selection will be cleared. - \value Select All specified indexes will be selected. - \value Deselect All specified indexes will be deselected. - \value Toggle All specified indexes will be selected or - deselected depending on their current state. - \value Current The current selection will be updated. - \value Rows All indexes will be expanded to span rows. - \value Columns All indexes will be expanded to span columns. - \value SelectCurrent A combination of Select and Current, provided for - convenience. - \value ToggleCurrent A combination of Toggle and Current, provided for - convenience. - \value ClearAndSelect A combination of Clear and Select, provided for - convenience. -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QItemSelection selection, SelectionFlags command) - - Selects the item \a selection using the specified \a command, and emits - selectionChanged(). - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clear() - - Clears the selection model. Emits selectionChanged() and currentChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::reset() - - Clears the selection model. Does not emit any signals. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearSelection() - - Clears the selection in the selection model. Emits selectionChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearCurrentIndex() - - Clears the current index. Emits currentChanged(). -*/ - -/*! - \qmlsignal ItemSelectionModel::selectionChanged(QItemSelection selected, QItemSelection deselected) - - This signal is emitted whenever the selection changes. The change in the - selection is represented as an item selection of \a deselected items and - an item selection of \a selected items. - - Note the that the current index changes independently from the selection. - Also note that this signal will not be emitted when the item model is reset. - - \sa select(), currentChanged() -*/ - -/*! - \qmlsignal ItemSelectionModel::currentChanged(QModelIndex current, QModelIndex previous) - - This signal is emitted whenever the current item changes. The \a previous - model item index is replaced by the \a current index as the selection's - current item. - - Note that this signal will not be emitted when the item model is reset. - - \sa currentIndex, setCurrentIndex(), selectionChanged() -*/ diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp deleted file mode 100644 index 565e60b3c1..0000000000 --- a/src/qml/types/qqmllistmodel.cpp +++ /dev/null @@ -1,2900 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmllistmodel_p_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include <private/qqmlopenmetaobject_p.h> -#include <private/qqmljsast_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qjsvalue_p.h> - -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlnotifier_p.h> - -#include <private/qv4object_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4objectiterator_p.h> -#include <private/qv4alloca_p.h> -#include <private/qv4lookup_p.h> - -#include <qqmlcontext.h> -#include <qqmlinfo.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qstack.h> -#include <QXmlStreamReader> -#include <QtCore/qdatetime.h> -#include <QScopedValueRollback> - -Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template <typename T> -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - static const QString roleTypeNames[] = { - QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"), - QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"), - QStringLiteral("DateTime"), QStringLiteral("Function") - }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - return roleTypeNames[t]; - - return QString(); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey = key->toQString(); - - return createRole(qkey, type); -} - -const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) -{ - const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - - Role *r = new Role; - r->name = key; - r->type = type; - - if (type == Role::List) { - r->subLayout = new ListLayout; - } else { - r->subLayout = nullptr; - } - - int dataSize = dataSizes[type]; - int dataAlignment = dataAlignments[type]; - - int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); - if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { - r->blockIndex = ++currentBlock; - r->blockOffset = 0; - currentBlockOffset = dataSize; - } else { - r->blockIndex = currentBlock; - r->blockOffset = dataOffset; - currentBlockOffset = dataOffset + dataSize; - } - - int roleIndex = roles.count(); - r->index = roleIndex; - - roles.append(r); - roleHash.insert(key, r); - - return *r; -} - -ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) -{ - const int otherRolesCount = other->roles.count(); - roles.reserve(otherRolesCount); - for (int i=0 ; i < otherRolesCount; ++i) { - Role *role = new Role(other->roles[i]); - roles.append(role); - roleHash.insert(role->name, role); - } - currentBlockOffset = other->currentBlockOffset; - currentBlock = other->currentBlock; -} - -ListLayout::~ListLayout() -{ - qDeleteAll(roles); -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - int roleOffset = target->roles.count(); - int newRoleCount = src->roles.count() - roleOffset; - - for (int i=0 ; i < newRoleCount ; ++i) { - Role *role = new Role(src->roles[roleOffset + i]); - target->roles.append(role); - target->roleHash.insert(role->name, role); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -ListLayout::Role::Role(const Role *other) -{ - name = other->name; - type = other->type; - blockIndex = other->blockIndex; - blockOffset = other->blockOffset; - index = other->index; - if (other->subLayout) - subLayout = new ListLayout(other->subLayout); - else - subLayout = nullptr; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - Role::DataType type; - - switch (data.type()) { - case QVariant::Double: type = Role::Number; break; - case QVariant::Int: type = Role::Number; break; - case QVariant::Bool: type = Role::Bool; break; - case QVariant::String: type = Role::String; break; - case QVariant::Map: type = Role::VariantMap; break; - case QVariant::DateTime: type = Role::DateTime; break; - case QVariant::UserType: { - if (data.userType() == qMetaTypeId<QJSValue>() && - data.value<QJSValue>().isCallable()) { - type = Role::Function; - break; - } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() - && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { - type = Role::String; - break; - } else { - type = Role::List; - break; - } - } - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlWarning(nullptr) << "Can't create role for unsupported data type"; - return nullptr; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -StringOrTranslation::StringOrTranslation(const QString &s) -{ - d.setFlag(); - setString(s); -} - -StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -StringOrTranslation::~StringOrTranslation() -{ - clear(); -} - -void StringOrTranslation::setString(const QString &s) -{ - d.setFlag(); - clear(); - QStringData *stringData = const_cast<QString &>(s).data_ptr(); - d = stringData; - if (stringData) - stringData->ref.ref(); -} - -void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -QString StringOrTranslation::toString(const QQmlListModel *owner) const -{ - if (d.isNull()) - return QString(); - if (d.isT1()) { - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); - } - if (!owner) - return QString(); - return d.asT2()->valueAsString(owner->m_compilationUnit.data()); -} - -QString StringOrTranslation::asString() const -{ - if (d.isNull()) - return QString(); - if (!d.isT1()) - return QString(); - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); -} - -void StringOrTranslation::clear() -{ - if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { - if (!strData->ref.deref()) - QStringData::deallocate(strData); - } - d = static_cast<QStringData *>(nullptr); -} - -QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == nullptr) { - void *memory = operator new(sizeof(QObject) + sizeof(QQmlData)); - void *ddataMemory = ((char *)memory) + sizeof(QObject); - e->m_objectCache = new (memory) QObject; - QQmlData *ddata = new (ddataMemory) QQmlData; - ddata->ownMemory = false; - QObjectPrivate::get(e->m_objectCache)->declarativeData = ddata; - (void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex); - } - return e->m_objectCache; -} - -bool ListModel::sync(ListModel *src, ListModel *target) -{ - // Sanity check - - bool hasChanges = false; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0; i < target->elements.count(); ++i) { - ListElement *e = target->elements.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *e = src->elements.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - QQmlListModel *targetModel = target->m_modelCache; - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - if (targetModel) - targetModel->beginRemoveRows(QModelIndex(), i, i); - s.target->destroy(target->m_layout); - target->elements.removeOne(s.target); - delete s.target; - if (targetModel) - targetModel->endRemoveRows(); - ++rowsRemoved; - --i; - continue; - } - } - - // Sync the layouts - ListLayout::sync(src->m_layout, target->m_layout); - - // Clear the target list, and append in correct order from the source - target->elements.clear(); - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *srcElement = src->elements.at(i); - ElementSync &s = elementHash.find(srcElement->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - ListElement *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new ListElement(srcElement->getUid()); - } - s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout); - target->elements.append(targetElement); - } - - target->updateCacheIndices(); - - // Update values stored in target meta objects - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements[i]; - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (targetModel) { - if (s.targetIndex == -1) { - targetModel->beginInsertRows(QModelIndex(), i, i); - targetModel->endInsertRows(); - } else { - targetModel->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - targetModel->endMoveRows(); - } - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = targetModel->createIndex(i, 0); - if (targetModel) - targetModel->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache) : m_layout(layout), m_modelCache(modelCache) -{ -} - -void ListModel::destroy() -{ - for (const auto &destroyer : remove(0, elements.count())) - destroyer(); - - m_layout = nullptr; - if (m_modelCache && m_modelCache->m_primary == false) - delete m_modelCache; - m_modelCache = nullptr; -} - -int ListModel::appendElement() -{ - int elementIndex = elements.count(); - newElement(elementIndex); - return elementIndex; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(index); -} - -void ListModel::move(int from, int to, int n) -{ - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<ListElement *, 4> store; - for (int i=0 ; i < (to-from) ; ++i) - store.append(elements[from+n+i]); - for (int i=0 ; i < n ; ++i) - store.append(elements[from+i]); - for (int i=0 ; i < store.count() ; ++i) - elements[from+i] = store[i]; - - updateCacheIndices(from, to + n); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -void ListModel::updateCacheIndices(int start, int end) -{ - int count = elements.count(); - - if (end < 0 || end > count) - end = count; - - for (int i = start; i < end; ++i) { - ListElement *e = elements.at(i); - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->m_elementIndex = i; - } -} - -QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - if (roleIndex >= m_layout->roleCount()) - return QVariant(); - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) -{ - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (const QV4::String *s = propertyValue->as<QV4::String>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - roleIndex = e->setStringProperty(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->booleanValue()); - } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = dd->toQDateTime(); - roleIndex = e->setDateTimeProperty(r, dt); - } else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function); - QV4::ScopedFunctionObject func(scope, f); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, v4, func); - roleIndex = e->setFunctionProperty(r, jsv); - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (role.type == ListLayout::Role::QObject) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) { - QV4::ScopedObject obj(scope, o); - roleIndex = e->setVariantMapProperty(role, obj); - } - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - - if (roleIndex != -1) - roles->append(roleIndex); - } - - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(*roles); -} - -void ListModel::set(int elementIndex, QV4::Object *object) -{ - if (!object) - return; - - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - QV4::ScopedObject o(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Add the value now - if (QV4::String *s = propertyValue->stringValue()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - if (r.type == ListLayout::Role::String) - e->setStringPropertyFast(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - if (r.type == ListLayout::Role::Number) { - e->setDoublePropertyFast(r, propertyValue->asDouble()); - } - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - e->setListPropertyFast(r, subModel); - } - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - if (r.type == ListLayout::Role::Bool) { - e->setBoolPropertyFast(r, propertyValue->booleanValue()); - } - } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = date->toQDateTime();; - e->setDateTimePropertyFast(r, dt); - } - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (r.type == ListLayout::Role::QObject) - e->setQObjectPropertyFast(r, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, o); - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - } -} - -QVector<std::function<void()>> ListModel::remove(int index, int count) -{ - QVector<std::function<void()>> toDestroy; - auto layout = m_layout; - for (int i=0 ; i < count ; ++i) { - auto element = elements[index+i]; - toDestroy.append([element, layout](){ - element->destroy(layout); - delete element; - }); - } - elements.remove(index, count); - updateCacheIndices(index); - return toDestroy; -} - -void ListModel::insert(int elementIndex, QV4::Object *object) -{ - insertElement(elementIndex); - set(elementIndex, object); -} - -int ListModel::append(QV4::Object *object) -{ - int elementIndex = appendElement(); - set(elementIndex, object); - return elementIndex; -} - -int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - - const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); - if (r) { - roleIndex = e->setVariantProperty(*r, data); - - ModelNodeMetaObject *cache = e->objectCache(); - - if (roleIndex != -1 && cache) - cache->updateValues(QVector<int>(1, roleIndex)); - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - const ListLayout::Role *r = m_layout->getExistingRole(key); - if (r) - roleIndex = e->setJsProperty(*r, data, eng); - } - - return roleIndex; -} - -inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) -{ - ListElement *e = this; - int blockIndex = 0; - while (blockIndex < role.blockIndex) { - if (e->next == nullptr) { - e->next = new ListElement; - e->next->uid = uid; - } - e = e->next; - ++blockIndex; - } - - char *mem = &e->data[role.blockOffset]; - return mem; -} - -ModelNodeMetaObject *ListElement::objectCache() -{ - if (!m_objectCache) - return nullptr; - return ModelNodeMetaObject::get(m_objectCache); -} - -StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - return s; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QPointer<QObject> *o = reinterpret_cast<QPointer<QObject> *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) - map = reinterpret_cast<QVariantMap *>(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) - dt = reinterpret_cast<QDateTime *>(mem); - - return dt; -} - -QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role) -{ - QJSValue *f = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) - f = reinterpret_cast<QJSValue *>(mem); - - return f; -} - -QPointer<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QPointer<QObject> *o = nullptr; - - if (existingGuard) - o = reinterpret_cast<QPointer<QObject> *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast<double *>(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); - if (value->isSet()) - data = value->toString(owner); - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast<bool *>(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast<ListModel **>(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == nullptr) { - model->m_modelCache = new QQmlListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = reinterpret_cast<QPointer<QObject> *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - data = *dt; - } - } - break; - case ListLayout::Role::Function: - { - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *func = reinterpret_cast<QJSValue *>(mem); - data = QVariant::fromValue(*func); - } - } - break; - default: - break; - } - - return data; -} - -int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); - bool changed; - if (!c->isSet() || c->isTranslation()) - changed = true; - else - changed = c->asString().compare(s) != 0; - c->setString(s); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Number) { - char *mem = getPropertyMemory(role); - double *value = reinterpret_cast<double *>(mem); - bool changed = *value != d; - *value = d; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Bool) { - char *mem = getPropertyMemory(role); - bool *value = reinterpret_cast<bool *>(mem); - bool changed = *value != b; - *value = b; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::List) { - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - if (*value && *value != m) { - (*value)->destroy(); - delete *value; - } - *value = m; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QPointer<QObject> *g = reinterpret_cast<QPointer<QObject> *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QPointer(); - } else { - changed = true; - } - new (mem) QPointer<QObject>(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - map->~QMap(); - } - new (mem) QVariantMap(o->engine()->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - if (m && map->isSharedWith(*m)) - return roleIndex; - map->~QMap(); - } else if (!m) { - return roleIndex; - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Function) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *f = reinterpret_cast<QJSValue *>(mem); - f->~QJSValue(); - } - new (mem) QJSValue(f); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - s->setTranslation(b); - roleIndex = role.index; - } - - return roleIndex; -} - - -void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) -{ - char *mem = getPropertyMemory(role); - new (mem) StringOrTranslation(s); -} - -void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) -{ - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - *value = d; -} - -void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) -{ - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - *value = b; -} - -void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) -{ - char *mem = getPropertyMemory(role); - new (mem) QPointer<QObject>(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, QV4::Object *o) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = o->engine()->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f) -{ - char *mem = getPropertyMemory(role); - new (mem) QJSValue(f); -} - -void ListElement::clearProperty(const ListLayout::Role &role) -{ - switch (role.type) { - case ListLayout::Role::String: - setStringProperty(role, QString()); - break; - case ListLayout::Role::Number: - setDoubleProperty(role, 0.0); - break; - case ListLayout::Role::Bool: - setBoolProperty(role, false); - break; - case ListLayout::Role::List: - setListProperty(role, nullptr); - break; - case ListLayout::Role::QObject: - setQObjectProperty(role, nullptr); - break; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, (QVariantMap *)nullptr); - break; - case ListLayout::Role::Function: - setFunctionProperty(role, QJSValue()); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = nullptr; - uid = uidCounter.fetchAndAddOrdered(1); - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = nullptr; - uid = existingUid; - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout) -{ - QVector<int> changedRoles; - for (int i=0 ; i < srcLayout->roleCount() ; ++i) { - const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); - const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); - - int roleIndex = -1; - switch (srcRole.type) { - case ListLayout::Role::List: - { - ListModel *srcSubModel = src->getListProperty(srcRole); - ListModel *targetSubModel = target->getListProperty(targetRole); - - if (srcSubModel) { - if (targetSubModel == nullptr) { - targetSubModel = new ListModel(targetRole.subLayout, nullptr); - target->setListPropertyFast(targetRole, targetSubModel); - } - if (ListModel::sync(srcSubModel, targetSubModel)) - roleIndex = targetRole.index; - } - } - break; - case ListLayout::Role::QObject: - { - QObject *object = src->getQObjectProperty(srcRole); - roleIndex = target->setQObjectProperty(targetRole, object); - } - break; - case ListLayout::Role::String: - case ListLayout::Role::Number: - case ListLayout::Role::Bool: - case ListLayout::Role::DateTime: - case ListLayout::Role::Function: - { - QVariant v = src->getProperty(srcRole, nullptr, nullptr); - roleIndex = target->setVariantProperty(targetRole, v); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - roleIndex = target->setVariantMapProperty(targetRole, map); - } - break; - default: - break; - } - if (roleIndex >= 0) - changedRoles << roleIndex; - } - - return changedRoles; -} - -void ListElement::destroy(ListLayout *layout) -{ - if (layout) { - for (int i=0 ; i < layout->roleCount() ; ++i) { - const ListLayout::Role &r = layout->getExistingRole(i); - - switch (r.type) { - case ListLayout::Role::String: - { - StringOrTranslation *string = getStringProperty(r); - if (string) - string->~StringOrTranslation(); - } - break; - case ListLayout::Role::List: - { - ListModel *model = getListProperty(r); - if (model) { - model->destroy(); - delete model; - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = getGuardProperty(r); - if (guard) - guard->~QPointer(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - break; - case ListLayout::Role::Function: - { - QJSValue *f = getFunctionProperty(r); - if (f) - f->~QJSValue(); - } - break; - default: - // other types don't need explicit cleanup. - break; - } - } - - if (m_objectCache) { - m_objectCache->~QObject(); - operator delete(m_objectCache); - } - } - - if (next) - next->destroy(nullptr); - uid = -1; -} - -int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) -{ - int roleIndex = -1; - - switch (role.type) { - case ListLayout::Role::Number: - roleIndex = setDoubleProperty(role, d.toDouble()); - break; - case ListLayout::Role::String: - if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) - roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); - else - roleIndex = setStringProperty(role, d.toString()); - break; - case ListLayout::Role::Bool: - roleIndex = setBoolProperty(role, d.toBool()); - break; - case ListLayout::Role::List: - roleIndex = setListProperty(role, d.value<ListModel *>()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - case ListLayout::Role::Function: - roleIndex = setFunctionProperty(role, d.value<QJSValue>()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - QV4::Scope scope(eng); - - // Add the value now - if (d.isString()) { - QString qstr = d.toQString(); - roleIndex = setStringProperty(role, qstr); - } else if (d.isNumber()) { - roleIndex = setDoubleProperty(role, d.asDouble()); - } else if (d.as<QV4::ArrayObject>()) { - QV4::ScopedArrayObject a(scope, d); - if (role.type == ListLayout::Role::List) { - QV4::Scope scope(a->engine()); - QV4::ScopedObject o(scope); - - ListModel *subModel = new ListModel(role.subLayout, nullptr); - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d.isBoolean()) { - roleIndex = setBoolProperty(role, d.booleanValue()); - } else if (d.as<QV4::DateObject>()) { - QV4::Scoped<QV4::DateObject> dd(scope, d); - QDateTime dt = dd->toQDateTime(); - roleIndex = setDateTimeProperty(role, dt); - } else if (d.as<QV4::FunctionObject>()) { - QV4::ScopedFunctionObject f(scope, d); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, eng, f); - roleIndex = setFunctionProperty(role, jsv); - } else if (d.isObject()) { - QV4::ScopedObject o(scope, d); - QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>(); - if (role.type == ListLayout::Role::QObject && wrapper) { - QObject *o = wrapper->object(); - roleIndex = setQObjectProperty(role, o); - } else if (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, o); - } - } else if (d.isNullOrUndefined()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelNodeMetaObject::ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex) -: QQmlOpenMetaObject(object), m_enabled(false), m_model(model), m_elementIndex(elementIndex), m_initialized(false) -{} - -void ModelNodeMetaObject::initialize() -{ - const int roleCount = m_model->m_listModel->roleCount(); - QVector<QByteArray> properties; - properties.reserve(roleCount); - for (int i = 0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - properties << name; - } - type()->createProperties(properties); - updateValues(); - m_enabled = true; -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - -QAbstractDynamicMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object) -{ - if (!m_initialized) { - m_initialized = true; - initialize(); - } - return QQmlOpenMetaObject::toDynamicMetaObject(object); -} - -ModelNodeMetaObject *ModelNodeMetaObject::get(QObject *obj) -{ - QObjectPrivate *op = QObjectPrivate::get(obj); - return static_cast<ModelNodeMetaObject*>(op->metaObject); -} - -void ModelNodeMetaObject::updateValues() -{ - const int roleCount = m_model->m_listModel->roleCount(); - if (!m_initialized) { - if (roleCount) { - Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int)); - for (int i = 0; i < roleCount; ++i) - changedRoles[i] = i; - emitDirectNotifies(changedRoles, roleCount); - } - return; - } - for (int i=0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, i); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::updateValues(const QVector<int> &roles) -{ - if (!m_initialized) { - emitDirectNotifies(roles.constData(), roles.count()); - return; - } - int roleCount = roles.count(); - for (int i=0 ; i < roleCount ; ++i) { - int roleIndex = roles.at(i); - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, roleIndex); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QString propName = QString::fromUtf8(name(index)); - const QVariant value = this->value(index); - - QV4::Scope scope(m_model->engine()); - QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); - - int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine); - if (roleIndex != -1) - m_model->emitItemsChanged(m_elementIndex, 1, QVector<int>(1, roleIndex)); -} - -// Does the emission of the notifiers when we haven't created the meta-object yet -void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCount) -{ - Q_ASSERT(!m_initialized); - QQmlData *ddata = QQmlData::get(object(), /*create*/false); - if (!ddata) - return; - // There's nothing to emit if we're a list model in a worker thread. - if (!qmlEngine(m_model)) - return; - for (int i = 0; i < roleCount; ++i) { - const int changedRole = changedRoles[i]; - QQmlNotifier::notify(ddata, changedRole); - } -} - -namespace QV4 { - -bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) -{ - if (!id.isString()) - return Object::virtualPut(m, id, value, receiver); - QString propName = id.toQString(); - - ModelObject *that = static_cast<ModelObject*>(m); - - ExecutionEngine *eng = that->engine(); - const int elementIndex = that->d()->elementIndex(); - int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); - if (roleIndex != -1) - that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - - ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); - if (mo->initialized()) - mo->emitPropertyNotification(propName.toUtf8()); - return true; -} - -ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) -{ - if (!id.isString()) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - - const ModelObject *that = static_cast<const ModelObject*>(m); - Scope scope(that); - ScopedString name(scope, id.asStringOrSymbol()); - const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); - if (!role) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - if (hasProperty) - *hasProperty = true; - - if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); - if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false); - } - - const int elementIndex = that->d()->elementIndex(); - QVariant value = that->d()->m_model->data(elementIndex, role->index); - return that->engine()->fromVariant(value); -} - -ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) -{ - lookup->getter = Lookup::getterFallback; - return lookup->getter(lookup, engine, *object); -} - -struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator -{ - int roleNameIndex = 0; - ~ModelObjectOwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; - -}; - -PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) -{ - const ModelObject *that = static_cast<const ModelObject *>(o); - - ExecutionEngine *v4 = that->engine(); - if (roleNameIndex < that->listModel()->roleCount()) { - Scope scope(that->engine()); - const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex); - ++roleNameIndex; - ScopedString roleName(scope, v4->newString(role.name)); - if (attrs) - *attrs = QV4::Attr_Data; - if (pd) { - QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index); - pd->value = v4->fromVariant(value); - } - return roleName->toPropertyKey(); - } - - // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add - // unnecessary entries that relate to the roles used. These just create extra work - // later on as they will just be ignored. - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); -} - -OwnPropertyKeyIterator *ModelObject::virtualOwnPropertyKeys(const Object *m, Value *target) -{ - *target = *m; - return new ModelObjectOwnPropertyKeyIterator; -} - -DEFINE_OBJECT_VTABLE(ModelObject); - -} // namespace QV4 - -DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector<int> roles; - object->updateValues(obj, roles); - return object; -} - -QVector<int> DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target) -{ - QVector<int> changedRoles; - for (int i = 0; i < src->m_meta->count(); ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>()); - QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>()); - - bool modelHasChanges = false; - if (srcModel) { - if (targetModel == nullptr) - targetModel = QQmlListModel::createWithOwner(target->m_owner); - - modelHasChanges = QQmlListModel::sync(srcModel, targetModel); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - if (target->setValue(name, value) || modelHasChanges) - changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name)); - } - return changedRoles; -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> &roles) -{ - for (auto it = object.cbegin(), end = object.cend(); it != end; ++it) { - const QString &key = it.key(); - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = it.value(); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (value.userType() == qMetaTypeId<QJSValue>()) - value = value.value<QJSValue>().toVariant(); - - if (value.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>()); - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>()); - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>()); - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQmlListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (v.userType() == qMetaTypeId<QJSValue>()) - v= v.value<QJSValue>().toVariant(); - - if (v.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - if (elementIndex != -1) { - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - if (roleIndex != -1) - parentModel->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a free-form list data source. - - The ListModel is a simple container of ListElement definitions, each - containing data roles. The contents can be defined dynamically, or - explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet ../quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.mjs, looks like this: - - \snippet ../quick/threading/threadedlistmodel/dataloader.mjs 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \c WorkerScript.onMessage() is invoked in \c dataloader.mjs, - which appends the current time to the list model. - - Note the call to sync() from the external thread. - You must call sync() or else the changes made to the list from that - thread will not be reflected in the list model in the main thread. - - \sa {qml-data-models}{Data Models}, {Qt Quick Examples - Threading}, {Qt QML} -*/ - -QQmlListModel::QQmlListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = nullptr; - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this); - - m_engine = nullptr; -} - -QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = nullptr; - m_listModel = data; - - m_engine = engine; - m_compilationUnit = owner->m_compilationUnit; -} - -QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this); - - if (m_dynamicRoles) - sync(orig, this); - else - ListModel::sync(orig->m_listModel, m_listModel); - - m_engine = nullptr; - m_compilationUnit = orig->m_compilationUnit; -} - -QQmlListModel::~QQmlListModel() -{ - qDeleteAll(m_modelObjects); - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = nullptr; - - delete m_layout; - m_layout = nullptr; -} - -QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) -{ - QQmlListModel *model = new QQmlListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV4::ExecutionEngine *QQmlListModel::engine() const -{ - if (m_engine == nullptr) { - m_engine = qmlEngine(this)->handle(); - } - - return m_engine; -} - -bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - bool hasChanges = false; - - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0 ; i < target->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0 ; i < src->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - target->beginRemoveRows(QModelIndex(), i, i); - target->m_modelObjects.remove(i, 1); - target->endRemoveRows(); - delete s.target; - ++rowsRemoved; - --i; - continue; - } - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i = 0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = src->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new DynamicRoleModelNode(target, element->getUid()); - } - s.changedRoles = DynamicRoleModelNode::sync(element, targetElement); - target->m_modelObjects.append(targetElement); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (s.targetIndex == -1) { - target->beginInsertRows(QModelIndex(), i, i); - target->endInsertRows(); - } else { - target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - target->endMoveRows(); - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = target->createIndex(i, 0); - emit target->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; -} - -void QQmlListModel::emitItemsAboutToBeInserted(int index, int count) -{ - Q_ASSERT(index >= 0 && count >= 0); - if (m_mainThread) - beginInsertRows(QModelIndex(), index, index + count - 1); -} - -void QQmlListModel::emitItemsInserted() -{ - if (m_mainThread) { - endInsertRows(); - emit countChanged(); - } -} - -QQmlListModelWorkerAgent *QQmlListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQmlListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQmlListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQmlListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -bool QQmlListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - const int row = index.row(); - if (row >= count() || row < 0) - return false; - - if (m_dynamicRoles) { - const QByteArray property = m_roles.at(role).toUtf8(); - if (m_modelObjects[row]->setValue(property, value)) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } else { - const ListLayout::Role &r = m_listModel->getExistingRole(role); - const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value); - if (roleIndex != -1) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } - - return false; -} - -QVariant QQmlListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash<int, QByteArray> QQmlListModel::roleNames() const -{ - QHash<int, QByteArray> roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == nullptr) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlWarning(this) << tr("unable to enable static roles as this model is not empty"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlWarning(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int ListModel::count - The number of data entries in the model. -*/ -int QQmlListModel::count() const -{ - return m_dynamicRoles ? m_modelObjects.count() : m_listModel->elementCount(); -} - -/*! - \qmlmethod ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQmlListModel::clear() -{ - removeElements(0, count()); -} - -/*! - \qmlmethod ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQmlListModel::remove(QQmlV4Function *args) -{ - int argLength = args->length(); - - if (argLength == 1 || argLength == 2) { - QV4::Scope scope(args->v4engine()); - int index = QV4::ScopedValue(scope, (*args)[0])->toInt32(); - int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - removeElements(index, removeCount); - } else { - qmlWarning(this) << tr("remove: incorrect number of arguments"); - } -} - -void QQmlListModel::removeElements(int index, int removeCount) -{ - Q_ASSERT(index >= 0 && removeCount >= 0); - - if (!removeCount) - return; - - if (m_mainThread) - beginRemoveRows(QModelIndex(), index, index + removeCount - 1); - - QVector<std::function<void()>> toDestroy; - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) { - auto modelObject = m_modelObjects[index+i]; - toDestroy.append([modelObject](){ - delete modelObject; - }); - } - m_modelObjects.remove(index, removeCount); - } else { - toDestroy = m_listModel->remove(index, removeCount); - } - - if (m_mainThread) { - endRemoveRows(); - emit countChanged(); - } - for (const auto &destroyer : toDestroy) - destroyer(); -} - -/*! - \qmlmethod ListModel::insert(int index, jsobject dict) - - Adds a new item to the list model at position \a index, with the - values in \a dict. - - \code - fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQmlListModel::insert(QQmlV4Function *args) -{ - if (args->length() == 2) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue arg0(scope, (*args)[0]); - int index = arg0->toInt32(); - - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - QV4::ScopedObject argObject(scope, (*args)[1]); - QV4::ScopedArrayObject objectArray(scope, (*args)[1]); - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - emitItemsAboutToBeInserted(index, objectArrayLength); - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject); - } - } - emitItemsInserted(); - } else if (argObject) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod ListModel::move(int from, int to, int n) - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - fruitModel.move(0, fruitModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlListModel::move(int from, int to, int n) -{ - if (n == 0 || from == to) - return; - if (!canMove(from, to, n)) { - qmlWarning(this) << tr("move: out of range"); - return; - } - - if (m_mainThread) - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector<DynamicRoleModelNode *, 4> store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - if (m_mainThread) - endMoveRows(); -} - -/*! - \qmlmethod ListModel::append(jsobject dict) - - Adds a new item to the end of the list model, with the - values in \a dict. - - \code - fruitModel.append({"cost": 5.95, "name":"Pizza"}) - \endcode - - \sa set(), remove() -*/ -void QQmlListModel::append(QQmlV4Function *args) -{ - if (args->length() == 1) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedObject argObject(scope, (*args)[0]); - QV4::ScopedArrayObject objectArray(scope, (*args)[0]); - - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - if (objectArrayLength > 0) { - int index = count(); - emitItemsAboutToBeInserted(index, objectArrayLength); - - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject); - } - } - - emitItemsInserted(); - } - } else if (argObject) { - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - emitItemsAboutToBeInserted(index, 1); - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->elementCount(); - emitItemsAboutToBeInserted(index, 1); - m_listModel->append(argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("append: value is not an object"); - } - } else { - qmlWarning(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QQmlV4Handle QQmlListModel::get(int index) const -{ - QV4::Scope scope(engine()); - QV4::ScopedValue result(scope, QV4::Value::undefinedValue()); - - if (index >= 0 && index < count()) { - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = QV4::QObjectWrapper::wrap(scope.engine, object); - } else { - QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); - QQmlData *ddata = QQmlData::get(object); - if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); - // Keep track of the QObjectWrapper in persistent value storage - ddata->jsWrapper.set(scope.engine, result); - } else { - result = ddata->jsWrapper.value(); - } - } - } - - return QQmlV4Handle(result); -} - -/*! - \qmlmethod ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::set(int index, const QQmlV4Handle &handle) -{ - QV4::Scope scope(engine()); - QV4::ScopedObject object(scope, handle); - - if (!object) { - qmlWarning(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - - if (index == count()) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object); - } - - emitItemsInserted(); - } else { - - QVector<int> roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmlmethod ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQmlListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQmlListModelWorkerAgent) and to let - // us document sync(). - qmlWarning(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) -{ - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); - if (objName != listElementTypeName) { - const QMetaObject *mo = resolveType(objName); - if (mo != &QQmlListElement::staticMetaObject) { - error(target, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = objName; // cache right name for next time - } - - if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) { - error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - const QV4::CompiledData::Binding *binding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (propName.isEmpty()) { - error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (!verifyProperty(compilationUnit, binding)) - return false; - } - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { - QByteArray script = scriptStr.toUtf8(); - bool ok; - evaluateEnum(script, &ok); - if (!ok) { - error(binding, QQmlListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } - } - - return true; -} - -bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) -{ - const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); - - bool roleSet = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - - ListModel *subModel = nullptr; - if (outterElementIndex == -1) { - subModel = model; - } else { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - if (role.type == ListLayout::Role::List) { - subModel = model->getListProperty(outterElementIndex, role); - if (subModel == nullptr) { - subModel = new ListModel(role.subLayout, nullptr); - QVariant vModel = QVariant::fromValue(subModel); - model->setOrCreateProperty(outterElementIndex, elementName, vModel); - } - } - } - - int elementIndex = subModel ? subModel->appendElement() : -1; - - const QV4::CompiledData::Binding *subBinding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex); - } - - } else { - QVariant value; - - if (binding->isTranslationBinding()) { - value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); - } else if (binding->evaluatesToString()) { - value = binding->valueAsString(compilationUnit.data()); - } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - value = binding->valueAsNumber(compilationUnit->constants); - } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { - value = binding->valueAsBoolean(); - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { - value = QVariant::fromValue(nullptr); - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (definesEmptyList(scriptStr)) { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - ListModel *emptyModel = new ListModel(role.subLayout, nullptr); - value = QVariant::fromValue(emptyModel); - } else if (binding->isFunctionExpression()) { - QQmlBinding::Identifier id = binding->value.compiledScriptIndex; - Q_ASSERT(id != QQmlBinding::Invalid); - - auto v4 = compilationUnit->engine; - QV4::Scope scope(v4); - // for now we do not provide a context object; data from the ListElement must be passed to the function - QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr)); - QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id])); - - QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0); - - QJSValue v; - QJSValuePrivate::setValue(&v, v4, result); - value.setValue<QJSValue>(v); - } else { - QByteArray script = scriptStr.toUtf8(); - bool ok; - value = evaluateEnum(script, &ok); - } - } else { - Q_UNREACHABLE(); - } - - model->setOrCreateProperty(outterElementIndex, elementName, value); - roleSet = true; - } - return roleSet; -} - -void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - listElementTypeName = QString(); // unknown - - for (const QV4::CompiledData::Binding *binding : bindings) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (!propName.isEmpty()) { // isn't default property - error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName)); - return; - } - if (!verifyProperty(compilationUnit, binding)) - return; - } -} - -void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - QQmlListModel *rv = static_cast<QQmlListModel *>(obj); - - rv->m_engine = qmlEngine(rv)->handle(); - rv->m_compilationUnit = compilationUnit; - - bool setRoles = false; - - for (const QV4::CompiledData::Binding *binding : bindings) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); - } - - if (setRoles == false) - qmlWarning(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQmlListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i<s.length()-1; i++) { - if (!s[i].isSpace()) - return false; - } - return true; - } - return false; -} - - -/*! - \qmltype ListElement - \instantiates QQmlListElement - \inqmlmodule QtQml.Models - \brief Defines a data item in a ListModel. - \ingroup qtquick-models - - List elements are defined inside ListModel definitions, and represent items in a - list that will be displayed using ListView or \l Repeater items. - - List elements are defined like other QML elements except that they contain - a collection of \e role definitions instead of properties. Using the same - syntax as property definitions, roles both define how the data is accessed - and include the data itself. - - The names used for roles must begin with a lower-case letter and should be - common to all elements in a given model. Values must be simple constants; either - strings (quoted and optionally within a call to QT_TR_NOOP), boolean values - (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). - - Beginning with Qt 5.11 ListElement also allows assigning a function declaration to - a role. This allows the definition of ListElements with callable actions. - - \section1 Referencing Roles - - The role names are used by delegates to obtain data from list elements. - Each role name is accessible in the delegate's scope, and refers to the - corresponding role in the current element. Where a role name would be - ambiguous to use, it can be accessed via the \l{ListView::}{model} - property (e.g., \c{model.cost} instead of \c{cost}). - - \section1 Example Usage - - The following model defines a series of list elements, each of which - contain "name" and "cost" roles and their associated values. - - \snippet qml/listmodel/listelements.qml model - - The delegate obtains the name and cost for each element by simply referring - to \c name and \c cost: - - \snippet qml/listmodel/listelements.qml view - - \sa ListModel -*/ - -QT_END_NAMESPACE - -#include "moc_qqmllistmodel_p.cpp" diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h deleted file mode 100644 index 95b797c898..0000000000 --- a/src/qml/types/qqmllistmodel_p.h +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLLISTMODEL_H -#define QQMLLISTMODEL_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 <qqml.h> -#include <private/qqmlcustomparser_p.h> - -#include <QtCore/QObject> -#include <QtCore/QStringList> -#include <QtCore/QHash> -#include <QtCore/QList> -#include <QtCore/QVariant> -#include <QtCore/qabstractitemmodel.h> - -#include <private/qv4engine_p.h> -#include <private/qpodvector_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModelWorkerAgent; -class ListModel; -class ListLayout; - -namespace QV4 { -struct ModelObject; -} - -class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQmlListModel(QObject *parent=nullptr); - ~QQmlListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - int rowCount(const QModelIndex &parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - QHash<int,QByteArray> roleNames() const override; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQmlListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQmlListModelParser; - friend class QQmlListModelWorkerAgent; - friend class ModelObject; - friend struct QV4::ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - friend struct StringOrTranslation; - - // Constructs a flat list model for a worker agent - QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); - QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent=nullptr); - - QV4::ExecutionEngine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQmlListModelWorkerAgent *m_agent; - mutable QV4::ExecutionEngine *m_engine; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector<class DynamicRoleModelNode *> m_modelObjects; - QVector<QString> m_roles; - - struct ElementSync - { - DynamicRoleModelNode *src = nullptr; - DynamicRoleModelNode *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - static bool sync(QQmlListModel *src, QQmlListModel *target); - static QQmlListModel *createWithOwner(QQmlListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector<int> &roles); - void emitItemsAboutToBeInserted(int index, int count); - void emitItemsInserted(); - - void removeElements(int index, int removeCount); -}; - -// ### FIXME -class QQmlListElement : public QObject -{ -Q_OBJECT -}; - -class QQmlListModelParser : public QQmlCustomParser -{ -public: - enum PropertyType { - Invalid, - Boolean, - Number, - String, - Script - }; - - - QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - - void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - -private: - bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding); - // returns true if a role was set - bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); - - static bool definesEmptyList(const QString &); - - QString listElementTypeName; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlListModel) -QML_DECLARE_TYPE(QQmlListElement) - -#endif // QQMLLISTMODEL_H diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h deleted file mode 100644 index 2876c71de6..0000000000 --- a/src/qml/types/qqmllistmodel_p_p.h +++ /dev/null @@ -1,428 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLLISTMODEL_P_P_H -#define QQMLLISTMODEL_P_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 "qqmllistmodel_p.h" -#include <private/qqmlengine_p.h> -#include <private/qqmlopenmetaobject_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <qqml.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index) override; - void propertyWritten(int index) override; - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQmlListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); - - void updateValues(const QVariantMap &object, QVector<int> &roles); - - QVariant getValue(const QString &name) const - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static QVector<int> sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target); - -private: - QQmlListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex); - ~ModelNodeMetaObject(); - - QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *object) override; - - static ModelNodeMetaObject *get(QObject *obj); - - bool m_enabled; - QQmlListModel *m_model; - int m_elementIndex; - - void updateValues(); - void updateValues(const QVector<int> &roles); - - bool initialized() const { return m_initialized; } - -protected: - void propertyWritten(int index) override; - -private: - using QQmlOpenMetaObject::setValue; - - void emitDirectNotifies(const int *changedRoles, int roleCount); - - void initialize(); - bool m_initialized; -}; - -namespace QV4 { - -namespace Heap { - -struct ModelObject : public QObjectWrapper { - void init(QObject *object, QQmlListModel *model) - { - QObjectWrapper::init(object); - m_model = model; - QObjectPrivate *op = QObjectPrivate::get(object); - m_nodeModelMetaObject = static_cast<ModelNodeMetaObject *>(op->metaObject); - } - void destroy() { QObjectWrapper::destroy(); } - int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; } - QQmlListModel *m_model; - ModelNodeMetaObject *m_nodeModelMetaObject; -}; - -} - -struct ModelObject : public QObjectWrapper -{ - V4_OBJECT2(ModelObject, QObjectWrapper) - V4_NEEDS_DESTROY - - ListModel *listModel() const { return d()->m_model->m_listModel; } - -protected: - static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); - static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); - static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); - static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); - static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); -}; - -} // namespace QV4 - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - Function, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(QV4::String *key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) const { return *roles.at(index); } - const Role *getExistingRole(const QString &key) const; - const Role *getExistingRole(QV4::String *key) const; - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector<Role *> roles; - QStringHash<Role *> roleHash; -}; - -struct StringOrTranslation -{ - explicit StringOrTranslation(const QString &s); - explicit StringOrTranslation(const QV4::CompiledData::Binding *binding); - ~StringOrTranslation(); - bool isSet() const { return d.flag(); } - bool isTranslation() const { return d.isT2(); } - void setString(const QString &s); - void setTranslation(const QV4::CompiledData::Binding *binding); - QString toString(const QQmlListModel *owner) const; - QString asString() const; -private: - void clear(); - QBiPointer<QStringData, const QV4::CompiledData::Binding> d; -}; - -/*! -\internal -*/ -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static QVector<int> sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); - int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - StringOrTranslation *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - QJSValue *getFunctionProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - ModelNodeMetaObject *objectCache(); - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - QObject *m_objectCache; - - friend class ListModel; -}; - -/*! -\internal -*/ -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQmlListModel *modelCache); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) const - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role *getExistingRole(QV4::String *key) const - { - return m_layout->getExistingRole(key); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, QV4::Object *object, QVector<int> *roles); - void set(int elementIndex, QV4::Object *object); - - int append(QV4::Object *object); - void insert(int elementIndex, QV4::Object *object); - - Q_REQUIRED_RESULT QVector<std::function<void()>> remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - static bool sync(ListModel *src, ListModel *target); - - QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); - -private: - QPODVector<ListElement *, 4> elements; - ListLayout *m_layout; - - QQmlListModel *m_modelCache; - - struct ElementSync - { - ListElement *src = nullptr; - ListElement *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - void newElement(int index); - - void updateCacheIndices(int start = 0, int end = -1); - - friend class ListElement; - friend class QQmlListModelWorkerAgent; - friend class QQmlListModelParser; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -#endif // QQUICKLISTMODEL_P_P_H diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp deleted file mode 100644 index fe3eaa3198..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmllistmodelworkeragent_p.h" -#include "qqmllistmodel_p_p.h" -#include <private/qqmldata_p.h> -#include <private/qqmlengine_p.h> -#include <qqmlinfo.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> - - -QT_BEGIN_NAMESPACE - -QQmlListModelWorkerAgent::Sync::~Sync() -{ -} - -QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) -{ -} - -QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQmlListModelWorkerAgent::setEngine(QV4::ExecutionEngine *eng) -{ - m_copy->m_engine = eng; -} - -void QQmlListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQmlListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQmlListModelWorkerAgent::modelDestroyed() -{ - m_orig = nullptr; -} - -int QQmlListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQmlListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQmlListModelWorkerAgent::remove(QQmlV4Function *args) -{ - m_copy->remove(args); -} - -void QQmlListModelWorkerAgent::append(QQmlV4Function *args) -{ - m_copy->append(args); -} - -void QQmlListModelWorkerAgent::insert(QQmlV4Function *args) -{ - m_copy->insert(args); -} - -QQmlV4Handle QQmlListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQmlListModelWorkerAgent::set(int index, const QQmlV4Handle &value) -{ - m_copy->set(index, value); -} - -void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQmlListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQmlListModelWorkerAgent::sync() -{ - Sync *s = new Sync(m_copy); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQmlListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast<Sync *>(e); - - cc = (m_orig->count() != s->list->count()); - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel); - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - -#include "moc_qqmllistmodelworkeragent_p.cpp" diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h deleted file mode 100644 index ae2d4b11e0..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** 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 QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_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 <qqml.h> - -#include <QEvent> -#include <QMutex> -#include <QWaitCondition> - -#include <private/qv8engine_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModel; - -class QQmlListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQmlListModelWorkerAgent(QQmlListModel *); - ~QQmlListModelWorkerAgent(); - void setEngine(QV4::ExecutionEngine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(nullptr) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); - a = o.a; - return *this; - } - - QQmlListModelWorkerAgent *a; - }; - - void modelDestroyed(); -protected: - bool event(QEvent *) override; - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQmlListModel; - - struct Sync : public QEvent { - Sync(QQmlListModel *l) - : QEvent(QEvent::User) - , list(l) - {} - ~Sync(); - QQmlListModel *list; - }; - - QAtomicInt m_ref; - QQmlListModel *m_orig; - QQmlListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp deleted file mode 100644 index bb72771cd9..0000000000 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 "qqmlmodelsmodule_p.h" -#include <QtCore/qitemselectionmodel.h> -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#endif -#if QT_CONFIG(qml_delegate_model) -#include <private/qqmldelegatemodel_p.h> -#include <private/qqmldelegatecomponent_p.h> -#endif -#include <private/qqmlobjectmodel_p.h> - -QT_BEGIN_NAMESPACE - -void QQmlModelsModule::defineModule() -{ - const char uri[] = "QtQml.Models"; - -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); - qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); -#endif -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); -#endif - qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); - qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); - - qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); -} - -void QQmlModelsModule::defineLabsModule() -{ - const char uri[] = "Qt.labs.qmlmodels"; - -#if QT_CONFIG(qml_delegate_model) - qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent.")); - qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser"); - qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice"); -#endif -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp deleted file mode 100644 index 525920c6e0..0000000000 --- a/src/qml/types/qqmlobjectmodel.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmlobjectmodel_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlinfo.h> - -#include <private/qqmlchangeset_p.h> -#include <private/qqmlglobal_p.h> -#include <private/qobject_p.h> -#include <private/qpodvector_p.h> - -#include <QtCore/qhash.h> -#include <QtCore/qlist.h> - -QT_BEGIN_NAMESPACE - -QHash<QObject*, QQmlObjectModelAttached*> QQmlObjectModelAttached::attachedProperties; - - -class QQmlObjectModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlObjectModel) -public: - class Item { - public: - Item(QObject *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QObject *item; - int ref; - }; - - QQmlObjectModelPrivate() : QObjectPrivate(), moveId(0) {} - - static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { - int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); - } - - static int children_count(QQmlListProperty<QObject> *prop) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - } - - static QObject *children_at(QQmlListProperty<QObject> *prop, int index) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty<QObject> *prop) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); - } - - void insert(int index, QObject *item) { - Q_Q(QQmlObjectModel); - children.insert(index, Item(item)); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.insert(index, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void move(int from, int to, int n) { - Q_Q(QQmlObjectModel); - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<QQmlObjectModelPrivate::Item, 4> store; - for (int i = 0; i < to - from; ++i) - store.append(children[from + n + i]); - for (int i = 0; i < n; ++i) - store.append(children[from + i]); - - for (int i = 0; i < store.count(); ++i) { - children[from + i] = store[i]; - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); - attached->setIndex(from + i); - } - - QQmlChangeSet changeSet; - changeSet.move(from, to, n, ++moveId); - emit q->modelUpdated(changeSet, false); - emit q->childrenChanged(); - } - - void remove(int index, int n) { - Q_Q(QQmlObjectModel); - for (int i = index; i < index + n; ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(-1); - } - children.erase(children.begin() + index, children.begin() + index + n); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.remove(index, n); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void clear() { - Q_Q(QQmlObjectModel); - for (const Item &child : qAsConst(children)) - emit q->destroyingItem(child.item); - remove(0, children.count()); - } - - int indexOf(QObject *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - uint moveId; - QList<Item> children; -}; - - -/*! - \qmltype ObjectModel - \instantiates QQmlObjectModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a set of items to be used as a model. - - An ObjectModel contains the visual items to be used in a view. - When an ObjectModel is used in a view, the view does not require - a delegate since the ObjectModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{ObjectModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - import QtQml.Models 2.1 - - Rectangle { - ObjectModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image objectmodel.png - - \sa {Qt Quick Examples - Views} -*/ - -QQmlObjectModel::QQmlObjectModel(QObject *parent) - : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQml.Models::ObjectModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty<QObject> QQmlObjectModel::children() -{ - Q_D(QQmlObjectModel); - return QQmlListProperty<QObject>(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQml.Models::ObjectModel::count - - The number of items in the model. This property is readonly. -*/ -int QQmlObjectModel::count() const -{ - Q_D(const QQmlObjectModel); - return d->children.count(); -} - -bool QQmlObjectModel::isValid() const -{ - return true; -} - -QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode) -{ - Q_D(QQmlObjectModel); - QQmlObjectModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) -{ - Q_D(QQmlObjectModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (!d->children[idx].deref()) - return QQmlInstanceModel::Referenced; - } - return nullptr; -} - -QString QQmlObjectModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -QQmlIncubator::Status QQmlObjectModel::incubationStatus(int) -{ - return QQmlIncubator::Ready; -} - -int QQmlObjectModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlObjectModel); - return d->indexOf(item); -} - -QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) -{ - return QQmlObjectModelAttached::properties(obj); -} - -/*! - \qmlmethod object QtQml.Models::ObjectModel::get(int index) - \since 5.6 - - Returns the item at \a index in the model. This allows the item - to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - objectModel.append(objectComponent.createObject()) - console.log(objectModel.get(0).objectName); - objectModel.get(0).objectName = "first"; - } - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -QObject *QQmlObjectModel::get(int index) const -{ - Q_D(const QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return nullptr; - return d->children.at(index).item; -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::append(object item) - \since 5.6 - - Appends a new \a item to the end of the model. - - \code - objectModel.append(objectComponent.createObject()) - \endcode - - \sa insert(), remove() -*/ -void QQmlObjectModel::append(QObject *object) -{ - Q_D(QQmlObjectModel); - d->insert(count(), object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) - \since 5.6 - - Inserts a new \a item to the model at position \a index. - - \code - objectModel.insert(2, objectComponent.createObject()) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa append(), remove() -*/ -void QQmlObjectModel::insert(int index, QObject *object) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - d->insert(index, object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) - \since 5.6 - - Moves \e n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the model: - - \code - objectModel.move(0, objectModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlObjectModel::move(int from, int to, int n) -{ - Q_D(QQmlObjectModel); - if (n <= 0 || from == to) - return; - if (from < 0 || to < 0 || from + n > count() || to + n > count()) { - qmlWarning(this) << tr("move: out of range"); - return; - } - d->move(from, to, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) - \since 5.6 - - Removes \e n items at \a index from the model. - - \sa clear() -*/ -void QQmlObjectModel::remove(int index, int n) -{ - Q_D(QQmlObjectModel); - if (index < 0 || n <= 0 || index + n > count()) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); - return; - } - d->remove(index, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::clear() - \since 5.6 - - Clears all items from the model. - - \sa append(), remove() -*/ -void QQmlObjectModel::clear() -{ - Q_D(QQmlObjectModel); - d->clear(); -} - -QT_END_NAMESPACE - -#include "moc_qqmlobjectmodel_p.cpp" diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h deleted file mode 100644 index 4ac4f1c65b..0000000000 --- a/src/qml/types/qqmlobjectmodel_p.h +++ /dev/null @@ -1,193 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLINSTANCEMODEL_P_H -#define QQMLINSTANCEMODEL_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/qtqmlglobal_p.h> -#include <private/qqmlincubator_p.h> -#include <QtQml/qqml.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlChangeSet; -class QAbstractItemModel; - -class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQmlInstanceModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0; - virtual ReleaseFlags release(QObject *object) = 0; - virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; - virtual QQmlIncubator::Status incubationStatus(int index) = 0; - - virtual int indexOf(QObject *object, QObject *objectContext) const = 0; - virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; } - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQmlChangeSet &changeSet, bool reset); - void createdItem(int index, QObject *object); - void initItem(int index, QObject *object); - void destroyingItem(QObject *object); - -protected: - QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = nullptr) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQmlInstanceModel) -}; - -class QQmlObjectModelAttached; -class QQmlObjectModelPrivate; -class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlObjectModel) - - Q_PROPERTY(QQmlListProperty<QObject> children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQmlObjectModel(QObject *parent=nullptr); - ~QQmlObjectModel() {} - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - QString stringValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &) override {} - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QQmlListProperty<QObject> children(); - - static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); - - Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; - Q_REVISION(3) Q_INVOKABLE void append(QObject *object); - Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); - Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); - Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); - -public Q_SLOTS: - Q_REVISION(3) void clear(); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQmlObjectModel) -}; - -class QQmlObjectModelAttached : public QObject -{ - Q_OBJECT - -public: - QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(-1) {} - ~QQmlObjectModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - Q_EMIT indexChanged(); - } - } - - static QQmlObjectModelAttached *properties(QObject *obj) { - QQmlObjectModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQmlObjectModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash<QObject*, QQmlObjectModelAttached*> attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlInstanceModel) -QML_DECLARE_TYPE(QQmlObjectModel) -QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp deleted file mode 100644 index 2170e2daec..0000000000 --- a/src/qml/types/qqmltableinstancemodel.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qqmltableinstancemodel_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtCore/QTimer> - -#include <QtQml/private/qqmlincubator_p.h> -#include <QtQml/private/qqmlchangeset_p.h> -#include <QtQml/private/qqmlcomponent_p.h> - -QT_BEGIN_NAMESPACE - -const char* kModelItemTag = "_tableinstancemodel_modelItem"; - -bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) -{ - if (!modelItem->incubationTask) - return true; - - const auto status = modelItem->incubationTask->status(); - return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); -} - -void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) -{ - Q_ASSERT(modelItem); - - delete modelItem->object; - modelItem->object = nullptr; - - if (modelItem->contextData) { - modelItem->contextData->invalidate(); - Q_ASSERT(modelItem->contextData->refCount == 1); - modelItem->contextData = nullptr; - } - - modelItem->deleteLater(); -} - -QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) - : QQmlInstanceModel(*(new QObjectPrivate()), parent) - , m_qmlContext(qmlContext) - , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList())) -{ -} - -void QQmlTableInstanceModel::useImportVersion(int minorVersion) -{ - m_adaptorModel.useImportVersion(minorVersion); -} - -QQmlTableInstanceModel::~QQmlTableInstanceModel() -{ - for (const auto modelItem : m_modelItems) { - // No item in m_modelItems should be referenced at this point. The view - // should release all its items before it deletes this model. Only model items - // that are still being incubated should be left for us to delete. - Q_ASSERT(modelItem->objectRef == 0); - Q_ASSERT(modelItem->incubationTask); - // Check that we are not being deleted while we're - // in the process of e.g emitting a created signal. - Q_ASSERT(modelItem->scriptRef == 0); - - if (modelItem->object) { - delete modelItem->object; - modelItem->object = nullptr; - modelItem->contextData->invalidate(); - modelItem->contextData = nullptr; - } - } - - deleteAllFinishedIncubationTasks(); - qDeleteAll(m_modelItems); - drainReusableItemsPool(0); -} - -QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index) -{ - if (m_delegateChooser) { - const int row = m_adaptorModel.rowAt(index); - const int column = m_adaptorModel.columnAt(index); - QQmlComponent *delegate = nullptr; - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, row, column); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - return delegate; - } - - return m_delegate; -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index) -{ - // Check if an item for the given index is already loaded and ready - QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr); - if (modelItem) - return modelItem; - - QQmlComponent *delegate = resolveDelegate(index); - if (!delegate) - return nullptr; - - // Check if the pool contains an item that can be reused - modelItem = takeFromReusableItemsPool(delegate); - if (modelItem) { - reuseItem(modelItem, index); - m_modelItems.insert(index, modelItem); - return modelItem; - } - - // Create a new item from scratch - modelItem = m_adaptorModel.createItem(m_metaType, index); - if (modelItem) { - modelItem->delegate = delegate; - m_modelItems.insert(index, modelItem); - return modelItem; - } - - qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << index; - return nullptr; -} - -QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_ASSERT(m_delegate); - Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); - Q_ASSERT(m_qmlContext && m_qmlContext->isValid()); - - QQmlDelegateModelItem *modelItem = resolveModelItem(index); - if (!modelItem) - return nullptr; - - if (modelItem->object) { - // The model item has already been incubated. So - // just bump the ref-count and return it. - modelItem->referenceObject(); - return modelItem->object; - } - - // The object is not ready, and needs to be incubated - incubateModelItem(modelItem, incubationMode); - if (!isDoneIncubating(modelItem)) - return nullptr; - - // Incubation is done, so the task should be removed - Q_ASSERT(!modelItem->incubationTask); - - if (!modelItem->object) { - // The object was incubated synchronously (otherwise we would return above). But since - // we have no object, the incubation must have failed. And when we have no object, there - // should be no object references either. And there should also not be any internal script - // refs at this point. So we delete the model item. - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - m_modelItems.remove(modelItem->index); - delete modelItem; - return nullptr; - } - - // Incubation was completed sync and successful - modelItem->referenceObject(); - return modelItem->object; -} - -QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable) -{ - Q_ASSERT(object); - auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); - Q_ASSERT(modelItem); - - if (!modelItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - if (modelItem->isReferenced()) { - // We still have an internal reference to this object, which means that we are told to release an - // object while the createdItem signal for it is still on the stack. This can happen when objects - // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch - // up with. The view might then find that the object is no longer visible and should be released. - // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers - // point of view, it should consider it destroyed. - return QQmlDelegateModel::Destroyed; - } - - // The item is not referenced by anyone - m_modelItems.remove(modelItem->index); - - if (reusable == Reusable) { - insertIntoReusableItemsPool(modelItem); - return QQmlInstanceModel::Referenced; - } - - // The item is not reused or referenced by anyone, so just delete it - modelItem->destroyObject(); - emit destroyingItem(object); - - delete modelItem; - return QQmlInstanceModel::Destroyed; -} - -void QQmlTableInstanceModel::cancel(int index) -{ - auto modelItem = m_modelItems.value(index); - Q_ASSERT(modelItem); - - // Since the view expects the item to be incubating, there should be - // an incubation task. And since the incubation is not done, no-one - // should yet have received, and therfore hold a reference to, the object. - Q_ASSERT(modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - - m_modelItems.remove(index); - - if (modelItem->object) - delete modelItem->object; - - // modelItem->incubationTask will be deleted from the modelItems destructor - delete modelItem; -} - -void QQmlTableInstanceModel::insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem) -{ - // Currently, the only way for a view to reuse items is to call QQmlTableInstanceModel::release() - // with the second argument explicitly set to QQmlTableInstanceModel::Reusable. If the released - // item is no longer referenced, it will be added to the pool. Reusing of items can be specified - // per item, in case certain items cannot be recycled. - // A QQmlDelegateModelItem knows which delegate its object was created from. So when we are - // about to create a new item, we first check if the pool contains an item based on the same - // delegate from before. If so, we take it out of the pool (instead of creating a new item), and - // update all its context-, and attached properties. - // When a view is recycling items, it should call QQmlTableInstanceModel::drainReusableItemsPool() - // regularly. As there is currently no logic to 'hibernate' items in the pool, they are only - // meant to rest there for a short while, ideally only from the time e.g a row is unloaded - // on one side of the view, and until a new row is loaded on the opposite side. In-between - // this time, the application will see the item as fully functional and 'alive' (just not - // visible on screen). Since this time is supposed to be short, we don't take any action to - // notify the application about it, since we don't want to trigger any bindings that can - // disturb performance. - // A recommended time for calling drainReusableItemsPool() is each time a view has finished - // loading e.g a new row or column. If there are more items in the pool after that, it means - // that the view most likely doesn't need them anytime soon. Those items should be destroyed to - // not consume resources. - // Depending on if a view is a list or a table, it can sometimes be performant to keep - // items in the pool for a bit longer than one "row out/row in" cycle. E.g for a table, if the - // number of visible rows in a view is much larger than the number of visible columns. - // In that case, if you flick out a row, and then flick in a column, you would throw away a lot - // of items in the pool if completely draining it. The reason is that unloading a row places more - // items in the pool than what ends up being recycled when loading a new column. And then, when you - // next flick in a new row, you would need to load all those drained items again from scratch. For - // that reason, you can specify a maxPoolTime to the drainReusableItemsPool() that allows you to keep - // items in the pool for a bit longer, effectively keeping more items in circulation. - // A recommended maxPoolTime would be equal to the number of dimenstions in the view, which - // means 1 for a list view and 2 for a table view. If you specify 0, all items will be drained. - Q_ASSERT(!modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - Q_ASSERT(modelItem->object); - - modelItem->poolTime = 0; - m_reusableItemsPool.append(modelItem); - emit itemPooled(modelItem->index, modelItem->object); -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::takeFromReusableItemsPool(const QQmlComponent *delegate) -{ - // Find the oldest item in the pool that was made from the same delegate as - // the given argument, remove it from the pool, and return it. - if (m_reusableItemsPool.isEmpty()) - return nullptr; - - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end(); ++it) { - if ((*it)->delegate != delegate) - continue; - auto modelItem = *it; - m_reusableItemsPool.erase(it); - return modelItem; - } - - return nullptr; -} - -void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime) -{ - // Rather than releasing all pooled items upon a call to this function, each - // item has a poolTime. The poolTime specifies for how many loading cycles an item - // has been resting in the pool. And for each invocation of this function, poolTime - // will increase. If poolTime is equal to, or exceeds, maxPoolTime, it will be removed - // from the pool and released. This way, the view can tweak a bit for how long - // items should stay in "circulation", even if they are not recycled right away. - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end();) { - auto modelItem = *it; - modelItem->poolTime++; - if (modelItem->poolTime <= maxPoolTime) { - ++it; - } else { - it = m_reusableItemsPool.erase(it); - release(modelItem->object, NotReusable); - } - } -} - -void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex) -{ - // Update the context properties index, row and column on - // the delegate item, and inform the application about it. - const int newRow = m_adaptorModel.rowAt(newModelIndex); - const int newColumn = m_adaptorModel.columnAt(newModelIndex); - item->setModelIndex(newModelIndex, newRow, newColumn); - - // Notify the application that all 'dynamic'/role-based context data has - // changed as well (their getter function will use the updated index). - auto const itemAsList = QList<QQmlDelegateModelItem *>() << item; - auto const updateAllRoles = QVector<int>(); - m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles); - - // Inform the view that the item is recycled. This will typically result - // in the view updating its own attached delegate item properties. - emit itemReused(newModelIndex, item->object); -} - -void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode) -{ - // Guard the model item temporarily so that it's not deleted from - // incubatorStatusChanged(), in case the incubation is done synchronously. - modelItem->scriptRef++; - - if (modelItem->incubationTask) { - // We're already incubating the model item from a previous request. If the previous call requested - // the item async, but the current request needs it sync, we need to force-complete the incubation. - const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) - modelItem->incubationTask->forceCompletion(); - } else { - modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); - - QQmlContextData *ctxt = new QQmlContextData; - QQmlContext *creationContext = modelItem->delegate->creationContext(); - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); - ctxt->contextObject = modelItem; - modelItem->contextData = ctxt; - - QQmlComponentPrivate::get(modelItem->delegate)->incubateObject( - modelItem->incubationTask, - modelItem->delegate, - m_qmlContext->engine(), - ctxt, - QQmlContextData::get(m_qmlContext)); - } - - // Remove the temporary guard - modelItem->scriptRef--; -} - -void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; - Q_ASSERT(modelItem->incubationTask); - - modelItem->incubationTask = nullptr; - incubationTask->modelItemToIncubate = nullptr; - - if (status == QQmlIncubator::Ready) { - // Tag the incubated object with the model item for easy retrieval upon release etc. - modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem)); - - // Emit that the item has been created. What normally happens next is that the view - // upon receiving the signal asks for the model item once more. And since the item is - // now in the map, it will be returned directly. - Q_ASSERT(modelItem->object); - modelItem->scriptRef++; - emit createdItem(modelItem->index, modelItem->object); - modelItem->scriptRef--; - } else if (status == QQmlIncubator::Error) { - qWarning() << "Error incubating delegate:" << incubationTask->errors(); - } - - if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { - // We have no internal reference to the model item, and the view has no - // reference to the incubated object. So just delete the model item. - // Note that being here means that the object was incubated _async_ - // (otherwise modelItem->isReferenced() would be true). - m_modelItems.remove(modelItem->index); - - if (modelItem->object) { - modelItem->scriptRef++; - emit destroyingItem(modelItem->object); - modelItem->scriptRef--; - Q_ASSERT(!modelItem->isReferenced()); - } - - deleteModelItemLater(modelItem); - } - - deleteIncubationTaskLater(incubationTask); -} - -QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { - const auto modelItem = m_modelItems.value(index, nullptr); - if (!modelItem) - return QQmlIncubator::Null; - - if (modelItem->incubationTask) - return modelItem->incubationTask->status(); - - // Since we clear the incubation task when we're done - // incubating, it means that the status is Ready. - return QQmlIncubator::Ready; -} - -void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) -{ - // We often need to post-delete incubation tasks, since we cannot - // delete them while we're in the middle of an incubation change callback. - Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); - m_finishedIncubationTasks.append(incubationTask); - if (m_finishedIncubationTasks.count() == 1) - QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); -} - -void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() -{ - qDeleteAll(m_finishedIncubationTasks); - m_finishedIncubationTasks.clear(); -} - -QVariant QQmlTableInstanceModel::model() const -{ - return m_adaptorModel.model(); -} - -void QQmlTableInstanceModel::setModel(const QVariant &model) -{ - // Pooled items are still accessible/alive for the application, and - // needs to stay in sync with the model. So we need to drain the pool - // completely when the model changes. - drainReusableItemsPool(0); - if (auto const aim = abstractItemModel()) - disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); - m_adaptorModel.setModel(model, this, m_qmlContext->engine()); - if (auto const aim = abstractItemModel()) - connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); -} - -void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - // This function is called when model data has changed. In that case, we tell the adaptor model - // to go through all the items we have created, find the ones that are affected, and notify that - // their model data has changed. This will in turn update QML bindings inside the delegate items. - int numberOfRowsChanged = end.row() - begin.row() + 1; - int numberOfColumnsChanged = end.column() - begin.column() + 1; - - for (int column = 0; column < numberOfColumnsChanged; ++column) { - const int columnIndex = begin.column() + column; - const int rowIndex = begin.row() + (columnIndex * rows()); - m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles); - } -} - -QQmlComponent *QQmlTableInstanceModel::delegate() const -{ - return m_delegate; -} - -void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - - m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - m_delegateChooser = adc; - } - - m_delegate = delegate; -} - -const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const -{ - return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr; -} - -// -------------------------------------------------------- - -void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) -{ - modelItemToIncubate->object = object; - emit tableInstanceModel->initItem(modelItemToIncubate->index, object); -} - -void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) -{ - if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate)) - return; - - // We require the view to cancel any ongoing load - // requests before the tableInstanceModel is destructed. - Q_ASSERT(tableInstanceModel); - - tableInstanceModel->incubatorStatusChanged(this, status); -} - -#include "moc_qqmltableinstancemodel_p.cpp" - -QT_END_NAMESPACE - diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h deleted file mode 100644 index 3dd5c4e4ce..0000000000 --- a/src/qml/types/qqmltableinstancemodel_p.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 QQMLTABLEINSTANCEMODEL_P_H -#define QQMLTABLEINSTANCEMODEL_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/private/qqmldelegatemodel_p.h> -#include <QtQml/private/qqmldelegatemodel_p_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlTableInstanceModel; -class QQmlAbstractDelegateComponent; - -class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask -{ -public: - QQmlTableInstanceModelIncubationTask( - QQmlTableInstanceModel *tableInstanceModel - , QQmlDelegateModelItem* modelItemToIncubate - , IncubationMode mode) - : QQDMIncubationTask(nullptr, mode) - , modelItemToIncubate(modelItemToIncubate) - , tableInstanceModel(tableInstanceModel) { - clear(); - } - - void statusChanged(Status status) override; - void setInitialState(QObject *object) override; - - QQmlDelegateModelItem *modelItemToIncubate = nullptr; - QQmlTableInstanceModel *tableInstanceModel = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel -{ - Q_OBJECT - -public: - - enum ReusableFlag { - NotReusable, - Reusable - }; - - QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr); - ~QQmlTableInstanceModel() override; - - void useImportVersion(int minorVersion); - - int count() const override { return m_adaptorModel.count(); } - int rows() const { return m_adaptorModel.rowCount(); } - int columns() const { return m_adaptorModel.columnCount(); } - - bool isValid() const override { return true; } - - QVariant model() const; - void setModel(const QVariant &model); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - const QAbstractItemModel *abstractItemModel() const override; - - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override { return release(object, NotReusable); } - ReleaseFlags release(QObject *object, ReusableFlag reusable); - void cancel(int) override; - - void insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem); - QQmlDelegateModelItem *takeFromReusableItemsPool(const QQmlComponent *delegate); - void drainReusableItemsPool(int maxPoolTime); - int poolSize() { return m_reusableItemsPool.size(); } - void reuseItem(QQmlDelegateModelItem *item, int newModelIndex); - - QQmlIncubator::Status incubationStatus(int index) override; - - QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); } - void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } - int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } - -Q_SIGNALS: - void itemPooled(int index, QObject *object); - void itemReused(int index, QObject *object); - -private: - QQmlComponent *resolveDelegate(int index); - - QQmlAdaptorModel m_adaptorModel; - QQmlAbstractDelegateComponent *m_delegateChooser = nullptr; - QQmlComponent *m_delegate = nullptr; - QPointer<QQmlContext> m_qmlContext; - QQmlDelegateModelItemMetaType *m_metaType; - - QHash<int, QQmlDelegateModelItem *> m_modelItems; - QList<QQmlDelegateModelItem *> m_reusableItemsPool; - QList<QQmlIncubator *> m_finishedIncubationTasks; - - void incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode); - void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status); - void deleteIncubationTaskLater(QQmlIncubator *incubationTask); - void deleteAllFinishedIncubationTasks(); - QQmlDelegateModelItem *resolveModelItem(int index); - - void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles); - - static bool isDoneIncubating(QQmlDelegateModelItem *modelItem); - static void deleteModelItemLater(QQmlDelegateModelItem *modelItem); - - friend class QQmlTableInstanceModelIncubationTask; -}; - -QT_END_NAMESPACE - -#endif // QQMLTABLEINSTANCEMODEL_P_H diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp deleted file mode 100644 index 03539d8737..0000000000 --- a/src/qml/types/qquickpackage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************************** -** -** 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 "qquickpackage_p.h" - -#include <private/qobject_p.h> -#include <private/qqmlguard_p.h> - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick - \ingroup qtquick-views - \brief Specifies a collection of named items. - - The Package type is used in conjunction with - DelegateModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{DelegateModel::parts} property to select - a model which provides the chosen delegate. - - \snippet package/view.qml 0 - - \sa {Qt Quick Examples - Views}, {Qt Quick Demo - Photo Viewer}, {Qt QML} -*/ - -/*! - \qmlattachedproperty string QtQuick::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard<QObject> - { - DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; } - QList<DataGuard> *list; - void objectDestroyed(QObject *) override { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList<DataGuard> dataList; - static void data_append(QQmlListProperty<QObject> *prop, QObject *o) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty<QObject> *prop, int index) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->count(); - } -}; - -QHash<QObject *, QQuickPackageAttached *> QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty<QObject> QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty<QObject>(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return nullptr; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE - -#include "moc_qquickpackage_p.cpp" diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp deleted file mode 100644 index edb112276c..0000000000 --- a/src/qml/types/qquickworkerscript.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/**************************************************************************** -** -** 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 "qtqmlglobal_p.h" -#include "qquickworkerscript_p.h" -#if QT_CONFIG(qml_list_model) -#include "qqmllistmodel_p.h" -#include "qqmllistmodelworkeragent_p.h" -#endif -#include <private/qqmlengine_p.h> -#include <private/qqmlexpression_p.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtQml/qjsengine.h> -#include <QtCore/qmutex.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> -#include <QtQml/qqmlinfo.h> -#include <QtQml/qqmlfile.h> -#if QT_CONFIG(qml_network) -#include <QtNetwork/qnetworkaccessmanager.h> -#include "qqmlnetworkaccessmanagerfactory.h" -#endif - -#include <private/qv8engine_p.h> -#include <private/qv4serialize_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4script_p.h> -#include <private/qv4scopedvalue_p.h> -#include <private/qv4jscall_p.h> - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray m_data; -}; - -class WorkerLoadEvent : public QEvent -{ -public: - enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; - - WorkerLoadEvent(int workerId, const QUrl &url); - - int workerId() const; - QUrl url() const; - -private: - int m_id; - QUrl m_url; -}; - -class WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript : public QV8Engine { - WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent); - ~WorkerScript() override; - -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() override; -#endif - - QQuickWorkerScriptEnginePrivate *p = nullptr; - QUrl source; - QQuickWorkerScript *owner = nullptr; -#if QT_CONFIG(qml_network) - QScopedPointer<QNetworkAccessManager> accessManager; -#endif - int id = -1; - }; - - QHash<int, WorkerScript *> workers; - QV4::ReturnedValue getWorker(WorkerScript *); - - int m_nextId; - - static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - -signals: - void stopThread(); - -protected: - bool event(QEvent *) override; - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: qmlengine(engine), m_nextId(0) -{ -} - -QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b, - const QV4::Value *, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine); - - QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue()); - QByteArray data = QV4::Serialize::serialize(v, scope.engine); - - QMutexLocker locker(&script->p->m_lock); - if (script && script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return QV4::Encode::undefined(); -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - QMutexLocker locker(&m_lock); - WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event); - QHash<int, WorkerScript *>::iterator itr = workers.find(workerEvent->workerId()); - if (itr != workers.end()) { - delete itr.value(); - workers.erase(itr); - } - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - QV4::Scope scope(v4); - QV4::ScopedString v(scope); - QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript"))))); - QV4::ScopedFunctionObject onmessage(scope); - if (worker) - onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage")))); - - if (!onmessage) - return; - - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); - - QV4::JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = v4->global(); - jsCallData->args[0] = value; - onmessage->call(jsCallData); - if (scope.hasException()) { - QQmlError error = scope.engine->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - - script->source = url; - - if (fileName.endsWith(QLatin1String(".mjs"))) { - auto moduleUnit = v4->loadModule(url); - if (moduleUnit) { - if (moduleUnit->instantiate(v4)) - moduleUnit->evaluate(); - } else { - v4->throwError(QStringLiteral("Could not load module file")); - } - } else { - QString error; - QV4::Scope scope(v4); - QScopedPointer<QV4::Script> program; - program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error)); - if (program.isNull()) { - if (!error.isEmpty()) - qWarning().nospace() << error; - return; - } - - if (!v4->hasException) - program->run(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QMutexLocker locker(&script->p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) -: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) -{ -} - -int WorkerLoadEvent::workerId() const -{ - return m_id; -} - -QUrl WorkerLoadEvent::url() const -{ - return m_url; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(new QV4::ExecutionEngine) - , p(parent) - , id(id) -{ - m_v4Engine->v8Engine = this; - - initQmlGlobalObject(); - - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject api(scope, scope.engine->newObject()); - QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); - api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage); - m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - delete m_v4Engine; -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p)); - } else { - accessManager.reset(new QNetworkAccessManager(p)); - } - } - return accessManager.data(); -} -#endif - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript(d->m_nextId++, d); - - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = nullptr; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQml - \brief Enables the use of threads in a Qt Quick application. - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \c onMessage() handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.mjs", that handles - the operations to be performed in the new thread. Here is \c script.mjs: - - \quotefile qml/workerscript/script.mjs - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.mjs. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - The example uses a script that is an ECMAScript module, because it has the ".mjs" extension. - It can use import statements to access functionality from other modules and it is run in JavaScript - strict mode. - - If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript - statements and it is run in non-strict mode. - - \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect - isolation and thread-safety. If the impact of that results in a memory consumption that is too - high for your environment, then consider sharing a WorkerScript element. - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax. - Scripts that are ECMAScript modules can freely use import and export statements. - - \sa {Qt Quick Examples - Threading}, - {Threaded ListModel Example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. - - If the file name component of the url ends with ".mjs", then the script - is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be - plain script. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV4Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue argument(scope, QV4::Value::undefinedValue()); - if (args->length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine)); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return nullptr; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return nullptr; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::message(jsobject msg) - - This signal is emitted when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). - - The corresponding handler is \c onMessage. -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - QQmlEngine *engine = qmlEngine(this); - if (engine) { - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - QV4::Scope scope(engine->handle()); - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine)); - emit message(QQmlV4Handle(value)); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include <qquickworkerscript.moc> - -#include "moc_qquickworkerscript_p.cpp" diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index e74c89b1f1..54cd8710b6 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,51 +1,16 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ - $$PWD/qqmlconnections.cpp \ - $$PWD/qqmlmodelsmodule.cpp \ - $$PWD/qqmlmodelindexvaluetype.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qquickpackage.cpp \ - $$PWD/qqmlinstantiator.cpp \ - $$PWD/qqmltableinstancemodel.cpp + $$PWD/qqmlconnections.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ - $$PWD/qqmlconnections_p.h \ - $$PWD/qqmlmodelsmodule_p.h \ - $$PWD/qqmlmodelindexvaluetype_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qquickpackage_p.h \ - $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h \ - $$PWD/qqmltableinstancemodel_p.h + $$PWD/qqmlconnections_p.h -qtConfig(qml-worker-script) { +qtConfig(qml-itemmodel) { SOURCES += \ - $$PWD/qquickworkerscript.cpp + $$PWD/qqmlmodelindexvaluetype.cpp HEADERS += \ - $$PWD/qquickworkerscript_p.h -} - -qtConfig(qml-list-model) { - SOURCES += \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp - - HEADERS += \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h -} - -qtConfig(qml-delegate-model) { - SOURCES += \ - $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmldelegatecomponent.cpp - - HEADERS += \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmldelegatecomponent_p.h + $$PWD/qqmlmodelindexvaluetype_p.h } qtConfig(qml-animation) { diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp deleted file mode 100644 index 1ff866eb74..0000000000 --- a/src/qml/util/qqmladaptormodel.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qqmladaptormodel_p.h" - -#include <private/qqmldelegatemodel_p_p.h> -#include <private/qmetaobjectbuilder_p.h> -#include <private/qqmlproperty_p.h> -#include <private/qv8engine_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlAdaptorModelEngineData : public QV8Engine::Deletable -{ -public: - QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4); - ~QQmlAdaptorModelEngineData(); - - QV4::ExecutionEngine *v4; - QV4::PersistentValue listItemProto; -}; - -V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) - -static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(f); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); - - RETURN_RESULT(QV4::Encode(o->d()->item->index)); -} - -template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) -{ - builder->setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder->setClassName(T::staticMetaObject.className()); - builder->setSuperClass(&T::staticMetaObject); - metaType->propertyOffset = T::staticMetaObject.propertyCount(); - metaType->signalOffset = T::staticMetaObject.methodCount(); -} - -static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType) -{ - builder->addSignal("__" + QByteArray::number(propertyId) + "()"); - QMetaPropertyBuilder property = builder->addProperty( - propertyName, propertyType, propertyId); - property.setWritable(true); -} - -class VDMModelDelegateDataType; - -class QQmlDMCachedModelData : public QQmlDelegateModelItem -{ -public: - QQmlDMCachedModelData( - QQmlDelegateModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index, int row, int column); - - int metaCall(QMetaObject::Call call, int id, void **arguments); - - virtual QVariant value(int role) const = 0; - virtual void setValue(int role, const QVariant &value) = 0; - - void setValue(const QString &role, const QVariant &value) override; - bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; - - static QV4::ReturnedValue get_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue set_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - - VDMModelDelegateDataType *type; - QVector<QVariant> cachedData; -}; - -class VDMModelDelegateDataType - : public QQmlRefCount - , public QQmlAdaptorModel::Accessors - , public QAbstractDynamicMetaObject -{ -public: - VDMModelDelegateDataType(QQmlAdaptorModel *model) - : model(model) - , propertyOffset(0) - , signalOffset(0) - , hasModelData(false) - { - } - - bool notify( - const QQmlAdaptorModel &, - const QList<QQmlDelegateModelItem *> &items, - int index, - int count, - const QVector<int> &roles) const override - { - bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); - if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { - QList<int> roleIds; - for (const QByteArray &r : watchedRoles) { - QHash<QByteArray, int>::const_iterator it = roleNames.find(r); - if (it != roleNames.end()) - roleIds << it.value(); - } - const_cast<VDMModelDelegateDataType *>(this)->watchedRoleIds = roleIds; - } - - QVector<int> signalIndexes; - for (int i = 0; i < roles.count(); ++i) { - const int role = roles.at(i); - if (!changed && watchedRoleIds.contains(role)) - changed = true; - - int propertyId = propertyRoles.indexOf(role); - if (propertyId != -1) - signalIndexes.append(propertyId + signalOffset); - } - if (roles.isEmpty()) { - const int propertyRolesCount = propertyRoles.count(); - signalIndexes.reserve(propertyRolesCount); - for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) - signalIndexes.append(propertyId + signalOffset); - } - - 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) - QMetaObject::activate(item, signalIndexes.at(i), nullptr); - } - } - return changed; - } - - void replaceWatchedRoles( - QQmlAdaptorModel &, - const QList<QByteArray> &oldRoles, - const QList<QByteArray> &newRoles) const override - { - VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this); - - dataType->watchedRoleIds.clear(); - for (const QByteArray &oldRole : oldRoles) - dataType->watchedRoles.removeOne(oldRole); - dataType->watchedRoles += newRoles; - } - - static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) - { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); - - const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model; - if (o->d()->item->index >= 0 && *model) { - const QAbstractItemModel * const aim = model->aim(); - RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); - } else { - RETURN_RESULT(QV4::Encode(false)); - } - } - - - void initializeConstructor(QQmlAdaptorModelEngineData *const data) - { - QV4::ExecutionEngine *v4 = data->v4; - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr); - QV4::ScopedProperty p(scope); - - typedef QHash<QByteArray, int>::const_iterator iterator; - for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { - const int propertyId = propertyRoles.indexOf(it.value()); - const QByteArray &propertyName = it.key(); - - QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); - QV4::ExecutionContext *global = v4->rootContext(); - QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); - QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); - p->setGetter(g); - p->setSetter(s); - proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); - } - prototype.set(v4, proto); - } - - // QAbstractDynamicMetaObject - - void objectDestroyed(QObject *) override - { - release(); - } - - int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override - { - return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments); - } - - QV4::PersistentValue prototype; - QList<int> propertyRoles; - QList<int> watchedRoleIds; - QList<QByteArray> watchedRoles; - QHash<QByteArray, int> roleNames; - QQmlAdaptorModel *model; - int propertyOffset; - int signalOffset; - bool hasModelData; -}; - -QQmlDMCachedModelData::QQmlDMCachedModelData(QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index, int row, int column) - : QQmlDelegateModelItem(metaType, dataType, index, row, column) - , type(dataType) -{ - if (index == -1) - cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.count()); - - QObjectPrivate::get(this)->metaObject = type; - - type->addref(); -} - -int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments) -{ - if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - if (!cachedData.isEmpty()) { - *static_cast<QVariant *>(arguments[0]) = cachedData.at( - type->hasModelData ? 0 : propertyIndex); - } - } else if (*type->model) { - *static_cast<QVariant *>(arguments[0]) = value(type->propertyRoles.at(propertyIndex)); - } - return -1; - } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - const QMetaObject *meta = metaObject(); - if (cachedData.count() > 1) { - cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]); - QMetaObject::activate(this, meta, propertyIndex, nullptr); - } else if (cachedData.count() == 1) { - cachedData[0] = *static_cast<QVariant *>(arguments[0]); - QMetaObject::activate(this, meta, 0, nullptr); - QMetaObject::activate(this, meta, 1, nullptr); - } - } else if (*type->model) { - setValue(type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0])); - } - return -1; - } else { - return qt_metacall(call, id, arguments); - } -} - -void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value) -{ - QHash<QByteArray, int>::iterator it = type->roleNames.find(role.toUtf8()); - if (it != type->roleNames.end()) { - for (int i = 0; i < type->propertyRoles.count(); ++i) { - if (type->propertyRoles.at(i) == *it) { - cachedData[i] = value; - return; - } - } - } -} - -bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx) -{ - if (index == -1) { - Q_ASSERT(idx >= 0); - cachedData.clear(); - setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx)); - const QMetaObject *meta = metaObject(); - const int propertyCount = type->propertyRoles.count(); - for (int i = 0; i < propertyCount; ++i) - QMetaObject::activate(this, meta, i, nullptr); - return true; - } else { - return false; - } -} - -QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; - - QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); - if (o->d()->item->index == -1) { - if (!modelData->cachedData.isEmpty()) { - return scope.engine->fromVariant( - modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId)); - } - } else if (*modelData->type->model) { - return scope.engine->fromVariant( - modelData->value(modelData->type->propertyRoles.at(propertyId))); - } - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!argc) - return scope.engine->throwTypeError(); - - uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; - - if (o->d()->item->index == -1) { - QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); - if (!modelData->cachedData.isEmpty()) { - if (modelData->cachedData.count() > 1) { - modelData->cachedData[propertyId] = scope.engine->toVariant(argv[0], QVariant::Invalid); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr); - } else if (modelData->cachedData.count() == 1) { - modelData->cachedData[0] = scope.engine->toVariant(argv[0], QVariant::Invalid); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, nullptr); - } - } - } - return QV4::Encode::undefined(); -} - -//----------------------------------------------------------------- -// QAbstractItemModel -//----------------------------------------------------------------- - -class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData -{ - Q_OBJECT - Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) - -public: - QQmlDMAbstractItemModelData( - QQmlDelegateModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index, int row, int column) - : QQmlDMCachedModelData(metaType, dataType, index, row, column) - { - } - - bool hasModelChildren() const - { - if (index >= 0 && *type->model) { - const QAbstractItemModel * const model = type->model->aim(); - return model->hasChildren(model->index(row, column, type->model->rootIndex)); - } else { - return false; - } - } - - QVariant value(int role) const override - { - return type->model->aim()->index(row, column, type->model->rootIndex).data(role); - } - - void setValue(int role, const QVariant &value) override - { - type->model->aim()->setData( - type->model->aim()->index(row, column, type->model->rootIndex), value, role); - } - - QV4::ReturnedValue get() override - { - if (type->prototype.isUndefined()) { - QQmlAdaptorModelEngineData * const data = engineData(v4); - type->initializeConstructor(data); - } - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, type->prototype.value()); - QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); - o->setPrototypeOf(proto); - ++scriptRef; - return o.asReturnedValue(); - } -}; - -class VDMAbstractItemModelDataType : public VDMModelDelegateDataType -{ -public: - VDMAbstractItemModelDataType(QQmlAdaptorModel *model) - : VDMModelDelegateDataType(model) - { - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.aim()->rowCount(model.rootIndex); - } - - int columnCount(const QQmlAdaptorModel &model) const override - { - return model.aim()->columnCount(model.rootIndex); - } - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMAbstractItemModelDataType *>(this)->release(); - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); - if (it != roleNames.end()) { - return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it); - } else if (role == QLatin1String("hasModelChildren")) { - return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))); - } else { - return QVariant(); - } - } - - QVariant parentModelIndex(const QQmlAdaptorModel &model) const override - { - return model - ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) - : QVariant(); - } - - QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override - { - return model - ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)) - : QVariant(); - } - - bool canFetchMore(const QQmlAdaptorModel &model) const override - { - return model && model.aim()->canFetchMore(model.rootIndex); - } - - void fetchMore(QQmlAdaptorModel &model) const override - { - if (model) - model.aim()->fetchMore(model.rootIndex); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); - if (!metaObject) - dataType->initializeMetaType(model); - return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column); - } - - void initializeMetaType(QQmlAdaptorModel &model) - { - QMetaObjectBuilder builder; - setModelDataType<QQmlDMAbstractItemModelData>(&builder, this); - - const QByteArray propertyType = QByteArrayLiteral("QVariant"); - const QHash<int, QByteArray> names = model.aim()->roleNames(); - for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) { - const int propertyId = propertyRoles.count(); - propertyRoles.append(it.key()); - roleNames.insert(it.value(), it.key()); - addProperty(&builder, propertyId, it.value(), propertyType); - } - if (propertyRoles.count() == 1) { - hasModelData = true; - const int role = names.begin().key(); - const QByteArray propertyName = QByteArrayLiteral("modelData"); - - propertyRoles.append(role); - roleNames.insert(propertyName, role); - addProperty(&builder, 1, propertyName, propertyType); - } - - metaObject.reset(builder.toMetaObject()); - *static_cast<QMetaObject *>(this) = *metaObject; - propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision)); - } -}; - -//----------------------------------------------------------------- -// QQmlListAccessor -//----------------------------------------------------------------- - -class QQmlDMListAccessorData : public QQmlDelegateModelItem -{ - Q_OBJECT - Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) -public: - QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, - int index, int row, int column, const QVariant &value) - : QQmlDelegateModelItem(metaType, accessor, index, row, column) - , cachedData(value) - { - } - - QVariant modelData() const - { - return cachedData; - } - - void setModelData(const QVariant &data) - { - if (data == cachedData) - return; - - cachedData = data; - emit modelDataChanged(); - } - - static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) - { - QV4::ExecutionEngine *v4 = b->engine(); - const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); - if (!o) - return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData); - } - - static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) - { - QV4::ExecutionEngine *v4 = b->engine(); - const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); - if (!o) - return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!argc) - return v4->throwTypeError(); - - static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(v4->toVariant(argv[0], QVariant::Invalid)); - return QV4::Encode::undefined(); - } - - QV4::ReturnedValue get() override - { - QQmlAdaptorModelEngineData *data = engineData(v4); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); - QV4::ScopedObject p(scope, data->listItemProto.value()); - o->setPrototypeOf(p); - ++scriptRef; - return o.asReturnedValue(); - } - - void setValue(const QString &role, const QVariant &value) override - { - if (role == QLatin1String("modelData")) - cachedData = value; - } - - bool resolveIndex(const QQmlAdaptorModel &model, int idx) override - { - if (index == -1) { - index = idx; - cachedData = model.list.at(idx); - emit modelIndexChanged(); - emit modelDataChanged(); - return true; - } else { - return false; - } - } - - -Q_SIGNALS: - void modelDataChanged(); - -private: - QVariant cachedData; -}; - - -class VDMListDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors -{ -public: - VDMListDelegateDataType() - : QQmlRefCount() - , QQmlAdaptorModel::Accessors() - {} - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMListDelegateDataType *>(this)->release(); - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.list.count(); - } - - int columnCount(const QQmlAdaptorModel &) const override - { - return 1; - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - return role == QLatin1String("modelData") - ? model.list.at(index) - : QVariant(); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this); - if (!propertyCache) { - dataType->propertyCache.adopt(new QQmlPropertyCache( - &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision)); - } - - return new QQmlDMListAccessorData( - metaType, - dataType, - index, row, column, - index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant()); - } - - bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override - { - for (auto modelItem : items) { - const int modelItemIndex = modelItem->index; - if (modelItemIndex < index || modelItemIndex >= index + count) - continue; - - auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem); - QVariant updatedModelData = model.list.at(listModelItem->index); - listModelItem->setModelData(updatedModelData); - } - return true; - } -}; - -//----------------------------------------------------------------- -// QObject -//----------------------------------------------------------------- - -class VDMObjectDelegateDataType; -class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface -{ - Q_OBJECT - Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged) - Q_INTERFACES(QQmlAdaptorModelProxyInterface) -public: - QQmlDMObjectData( - QQmlDelegateModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, int row, int column, - QObject *object); - - void setModelData(QObject *modelData) - { - if (modelData == object) - return; - - object = modelData; - emit modelDataChanged(); - } - - QObject *modelData() const { return object; } - QObject *proxiedObject() override { return object; } - - QPointer<QObject> object; - -Q_SIGNALS: - void modelDataChanged(); -}; - -class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors -{ -public: - int propertyOffset; - int signalOffset; - bool shared; - QMetaObjectBuilder builder; - - VDMObjectDelegateDataType() - : propertyOffset(0) - , signalOffset(0) - , shared(true) - { - } - - VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type) - : QQmlRefCount() - , QQmlAdaptorModel::Accessors() - , propertyOffset(type.propertyOffset) - , signalOffset(type.signalOffset) - , shared(false) - , builder(type.metaObject.data(), QMetaObjectBuilder::Properties - | QMetaObjectBuilder::Signals - | QMetaObjectBuilder::SuperClass - | QMetaObjectBuilder::ClassName) - { - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.list.count(); - } - - int columnCount(const QQmlAdaptorModel &) const override - { - return 1; - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - if (QObject *object = model.list.at(index).value<QObject *>()) - return object->property(role.toUtf8()); - return QVariant(); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this); - if (!metaObject) - dataType->initializeMetaType(model); - return index >= 0 && index < model.list.count() - ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index))) - : nullptr; - } - - void initializeMetaType(QQmlAdaptorModel &model) - { - Q_UNUSED(model); - setModelDataType<QQmlDMObjectData>(&builder, this); - - metaObject.reset(builder.toMetaObject()); - // Note: ATM we cannot create a shared property cache for this class, since each model - // object can have different properties. And to make those properties available to the - // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass - // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache. - // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem - // will always be available to the delegate, regardless of the import version. - } - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMObjectDelegateDataType *>(this)->release(); - } - - bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override - { - for (auto modelItem : items) { - const int modelItemIndex = modelItem->index; - if (modelItemIndex < index || modelItemIndex >= index + count) - continue; - - auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem); - QObject *updatedModelData = qvariant_cast<QObject *>(model.list.at(objectModelItem->index)); - objectModelItem->setModelData(updatedModelData); - } - return true; - } -}; - -class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject -{ -public: - QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type) - : m_data(data) - , m_type(type) - { - QObjectPrivate *op = QObjectPrivate::get(m_data); - *static_cast<QMetaObject *>(this) = *type->metaObject; - op->metaObject = this; - m_type->addref(); - } - - ~QQmlDMObjectDataMetaObject() - { - m_type->release(); - } - - int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override - { - Q_ASSERT(o == m_data); - Q_UNUSED(o); - - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - if (id >= m_type->propertyOffset - && (call == QMetaObject::ReadProperty - || call == QMetaObject::WriteProperty - || call == QMetaObject::ResetProperty)) { - if (m_data->object) - QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments); - return -1; - } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { - QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } - - int createProperty(const char *name, const char *) override - { - if (!m_data->object) - return -1; - const QMetaObject *metaObject = m_data->object->metaObject(); - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - - const int previousPropertyCount = propertyCount() - propertyOffset(); - int propertyIndex = metaObject->indexOfProperty(name); - if (propertyIndex == -1) - return -1; - if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount()) - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - - if (m_type->shared) { - VDMObjectDelegateDataType *type = m_type; - m_type = new VDMObjectDelegateDataType(*m_type); - type->release(); - } - - const int previousMethodCount = methodCount(); - int notifierId = previousMethodCount - methodOffset(); - for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) { - QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset); - QMetaPropertyBuilder propertyBuilder; - if (property.hasNotifySignal()) { - m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); - ++notifierId; - } else { - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); - } - propertyBuilder.setWritable(property.isWritable()); - propertyBuilder.setResettable(property.isResettable()); - propertyBuilder.setConstant(property.isConstant()); - } - - m_type->metaObject.reset(m_type->builder.toMetaObject()); - *static_cast<QMetaObject *>(this) = *m_type->metaObject; - - notifierId = previousMethodCount; - for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) { - QMetaProperty property = metaObject->property(i + objectPropertyOffset); - if (property.hasNotifySignal()) { - QQmlPropertyPrivate::connect( - m_data->object, property.notifySignalIndex(), m_data, notifierId); - ++notifierId; - } - } - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - } - - QQmlDMObjectData *m_data; - VDMObjectDelegateDataType *m_type; -}; - -QQmlDMObjectData::QQmlDMObjectData(QQmlDelegateModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, int row, int column, - QObject *object) - : QQmlDelegateModelItem(metaType, dataType, index, row, column) - , object(object) -{ - new QQmlDMObjectDataMetaObject(this, dataType); -} - -//----------------------------------------------------------------- -// QQmlAdaptorModel -//----------------------------------------------------------------- - -static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors; - -QQmlAdaptorModel::Accessors::~Accessors() -{ -} - -QQmlAdaptorModel::QQmlAdaptorModel() - : accessors(&qt_vdm_null_accessors) -{ -} - -QQmlAdaptorModel::~QQmlAdaptorModel() -{ - accessors->cleanup(*this); -} - -void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine) -{ - accessors->cleanup(*this); - - list.setList(variant, engine); - - if (QObject *object = qvariant_cast<QObject *>(list.list())) { - setObject(object, parent); - if (qobject_cast<QAbstractItemModel *>(object)) - accessors = new VDMAbstractItemModelDataType(this); - else - accessors = new VDMObjectDelegateDataType; - } else if (list.type() == QQmlListAccessor::ListProperty) { - setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent); - accessors = new VDMObjectDelegateDataType; - } else if (list.type() != QQmlListAccessor::Invalid - && list.type() != QQmlListAccessor::Instance) { // Null QObject - setObject(nullptr, parent); - accessors = new VDMListDelegateDataType; - } else { - setObject(nullptr, parent); - accessors = &qt_vdm_null_accessors; - } -} - -void QQmlAdaptorModel::invalidateModel() -{ - accessors->cleanup(*this); - accessors = &qt_vdm_null_accessors; - // Don't clear the model object as we still need the guard to clear the list variant if the - // object is destroyed. -} - -bool QQmlAdaptorModel::isValid() const -{ - return accessors != &qt_vdm_null_accessors; -} - -int QQmlAdaptorModel::count() const -{ - return rowCount() * columnCount(); -} - -int QQmlAdaptorModel::rowCount() const -{ - return qMax(0, accessors->rowCount(*this)); -} - -int QQmlAdaptorModel::columnCount() const -{ - return qMax(0, accessors->columnCount(*this)); -} - -int QQmlAdaptorModel::rowAt(int index) const -{ - int count = rowCount(); - return count <= 0 ? -1 : index % count; -} - -int QQmlAdaptorModel::columnAt(int index) const -{ - int count = rowCount(); - return count <= 0 ? -1 : index / count; -} - -int QQmlAdaptorModel::indexAt(int row, int column) const -{ - return column * rowCount() + row; -} - -void QQmlAdaptorModel::useImportVersion(int minorVersion) -{ - modelItemRevision = minorVersion; -} - -void QQmlAdaptorModel::objectDestroyed(QObject *) -{ - setModel(QVariant(), nullptr, nullptr); -} - -QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4) - : v4(v4) -{ - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("modelData"), - QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData); - listItemProto.set(v4, proto); -} - -QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData() -{ -} - -QT_END_NAMESPACE - -#include <qqmladaptormodel.moc> diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h deleted file mode 100644 index 8c18466ab5..0000000000 --- a/src/qml/util/qqmladaptormodel_p.h +++ /dev/null @@ -1,179 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLADAPTORMODEL_P_H -#define QQMLADAPTORMODEL_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/qabstractitemmodel.h> - -#include "private/qqmllistaccessor_p.h" -#include <private/qqmlglobal_p.h> -#include <private/qqmlguard_p.h> -#include <private/qqmlnullablevalue_p.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -class QQmlEngine; - -class QQmlDelegateModel; -class QQmlDelegateModelItem; -class QQmlDelegateModelItemMetaType; - -class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject> -{ -public: - class Accessors - { - public: - inline Accessors() {} - virtual ~Accessors(); - virtual int rowCount(const QQmlAdaptorModel &) const { return 0; } - virtual int columnCount(const QQmlAdaptorModel &) const { return 0; } - virtual void cleanup(QQmlAdaptorModel &) const {} - - virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const { - return QVariant(); } - - virtual QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &, - QQmlDelegateModelItemMetaType *, - int, int, int) const { return nullptr; } - - virtual bool notify( - const QQmlAdaptorModel &, - const QList<QQmlDelegateModelItem *> &, - int, - int, - const QVector<int> &) const { return false; } - virtual void replaceWatchedRoles( - QQmlAdaptorModel &, - const QList<QByteArray> &, - const QList<QByteArray> &) const {} - virtual QVariant parentModelIndex(const QQmlAdaptorModel &) const { - return QVariant(); } - virtual QVariant modelIndex(const QQmlAdaptorModel &, int) const { - return QVariant(); } - virtual bool canFetchMore(const QQmlAdaptorModel &) const { return false; } - virtual void fetchMore(QQmlAdaptorModel &) const {} - - QScopedPointer<QMetaObject, QScopedPointerPodDeleter> metaObject; - QQmlRefPointer<QQmlPropertyCache> propertyCache; - }; - - const Accessors *accessors; - QPersistentModelIndex rootIndex; - QQmlListAccessor list; - - int modelItemRevision = 0; - - QQmlAdaptorModel(); - ~QQmlAdaptorModel(); - - inline QVariant model() const { return list.list(); } - void setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine); - void invalidateModel(); - - bool isValid() const; - int count() const; - int rowCount() const; - int columnCount() const; - int rowAt(int index) const; - int columnAt(int index) const; - int indexAt(int row, int column) const; - - void useImportVersion(int minorVersion); - - inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); } - inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); } - inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); } - - inline QVariant value(int index, const QString &role) const { - return accessors->value(*this, index, role); } - inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) { - return accessors->createItem(*this, metaType, index, rowAt(index), columnAt(index)); } - inline bool hasProxyObject() const { - return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; } - - inline bool notify( - const QList<QQmlDelegateModelItem *> &items, - int index, - int count, - const QVector<int> &roles) const { - return accessors->notify(*this, items, index, count, roles); } - inline void replaceWatchedRoles( - const QList<QByteArray> &oldRoles, const QList<QByteArray> &newRoles) { - accessors->replaceWatchedRoles(*this, oldRoles, newRoles); } - - inline QVariant modelIndex(int index) const { return accessors->modelIndex(*this, index); } - inline QVariant parentModelIndex() const { return accessors->parentModelIndex(*this); } - inline bool canFetchMore() const { return accessors->canFetchMore(*this); } - inline void fetchMore() { return accessors->fetchMore(*this); } - -protected: - void objectDestroyed(QObject *) override; -}; - -class QQmlAdaptorModelProxyInterface -{ -public: - virtual ~QQmlAdaptorModelProxyInterface() {} - - virtual QObject *proxiedObject() = 0; -}; - -#define QQmlAdaptorModelProxyInterface_iid "org.qt-project.Qt.QQmlAdaptorModelProxyInterface" - -Q_DECLARE_INTERFACE(QQmlAdaptorModelProxyInterface, QQmlAdaptorModelProxyInterface_iid) - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/qqmlchangeset.cpp b/src/qml/util/qqmlchangeset.cpp deleted file mode 100644 index ba876b42e2..0000000000 --- a/src/qml/util/qqmlchangeset.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmlchangeset_p.h" - -QT_BEGIN_NAMESPACE - - -/*! - \class QQmlChangeSet - \brief The QQmlChangeSet class stores an ordered list of notifications about - changes to a linear data set. - \internal - - QQmlChangeSet can be used to record a series of notifications about items in an indexed list - being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that - all notifications of a single type are grouped together and sorted in order of ascending index, - with remove notifications preceding all others, followed by insert notification, and then - change notifications. - - Moves in a change set are represented by a remove notification paired with an insert - notification by way of a shared unique moveId. Re-ordering may result in one or both of the - paired notifications being divided, when this happens the offset member of the notification - will indicate the relative offset of the divided notification from the beginning of the - original. -*/ - -/*! - Constructs an empty change set. -*/ - -QQmlChangeSet::QQmlChangeSet() - : m_difference(0) -{ -} - -/*! - Constructs a copy of a \a changeSet. -*/ - -QQmlChangeSet::QQmlChangeSet(const QQmlChangeSet &changeSet) - : m_removes(changeSet.m_removes) - , m_inserts(changeSet.m_inserts) - , m_changes(changeSet.m_changes) - , m_difference(changeSet.m_difference) -{ -} - -/*! - Destroys a change set. -*/ - -QQmlChangeSet::~QQmlChangeSet() -{ -} - -/*! - Assigns the value of a \a changeSet to another. -*/ - -QQmlChangeSet &QQmlChangeSet::operator =(const QQmlChangeSet &changeSet) -{ - m_removes = changeSet.m_removes; - m_inserts = changeSet.m_inserts; - m_changes = changeSet.m_changes; - m_difference = changeSet.m_difference; - return *this; -} - -/*! - Appends a notification that \a count items were inserted at \a index. -*/ - -void QQmlChangeSet::insert(int index, int count) -{ - insert(QVector<Change>() << Change(index, count)); -} - -/*! - Appends a notification that \a count items were removed at \a index. -*/ - -void QQmlChangeSet::remove(int index, int count) -{ - QVector<Change> removes; - removes.append(Change(index, count)); - remove(&removes, nullptr); -} - -/*! - Appends a notification that \a count items were moved \a from one index \a to another. - - The \a moveId must be unique across the lifetime of the change set and any related - change sets. -*/ - -void QQmlChangeSet::move(int from, int to, int count, int moveId) -{ - QVector<Change> removes; - removes.append(Change(from, count, moveId)); - QVector<Change> inserts; - inserts.append(Change(to, count, moveId)); - remove(&removes, &inserts); - insert(inserts); -} - -/*! - Appends a notification that \a count items were changed at \a index. -*/ - -void QQmlChangeSet::change(int index, int count) -{ - QVector<Change> changes; - changes.append(Change(index, count)); - change(changes); -} - -/*! - Applies the changes in a \a changeSet to another. -*/ - -void QQmlChangeSet::apply(const QQmlChangeSet &changeSet) -{ - QVector<Change> r = changeSet.m_removes; - QVector<Change> i = changeSet.m_inserts; - QVector<Change> c = changeSet.m_changes; - remove(&r, &i); - insert(i); - change(c); -} - -/*! - Applies a list of \a removes to a change set. - - If a remove contains a moveId then any intersecting insert in the set will replace the - corresponding intersection in the optional \a inserts list. -*/ - -void QQmlChangeSet::remove(const QVector<Change> &removes, QVector<Change> *inserts) -{ - QVector<Change> r = removes; - remove(&r, inserts); -} - -void QQmlChangeSet::remove(QVector<Change> *removes, QVector<Change> *inserts) -{ - int removeCount = 0; - int insertCount = 0; - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - QVector<Change>::iterator rit = removes->begin(); - for (; rit != removes->end(); ++rit) { - int index = rit->index + removeCount; - int count = rit->count; - - // Decrement the accumulated remove count from the indexes of any changes prior to the - // current remove. - for (; change != m_changes.end() && change->end() < rit->index; ++change) - change->index -= removeCount; - // Remove any portion of a change notification that intersects the current remove. - for (; change != m_changes.end() && change->index > rit->end(); ++change) { - change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index); - if (change->count == 0) { - change = m_changes.erase(change); - } else if (rit->index < change->index) { - change->index = rit->index; - } - } - - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; insert != m_inserts.end() && insert->end() <= index; ++insert) { - insertCount += insert->count; - insert->index -= removeCount; - } - - rit->index -= insertCount; - - // Remove any portion of a insert notification that intersects the current remove. - while (insert != m_inserts.end() && insert->index < index + count) { - int offset = index - insert->index; - const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index); - - // If part of the remove or insert that precedes the intersection has a moveId create - // a new delta for that portion and subtract the size of that delta from the current - // one. - if (offset < 0 && rit->moveId != -1) { - rit = removes->insert(rit, Change( - rit->index, -offset, rit->moveId, rit->offset)); - ++rit; - rit->count -= -offset; - rit->offset += -offset; - index += -offset; - count -= -offset; - removeCount += -offset; - offset = 0; - } else if (offset > 0 && insert->moveId != -1) { - insert = m_inserts.insert(insert, Change( - insert->index - removeCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - rit->index -= offset; - insertCount += offset; - } - - // If the current remove has a move id, find any inserts with the same move id and - // replace the corresponding sections with the insert removed from the change set. - if (rit->moveId != -1 && difference > 0 && inserts) { - for (QVector<Change>::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) { - if (iit->moveId != rit->moveId - || rit->offset > iit->offset + iit->count - || iit->offset > rit->offset + difference) { - continue; - } - // If the intersecting insert starts before the replacement one create - // a new insert for the portion prior to the replacement insert. - const int overlapOffset = rit->offset - iit->offset; - if (overlapOffset > 0) { - iit = inserts->insert(iit, Change( - iit->index, overlapOffset, iit->moveId, iit->offset)); - ++iit; - iit->index += overlapOffset; - iit->count -= overlapOffset; - iit->offset += overlapOffset; - } - if (iit->offset >= rit->offset - && iit->offset + iit->count <= rit->offset + difference) { - // If the replacement insert completely encapsulates the existing - // one just change the moveId. - iit->moveId = insert->moveId; - iit->offset = insert->offset + qMax(0, -overlapOffset); - } else { - // Create a new insertion before the intersecting one with the number of intersecting - // items and remove that number from that insert. - const int count - = qMin(iit->offset + iit->count, rit->offset + difference) - - qMax(iit->offset, rit->offset); - iit = inserts->insert(iit, Change( - iit->index, - count, - insert->moveId, - insert->offset + qMax(0, -overlapOffset))); - ++iit; - iit->index += count; - iit->count -= count; - iit->offset += count; - } - } - } - - // Subtract the number of intersecting items from the current remove and insert. - insert->count -= difference; - insert->offset += difference; - rit->count -= difference; - rit->offset += difference; - - index += difference; - count -= difference; - removeCount += difference; - - if (insert->count == 0) { - insert = m_inserts.erase(insert); - } else if (rit->count == -offset || rit->count == 0) { - insert->index += difference; - break; - } else { - insert->index -= removeCount - difference; - rit->index -= insert->count; - insertCount += insert->count; - ++insert; - } - } - removeCount += rit->count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index -= removeCount; - - removeCount = 0; - QVector<Change>::iterator remove = m_removes.begin(); - for (rit = removes->begin(); rit != removes->end(); ++rit) { - if (rit->count == 0) - continue; - // Accumulate consecutive removes into a single delta before attempting to apply. - for (QVector<Change>::iterator next = rit + 1; next != removes->end() - && next->index == rit->index - && next->moveId == -1 - && rit->moveId == -1; ++next) { - next->count += rit->count; - rit = next; - } - int index = rit->index + removeCount; - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; remove != m_removes.end() && index > remove->index; ++remove) - remove->index -= removeCount; - while (remove != m_removes.end() && index + rit->count >= remove->index) { - int count = 0; - const int offset = remove->index - index; - QVector<Change>::iterator rend = remove; - for (; rend != m_removes.end() - && rit->moveId == -1 - && rend->moveId == -1 - && index + rit->count >= rend->index; ++rend) { - count += rend->count; - } - if (remove != rend) { - // Accumulate all existing non-move removes that are encapsulated by or immediately - // follow the current remove into it. - int difference = 0; - if (rend == m_removes.end()) { - difference = rit->count; - } else if (rit->index + rit->count < rend->index - removeCount) { - difference = rit->count; - } else if (rend->moveId != -1) { - difference = rend->index - removeCount - rit->index; - index += difference; - } - count += difference; - - rit->count -= difference; - removeCount += difference; - remove->index = rit->index; - remove->count = count; - remove = m_removes.erase(++remove, rend); - } else { - // Insert a remove for the portion of the unmergable current remove prior to the - // point of intersection. - if (offset > 0) { - remove = m_removes.insert(remove, Change( - rit->index, offset, rit->moveId, rit->offset)); - ++remove; - rit->count -= offset; - rit->offset += offset; - removeCount += offset; - index += offset; - } - remove->index = rit->index; - - ++remove; - } - } - - if (rit->count > 0) { - remove = m_removes.insert(remove, *rit); - ++remove; - } - removeCount += rit->count; - } - for (; remove != m_removes.end(); ++remove) - remove->index -= removeCount; - m_difference -= removeCount; -} - -/*! - Applies a list of \a inserts to a change set. -*/ - -void QQmlChangeSet::insert(const QVector<Change> &inserts) -{ - int insertCount = 0; - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - for (QVector<Change>::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) { - if (iit->count == 0) - continue; - int index = iit->index - insertCount; - - Change current = *iit; - // Accumulate consecutive inserts into a single delta before attempting to insert. - for (QVector<Change>::const_iterator next = iit + 1; next != inserts.end() - && next->index == iit->index + iit->count - && next->moveId == -1 - && iit->moveId == -1; ++next) { - current.count += next->count; - iit = next; - } - - // Increment the index of any changes before the current insert by the accumlated insert - // count. - for (; change != m_changes.end() && change->index >= index; ++change) - change->index += insertCount; - // If the current insert index is in the middle of a change split it in two at that - // point and increment the index of the latter half. - if (change != m_changes.end() && change->index < index + iit->count) { - int offset = index - change->index; - change = m_changes.insert(change, Change(change->index + insertCount, offset)); - ++change; - change->index += iit->count + offset; - change->count -= offset; - } - - // Increment the index of any inserts before the current insert by the accumlated insert - // count. - for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert) - insert->index += insertCount; - if (insert == m_inserts.end()) { - insert = m_inserts.insert(insert, current); - ++insert; - } else { - const int offset = index - insert->index; - - if (offset < 0) { - // If the current insert is before an existing insert and not adjacent just insert - // it into the list. - insert = m_inserts.insert(insert, current); - ++insert; - } else if (iit->moveId == -1 && insert->moveId == -1) { - // If neither the current nor existing insert has a moveId add the current insert - // to the existing one. - if (offset < insert->count) { - insert->index -= current.count; - insert->count += current.count; - } else { - insert->index += insertCount; - insert->count += current.count; - ++insert; - } - } else if (offset < insert->count) { - // If either insert has a moveId then split the existing insert and insert the - // current one in the middle. - if (offset > 0) { - insert = m_inserts.insert(insert, Change( - insert->index + insertCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - } - insert = m_inserts.insert(insert, current); - ++insert; - } else { - insert->index += insertCount; - ++insert; - insert = m_inserts.insert(insert, current); - ++insert; - } - } - insertCount += current.count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index += insertCount; - m_difference += insertCount; -} - -/*! - Applies a combined list of \a removes and \a inserts to a change set. This is equivalent - calling \l remove() followed by \l insert() with the same lists. -*/ - -void QQmlChangeSet::move(const QVector<Change> &removes, const QVector<Change> &inserts) -{ - QVector<Change> r = removes; - QVector<Change> i = inserts; - remove(&r, &i); - insert(i); -} - -/*! - Applies a list of \a changes to a change set. -*/ - -void QQmlChangeSet::change(const QVector<Change> &changes) -{ - QVector<Change> c = changes; - change(&c); -} - -void QQmlChangeSet::change(QVector<Change> *changes) -{ - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - for (QVector<Change>::iterator cit = changes->begin(); cit != changes->end(); ++cit) { - for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {} - for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) { - const int offset = insert->index - cit->index; - const int count = cit->count + cit->index - insert->index - insert->count; - if (offset == 0) { - cit->index = insert->index + insert->count; - cit->count = count; - } else { - cit = changes->insert(++cit, Change(insert->index + insert->count, count)); - --cit; - cit->count = offset; - } - } - - for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {} - if (change == m_changes.end() || change->index > cit->index + cit->count) { - if (cit->count > 0) { - change = m_changes.insert(change, *cit); - ++change; - } - } else { - if (cit->index < change->index) { - change->count += change->index - cit->index; - change->index = cit->index; - } - - if (cit->index + cit->count > change->index + change->count) { - change->count = cit->index + cit->count - change->index; - QVector<Change>::iterator cbegin = change; - QVector<Change>::iterator cend = ++cbegin; - for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) { - if (cend->index + cend->count > change->index + change->count) - change->count = cend->index + cend->count - change->index; - } - if (cbegin != cend) { - change = m_changes.erase(cbegin, cend); - --change; - } - } - } - } -} - -/*! - Prints the contents of a change \a set to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQmlChangeSet &set) -{ - debug.nospace() << "QQmlChangeSet("; - const QVector<QQmlChangeSet::Change> &removes = set.removes(); - for (const QQmlChangeSet::Change &remove : removes) - debug << remove; - const QVector<QQmlChangeSet::Change> &inserts = set.inserts(); - for (const QQmlChangeSet::Change &insert : inserts) - debug << insert; - const QVector<QQmlChangeSet::Change> &changes = set.changes(); - for (const QQmlChangeSet::Change &change : changes) - debug << change; - return debug.nospace() << ')'; -} - -/*! - Prints a \a change to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change) -{ - return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space(); -} - -QT_END_NAMESPACE - diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qml/util/qqmlchangeset_p.h deleted file mode 100644 index 8347a3ff19..0000000000 --- a/src/qml/util/qqmlchangeset_p.h +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLCHANGESET_P_H -#define QQMLCHANGESET_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/qdebug.h> -#include <QtCore/qvector.h> -#include <QtQml/private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlChangeSet -{ -public: - struct MoveKey - { - MoveKey() {} - MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {} - int moveId = -1; - int offset = 0; - }; - - // The storrage for Change (below). This struct is trivial, which it has to be in order to store - // it in a QV4::Heap::Base object. The Change struct doesn't add any storage fields, so it is - // safe to cast ChangeData to/from Change. - struct ChangeData - { - int index; - int count; - int moveId; - int offset; - }; - - struct Change: ChangeData - { - Change() { - index = 0; - count = 0; - moveId = -1; - offset = 0; - } - Change(int index, int count, int moveId = -1, int offset = 0) { - this->index = index; - this->count = count; - this->moveId = moveId; - this->offset = offset; - } - - bool isMove() const { return moveId >= 0; } - - MoveKey moveKey(int index) const { - return MoveKey(moveId, index - Change::index + offset); } - - int start() const { return index; } - int end() const { return index + count; } - }; - - QQmlChangeSet(); - QQmlChangeSet(const QQmlChangeSet &changeSet); - ~QQmlChangeSet(); - - QQmlChangeSet &operator =(const QQmlChangeSet &changeSet); - - const QVector<Change> &removes() const { return m_removes; } - const QVector<Change> &inserts() const { return m_inserts; } - const QVector<Change> &changes() const { return m_changes; } - - void insert(int index, int count); - void remove(int index, int count); - void move(int from, int to, int count, int moveId); - void change(int index, int count); - - void insert(const QVector<Change> &inserts); - void remove(const QVector<Change> &removes, QVector<Change> *inserts = nullptr); - void move(const QVector<Change> &removes, const QVector<Change> &inserts); - void change(const QVector<Change> &changes); - void apply(const QQmlChangeSet &changeSet); - - bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.isEmpty(); } - - void clear() - { - m_removes.clear(); - m_inserts.clear(); - m_changes.clear(); - m_difference = 0; - } - - int difference() const { return m_difference; } - -private: - void remove(QVector<Change> *removes, QVector<Change> *inserts); - void change(QVector<Change> *changes); - - QVector<Change> m_removes; - QVector<Change> m_inserts; - QVector<Change> m_changes; - int m_difference; -}; - -Q_DECLARE_TYPEINFO(QQmlChangeSet::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlChangeSet::MoveKey, Q_PRIMITIVE_TYPE); - -inline uint qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } -inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) { - return l.moveId == r.moveId && l.offset == r.offset; } - -Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change); -Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qml/util/qqmllistaccessor.cpp deleted file mode 100644 index 46a11e2bc2..0000000000 --- a/src/qml/util/qqmllistaccessor.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmllistaccessor_p.h" - -#include <private/qqmlmetatype_p.h> - -#include <QtCore/qstringlist.h> -#include <QtCore/qdebug.h> - -// ### Remove me -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -QQmlListAccessor::QQmlListAccessor() -: m_type(Invalid) -{ -} - -QQmlListAccessor::~QQmlListAccessor() -{ -} - -QVariant QQmlListAccessor::list() const -{ - return d; -} - -void QQmlListAccessor::setList(const QVariant &v, QQmlEngine *engine) -{ - d = v; - - // An incoming JS array as model is treated as a variant list, so we need to - // convert it first with toVariant(). - if (d.userType() == qMetaTypeId<QJSValue>()) - d = d.value<QJSValue>().toVariant(); - - QQmlEnginePrivate *enginePrivate = engine?QQmlEnginePrivate::get(engine):nullptr; - - if (!d.isValid()) { - m_type = Invalid; - } else if (d.userType() == QVariant::StringList) { - m_type = StringList; - } else if (d.userType() == QMetaType::QVariantList) { - m_type = VariantList; - } else if (d.canConvert(QVariant::Int)) { - // Here we have to check for an upper limit, because down the line code might (well, will) - // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX: - // QVector<QPointer<QQuickItem>> something; - // something.resize(count()); - // (See e.g. QQuickRepeater::regenerate()) - // This will allocate data along the lines of: - // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize - // So, doing an approximate round-down-to-nice-number, we get: - const int upperLimit = 100 * 1000 * 1000; - - int i = v.toInt(); - if (i < 0) { - qWarning("Model size of %d is less than 0", i); - m_type = Invalid; - } else if (i > upperLimit) { - qWarning("Model size of %d is bigger than the upper limit %d", i, upperLimit); - m_type = Invalid; - } else { - m_type = Integer; - } - } else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) || - (enginePrivate && enginePrivate->isQObject(d.userType()))) { - QObject *data = enginePrivate?enginePrivate->toQObject(d):QQmlMetaType::toQObject(d); - d = QVariant::fromValue(data); - m_type = Instance; - } else if (d.userType() == qMetaTypeId<QQmlListReference>()) { - m_type = ListProperty; - } else { - m_type = Instance; - } -} - -int QQmlListAccessor::count() const -{ - switch(m_type) { - case StringList: - return qvariant_cast<QStringList>(d).count(); - case VariantList: - return qvariant_cast<QVariantList>(d).count(); - case ListProperty: - return ((const QQmlListReference *)d.constData())->count(); - case Instance: - return 1; - case Integer: - return d.toInt(); - default: - case Invalid: - return 0; - } -} - -QVariant QQmlListAccessor::at(int idx) const -{ - Q_ASSERT(idx >= 0 && idx < count()); - switch(m_type) { - case StringList: - return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); - case VariantList: - return qvariant_cast<QVariantList>(d).at(idx); - case ListProperty: - return QVariant::fromValue(((const QQmlListReference *)d.constData())->at(idx)); - case Instance: - return d; - case Integer: - return QVariant(idx); - default: - case Invalid: - return QVariant(); - } -} - -bool QQmlListAccessor::isValid() const -{ - return m_type != Invalid; -} - -QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qml/util/qqmllistcompositor.cpp deleted file mode 100644 index 921e86f355..0000000000 --- a/src/qml/util/qqmllistcompositor.cpp +++ /dev/null @@ -1,1482 +0,0 @@ -/**************************************************************************** -** -** 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 "qqmllistcompositor_p.h" - -#include <QtCore/qvarlengtharray.h> - -//#define QT_QML_VERIFY_MINIMAL -//#define QT_QML_VERIFY_INTEGRITY - -QT_BEGIN_NAMESPACE - -/*! - \class QQmlListCompositor - \brief The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list - indexes. - \internal - - QQmlListCompositor is intended as an aid for developing proxy models. It doesn't however - directly proxy a list or model, instead a range of indexes from one or many lists can be - inserted into the compositor and then categorized and shuffled around and it will manage the - task of translating from an index in the combined space into an index in a particular list. - - Within a compositor indexes are categorized into groups where a group is a sub-set of the - total indexes referenced by the compositor, each with an address space ranging from 0 to - the number of indexes in the group - 1. Group memberships are independent of each other with - the one exception that items always retain the same order so if an index is moved within a - group, its position in other groups will change as well. - - The iterator classes encapsulate information about a specific position in a compositor group. - This includes a source list, the index of an item within that list and the groups that item - is a member of. The iterator for a specific position in a group can be retrieved with the - find() function and the addition and subtraction operators of the iterators can be used to - navigate to adjacent items in the same group. - - Items can be added to the compositor with the append() and insert() functions, group - membership can be changed with the setFlags() and clearFlags() functions, and the position - of items in the compositor can be changed with the move() function. Each of these functions - optionally returns a list of the changes made to indexes within each group which can then - be propagated to view so that it can correctly refresh its contents; e.g. 3 items - removed at index 6, and 5 items inserted at index 1. The notification changes are always - ordered from the start of the list to the end and accumulate, so if 5 items are removed at - index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed - at index 4, followed by 3 items removed at index 4. - - When the contents of a source list change, the mappings within the compositor can be updated - with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged() - functions. Like the direct manipulation functions these too return a list of group indexes - affected by the change. If items are removed from a source list they are also removed from - any groups they belong to with the one exception being items belonging to the \l Cache group. - When items belonging to this group are removed the list, index, and other group membership - information are discarded but Cache membership is retained until explicitly removed. This - allows the cache index to be retained until cached resources for that item are actually - released. - - Internally the index mapping is stored as a list of Range objects, each has a list identifier, - a start index, a count, and a set of flags which represent group membership and some other - properties. The group index of a range is the sum of all preceding ranges that are members of - that group. To avoid the inefficiency of iterating over potentially all ranges when looking - for a specific index, each time a lookup is done the range and its indexes are cached and the - next lookup is done relative to this. This works out to near constant time in most relevant - use cases because successive index lookups are most frequently adjacent. The total number of - ranges is often quite small, which helps as well. If there is a need for faster random access - then a skip list like index may be an appropriate addition. - - \sa DelegateModel -*/ - -#ifdef QT_QML_VERIFY_MINIMAL -#define QT_QML_VERIFY_INTEGRITY -/* - Diagnostic to verify there are no consecutive ranges, or that the compositor contains the - most compact representation possible. - - Returns false and prints a warning if any range has a starting index equal to the end - (index + count) index of the previous range, and both ranges also have the same flags and list - property. - - If there are no consecutive ranges this will return true. -*/ - -static bool qt_verifyMinimal( - const QQmlListCompositor::iterator &begin, - const QQmlListCompositor::iterator &end) -{ - bool minimal = true; - int index = 0; - - for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) { - if (range->previous->list == range->list - && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag) - && range->previous->end() == range->index) { - qWarning() << index << "Consecutive ranges"; - qWarning() << *range->previous; - qWarning() << *range; - minimal = false; - } - } - - return minimal; -} - -#endif - -#ifdef QT_QML_VERIFY_INTEGRITY -static bool qt_printInfo(const QQmlListCompositor &compositor) -{ - qWarning() << compositor; - return true; -} - -/* - Diagnostic to verify the integrity of a compositor. - - Per range this verifies there are no invalid range combinations, that non-append ranges have - positive non-zero counts, and that list ranges have non-negative indexes. - - Accumulatively this verifies that the cached total group counts match the sum of counts - of member ranges. -*/ - -static bool qt_verifyIntegrity( - const QQmlListCompositor::iterator &begin, - const QQmlListCompositor::iterator &end, - const QQmlListCompositor::iterator &cachedIt) -{ - bool valid = true; - - int index = 0; - QQmlListCompositor::iterator it; - for (it = begin; *it != *end; *it = it->next) { - if (it->count == 0 && !it->append()) { - qWarning() << index << "Empty non-append range"; - valid = false; - } - if (it->count < 0) { - qWarning() << index << "Negative count"; - valid = false; - } - if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) { - qWarning() << index <<"Negative index"; - valid = false; - } - if (it->previous->next != it.range) { - qWarning() << index << "broken list: it->previous->next != it.range"; - valid = false; - } - if (it->next->previous != it.range) { - qWarning() << index << "broken list: it->next->previous != it.range"; - valid = false; - } - if (*it == *cachedIt) { - for (int i = 0; i < end.groupCount; ++i) { - int groupIndex = it.index[i]; - if (cachedIt->flags & (1 << i)) - groupIndex += cachedIt.offset; - if (groupIndex != cachedIt.index[i]) { - qWarning() << index - << "invalid cached index" - << QQmlListCompositor::Group(i) - << "Expected:" - << groupIndex - << "Actual" - << cachedIt.index[i] - << cachedIt; - valid = false; - } - } - } - it.incrementIndexes(it->count); - ++index; - } - - for (int i = 0; i < end.groupCount; ++i) { - if (end.index[i] != it.index[i]) { - qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i]; - valid = false; - } - } - return valid; -} -#endif - -#if defined(QT_QML_VERIFY_MINIMAL) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \ - && qt_printInfo(*this))); -#elif defined(QT_QML_VERIFY_INTEGRITY) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_printInfo(*this))); -#else -# define QT_QML_VERIFY_LISTCOMPOSITOR -#endif - -//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args; -#define QT_QML_TRACE_LISTCOMPOSITOR(args) - -QQmlListCompositor::iterator &QQmlListCompositor::iterator::operator +=(int difference) -{ - // Update all indexes to the start of the range. - decrementIndexes(offset); - - // If the iterator group isn't a member of the current range ignore the current offset. - if (!(range->flags & groupFlag)) - offset = 0; - - offset += difference; - - // Iterate backwards looking for a range with a positive offset. - while (offset <= 0 && range->previous->flags) { - range = range->previous; - if (range->flags & groupFlag) - offset += range->count; - decrementIndexes(range->count); - } - - // Iterate forwards looking for the first range which contains both the offset and the - // iterator group. - while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) { - if (range->flags & groupFlag) - offset -= range->count; - incrementIndexes(range->count); - range = range->next; - } - - // Update all the indexes to inclue the remaining offset. - incrementIndexes(offset); - - return *this; -} - -QQmlListCompositor::insert_iterator &QQmlListCompositor::insert_iterator::operator +=(int difference) -{ - iterator::operator +=(difference); - - // If the previous range contains the append flag move the iterator to the tail of the previous - // range so that appended appear after the insert position. - if (offset == 0 && range->previous->append()) { - range = range->previous; - offset = range->inGroup() ? range->count : 0; - } - - return *this; -} - - -/*! - Constructs an empty list compositor. -*/ - -QQmlListCompositor::QQmlListCompositor() - : m_end(m_ranges.next, 0, Default, 2) - , m_cacheIt(m_end) - , m_groupCount(2) - , m_defaultFlags(PrependFlag | DefaultFlag) - , m_removeFlags(AppendFlag | PrependFlag | GroupMask) - , m_moveId(0) -{ -} - -/*! - Destroys a list compositor. -*/ - -QQmlListCompositor::~QQmlListCompositor() -{ - for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) { - next = range->next; - delete range; - } -} - -/*! - Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front - of the existing range \a before. -*/ - -inline QQmlListCompositor::Range *QQmlListCompositor::insert( - Range *before, void *list, int index, int count, uint flags) -{ - return new Range(before, list, index, count, flags); -} - -/*! - Erases a \a range from the compositor. - - Returns a pointer to the next range in the compositor. -*/ - -inline QQmlListCompositor::Range *QQmlListCompositor::erase( - Range *range) -{ - Range *next = range->next; - next->previous = range->previous; - next->previous->next = range->next; - delete range; - return next; -} - -/*! - Sets the number (\a count) of possible groups that items may belong to in a compositor. -*/ - -void QQmlListCompositor::setGroupCount(int count) -{ - m_groupCount = count; - m_end = iterator(&m_ranges, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -/*! - Returns the number of items that belong to a \a group. -*/ - -int QQmlListCompositor::count(Group group) const -{ - return m_end.index[group]; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index < count(group)); - if (m_cacheIt == m_end) { - m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); - m_cacheIt += index; - } else { - const int offset = index - m_cacheIt.index[group]; - m_cacheIt.setGroup(group); - m_cacheIt += offset; - } - Q_ASSERT(m_cacheIt.index[group] == index); - Q_ASSERT(m_cacheIt->inGroup(group)); - QT_QML_VERIFY_LISTCOMPOSITOR - return m_cacheIt; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) const -{ - return const_cast<QQmlListCompositor *>(this)->find(group, index); -} - -/*! - Returns an iterator representing an insert position in front of the item at \a index in a - \a group. - - The iterator for an insert position can sometimes resolve to a different Range than a regular - iterator. This is because when items are inserted on a boundary between Ranges, if the first - range has the Append flag set then the items should be inserted into that range to ensure - that the append position for the existing range remains after the insert position. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::insert_iterator QQmlListCompositor::findInsertPosition(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index <= count(group)); - insert_iterator it; - if (m_cacheIt == m_end) { - it = iterator(m_ranges.next, 0, group, m_groupCount); - it += index; - } else { - const int offset = index - m_cacheIt.index[group]; - it = m_cacheIt; - it.setGroup(group); - it += offset; - } - Q_ASSERT(it.index[group] == index); - return it; -} - -/*! - Appends a range of \a count indexes starting at \a index from a \a list into a compositor - with the given \a flags. - - If supplied the \a inserts list will be populated with the positions of the inserted items - in each group. -*/ - -void QQmlListCompositor::append( - void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags) - insert(m_end, list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a \a group at index \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -void QQmlListCompositor::insert( - Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags) - insert(findInsertPosition(group, before), list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a compositor at position \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::insert( - iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags) - if (inserts) { - inserts->append(Insert(before, count, flags & GroupMask)); - } - if (before.offset > 0) { - // Inserting into the middle of a range. Split it two and update the iterator so it's - // positioned at the start of the second half. - *before = insert( - *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next; - before->index += before.offset; - before->count -= before.offset; - before.offset = 0; - } - - - if (!(flags & AppendFlag) && *before != m_ranges.next - && before->previous->list == list - && before->previous->flags == flags - && (!list || before->previous->end() == index)) { - // The insert arguments represent a continuation of the previous range so increment - // its count instead of inserting a new range. - before->previous->count += count; - before.incrementIndexes(count, flags); - } else { - *before = insert(*before, list, index, count, flags); - before.offset = 0; - } - - if (!(flags & AppendFlag) && before->next != &m_ranges - && before->list == before->next->list - && before->flags == before->next->flags - && (!list || before->end() == before->next->index)) { - // The current range and the next are continuous so add their counts and delete one. - before->next->index = before->index; - before->next->count += before->count; - *before = erase(*before); - } - - m_end.incrementIndexes(count, flags); - m_cacheIt = before; - QT_QML_VERIFY_LISTCOMPOSITOR - return before; -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQmlListCompositor::setFlags( - Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - setFlags(find(fromGroup, from), count, group, flags, inserts); -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQmlListCompositor::setFlags( - iterator from, int count, Group group, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != from.group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - // Determine the actual changes made to the range and increment counts accordingly. - const uint insertFlags = ~from->flags & flags; - const uint setFlags = (from->flags | flags) & ~AppendFlag; - if (insertFlags && inserts) - inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag))); - m_end.incrementIndexes(difference, insertFlags); - from.incrementIndexes(difference, setFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == setFlags) { - // If the additional flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - // in the previous range. - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - continue; - } else { - break; - } - } else if (!insertFlags) { - // No new flags, so roll onto the next range. - from.incrementIndexes(from->count - difference); - continue; - } else if (difference < from->count) { - // Create a new range with the updated flags, and remove the affected items - // from the current range. - *from = insert(*from, from->list, from->index, difference, setFlags)->next; - from->index += difference; - from->count -= difference; - } else { - // The whole range is affected so simply update the flags. - from->flags |= flags; - continue; - } - from.incrementIndexes(from->count); - } - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQmlListCompositor::clearFlags( - Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - clearFlags(find(fromGroup, from), count, group, flags, removes); -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQmlListCompositor::clearFlags( - iterator from, int count, Group group, uint flags, QVector<Remove> *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - const bool clearCache = flags & CacheFlag; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - - // Determine the actual changes made to the range and decrement counts accordingly. - const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag); - const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag); - if (removeFlags && removes) { - const int maskedFlags = clearCache - ? (removeFlags & ~CacheFlag) - : (removeFlags | (from->flags & CacheFlag)); - if (maskedFlags) - removes->append(Remove(from, difference, maskedFlags)); - } - m_end.decrementIndexes(difference, removeFlags); - from.incrementIndexes(difference, clearedFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index) - && from->previous->flags == clearedFlags) { - // If the removed flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - } else { - from.incrementIndexes(from->count); - } - } else if (difference < from->count) { - // Create a new range with the reduced flags, and remove the affected items from - // the current range. - if (clearedFlags) - *from = insert(*from, from->list, from->index, difference, clearedFlags)->next; - from->index += difference; - from->count -= difference; - from.incrementIndexes(from->count); - } else if (clearedFlags) { - // The whole range is affected so simply update the flags. - from->flags &= ~flags; - } else { - // All flags have been removed from the range so remove it. - *from = erase(*from)->previous; - } - } - - if (*from != &m_ranges && from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -bool QQmlListCompositor::verifyMoveTo( - Group fromGroup, int from, Group toGroup, int to, int count, Group group) const -{ - if (group != toGroup) { - // determine how many items from the destination group intersect with the source group. - iterator fromIt = find(fromGroup, from); - - int intersectingCount = 0; - - for (; count > 0; *fromIt = fromIt->next) { - if (*fromIt == &m_ranges) - return false; - if (!fromIt->inGroup(group)) - continue; - if (fromIt->inGroup(toGroup)) - intersectingCount += qMin(count, fromIt->count - fromIt.offset); - count -= fromIt->count - fromIt.offset; - fromIt.offset = 0; - } - count = intersectingCount; - } - - return to >= 0 && to + count <= m_end.index[toGroup]; -} - -/*! - \internal - - Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup - to the index \a to in \a toGroup. - - If \a removes and \a inserts are not null they will be populated with per group notifications - of the items moved. - */ - -void QQmlListCompositor::move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group moveGroup, - QVector<Remove> *removes, - QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count) - Q_ASSERT(count > 0); - Q_ASSERT(from >=0); - Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup)); - - // Find the position of the first item to move. - iterator fromIt = find(fromGroup, from); - - if (fromIt != moveGroup) { - // If the range at the from index doesn't contain items from the move group; skip - // to the next range. - fromIt.incrementIndexes(fromIt->count - fromIt.offset); - fromIt.offset = 0; - *fromIt = fromIt->next; - } else if (fromIt.offset > 0) { - // If the range at the from index contains items from the move group and the index isn't - // at the start of the range; split the range at the index and move the iterator to start - // of the second range. - *fromIt = insert( - *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next; - fromIt->index += fromIt.offset; - fromIt->count -= fromIt.offset; - fromIt.offset = 0; - } - - // Remove count items belonging to the move group from the list. - Range movedFlags; - for (int moveId = m_moveId; count > 0;) { - if (fromIt != moveGroup) { - // Skip ranges not containing items from the move group. - fromIt.incrementIndexes(fromIt->count); - *fromIt = fromIt->next; - continue; - } - int difference = qMin(count, fromIt->count); - - // Create a new static range containing the moved items from an existing range. - new Range( - &movedFlags, - fromIt->list, - fromIt->index, - difference, - fromIt->flags & ~(PrependFlag | AppendFlag)); - // Remove moved items from the count, the existing range, and a remove notification. - if (removes) - removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId)); - count -= difference; - fromIt->count -= difference; - - // If the existing range contains the prepend flag replace the removed items with - // a placeholder range for new items inserted into the source model. - int removeIndex = fromIt->index; - if (fromIt->prepend() - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - // Grow the previous range instead of creating a new one if possible. - fromIt->previous->count += difference; - } else if (fromIt->prepend()) { - *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next; - } - fromIt->index += difference; - - if (fromIt->count == 0) { - // If the existing range has no items remaining; remove it from the list. - if (fromIt->append()) - fromIt->previous->flags |= AppendFlag; - *fromIt = erase(*fromIt); - - // If the ranges before and after the removed range can be joined, do so. - if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - fromIt.incrementIndexes(fromIt->count); - fromIt->previous->count += fromIt->count; - *fromIt = erase(*fromIt); - } - } else if (count > 0) { - *fromIt = fromIt->next; - } - } - - // Try and join the range following the removed items to the range preceding it. - if (*fromIt != m_ranges.next - && *fromIt != &m_ranges - && fromIt->previous->list == fromIt->list - && (!fromIt->list || fromIt->previous->end() == fromIt->index) - && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) { - if (fromIt == fromIt.group) - fromIt.offset = fromIt->previous->count; - fromIt.offset = fromIt->previous->count; - fromIt->previous->count += fromIt->count; - fromIt->previous->flags = fromIt->flags; - *fromIt = erase(*fromIt)->previous; - } - - // Find the destination position of the move. - insert_iterator toIt = fromIt; - toIt.setGroup(toGroup); - - const int difference = to - toIt.index[toGroup]; - toIt += difference; - - // If the insert position is part way through a range; split it and move the iterator to the - // start of the second range. - if (toIt.offset > 0) { - *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next; - toIt->index += toIt.offset; - toIt->count -= toIt.offset; - toIt.offset = 0; - } - - // Insert the moved ranges before the insert iterator, growing the previous range if that - // is an option. - for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) { - if (*toIt != &m_ranges - && range->list == toIt->list - && (!range->list || range->end() == toIt->index) - && range->flags == (toIt->flags & ~AppendFlag)) { - toIt->index -= range->count; - toIt->count += range->count; - } else { - *toIt = insert(*toIt, range->list, range->index, range->count, range->flags); - } - } - - // Try and join the range after the inserted ranges to the last range inserted. - if (*toIt != m_ranges.next - && toIt->previous->list == toIt->list - && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) { - toIt.offset = toIt->previous->count; - toIt->previous->count += toIt->count; - toIt->previous->flags = toIt->flags; - *toIt = erase(*toIt)->previous; - } - // Create insert notification for the ranges moved. - Insert insert(toIt, 0, 0, 0); - for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) { - insert.count = range->count; - insert.flags = range->flags; - if (inserts) { - insert.moveId = ++m_moveId; - inserts->append(insert); - } - for (int i = 0; i < m_groupCount; ++i) { - if (insert.inGroup(i)) - insert.index[i] += range->count; - } - - next = range->next; - delete range; - } - - m_cacheIt = toIt; - - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the contents of a compositor. -*/ - -void QQmlListCompositor::clear() -{ - QT_QML_TRACE_LISTCOMPOSITOR("") - for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {} - m_end = iterator(m_ranges.next, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -void QQmlListCompositor::listItemsInserted( - QVector<Insert> *translatedInsertions, - void *list, - const QVector<QQmlChangeSet::Change> &insertions, - const QVector<MovedFlags> *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } else if (it->flags & MovedFlag) { - // Skip ranges that were already moved in listItemsRemoved. - it->flags &= ~MovedFlag; - it.incrementIndexes(it->count); - continue; - } - for (const QQmlChangeSet::Change &insertion : insertions) { - int offset = insertion.index - it->index; - if ((offset > 0 && offset < it->count) - || (offset == 0 && it->prepend()) - || (offset == it->count && it->append())) { - // The insert index is within the current range. - if (it->prepend()) { - // The range has the prepend flag set so we insert new items into the range. - uint flags = m_defaultFlags; - if (insertion.isMove()) { - // If the insert was part of a move replace the default flags with - // the flags from the source range. - for (QVector<MovedFlags>::const_iterator move = movedFlags->begin(); - move != movedFlags->end(); - ++move) { - if (move->moveId == insertion.moveId) { - flags = move->flags; - break; - } - } - } - if (flags & ~(AppendFlag | PrependFlag)) { - // If any items are added to groups append an insert notification. - Insert translatedInsert(it, insertion.count, flags, insertion.moveId); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedInsert.index[i] += offset; - } - translatedInsertions->append(translatedInsert); - } - if ((it->flags & ~AppendFlag) == flags) { - // Accumulate items on the current range it its flags are the same as - // the insert flags. - it->count += insertion.count; - } else if (offset == 0 - && it->previous != &m_ranges - && it->previous->list == list - && it->previous->end() == insertion.index - && it->previous->flags == flags) { - // Attempt to append to the previous range if the insert position is at - // the start of the current range. - it->previous->count += insertion.count; - it->index += insertion.count; - it.incrementIndexes(insertion.count); - } else { - if (offset > 0) { - // Divide the current range at the insert position. - it.incrementIndexes(offset); - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - } - // Insert a new range, and increment the start index of the current range. - *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next; - it.incrementIndexes(insertion.count, flags); - it->index += offset + insertion.count; - it->count -= offset; - } - m_end.incrementIndexes(insertion.count, flags); - } else { - // The range doesn't have the prepend flag set so split the range into parts; - // one before the insert position and one after the last inserted item. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags)->next; - it->index += offset; - it->count -= offset; - } - it->index += insertion.count; - } - } else if (offset <= 0) { - // The insert position was before the current range so increment the start index. - it->index += insertion.count; - } - } - it.incrementIndexes(it->count); - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Updates the contents of a compositor when \a count items are inserted into a \a list at - \a index. - - This corrects the indexes of each range for that list in the compositor, splitting the range - in two if the insert index is in the middle of that range. If a range at the insert position - has the Prepend flag set then a new range will be inserted at that position with the groups - specified in defaultGroups(). If the insert index corresponds to the end of a range with - the Append flag set a new range will be inserted before the end of the append range. - - The \a translatedInsertions list is populated with insert notifications for affected - groups. -*/ - -void QQmlListCompositor::listItemsInserted( - void *list, int index, int count, QVector<Insert> *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count > 0); - - QVector<QQmlChangeSet::Change> insertions; - insertions.append(QQmlChangeSet::Change(index, count)); - - listItemsInserted(translatedInsertions, list, insertions); -} - -void QQmlListCompositor::listItemsRemoved( - QVector<Remove> *translatedRemovals, - void *list, - QVector<QQmlChangeSet::Change> *removals, - QVector<QQmlChangeSet::Change> *insertions, - QVector<MovedFlags> *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals) - - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } - bool removed = false; - for (QVector<QQmlChangeSet::Change>::iterator removal = removals->begin(); - !removed && removal != removals->end(); - ++removal) { - int relativeIndex = removal->index - it->index; - int itemsRemoved = removal->count; - if (relativeIndex + removal->count > 0 && relativeIndex < it->count) { - // If the current range intersects the remove; remove the intersecting items. - const int offset = qMax(0, relativeIndex); - int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; - it->count -= removeCount; - int removeFlags = it->flags & m_removeFlags; - Remove translatedRemoval(it, removeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedRemoval.index[i] += offset; - } - if (removal->isMove()) { - // If the removal was part of a move find the corresponding insert. - QVector<QQmlChangeSet::Change>::iterator insertion = insertions->begin(); - for (; insertion != insertions->end() && insertion->moveId != removal->moveId; - ++insertion) {} - Q_ASSERT(insertion != insertions->end()); - Q_ASSERT(insertion->count == removal->count); - - if (relativeIndex < 0) { - // If the remove started before the current range, split it and the - // corresponding insert so we're only working with intersecting part. - int splitMoveId = ++m_moveId; - removal = removals->insert(removal, QQmlChangeSet::Change( - removal->index, -relativeIndex, splitMoveId)); - ++removal; - removal->count -= -relativeIndex; - insertion = insertions->insert(insertion, QQmlChangeSet::Change( - insertion->index, -relativeIndex, splitMoveId)); - ++insertion; - insertion->index += -relativeIndex; - insertion->count -= -relativeIndex; - } - - if (it->prepend()) { - // If the current range has the prepend flag preserve its flags to transfer - // to its new location. - removeFlags |= it->flags & CacheFlag; - translatedRemoval.moveId = ++m_moveId; - movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag)); - - if (removeCount < removal->count) { - // If the remove doesn't encompass all of the current range, - // split it and the corresponding insert. - removal = removals->insert(removal, QQmlChangeSet::Change( - removal->index, removeCount, translatedRemoval.moveId)); - ++removal; - insertion = insertions->insert(insertion, QQmlChangeSet::Change( - insertion->index, removeCount, translatedRemoval.moveId)); - ++insertion; - - removal->count -= removeCount; - insertion->index += removeCount; - insertion->count -= removeCount; - } else { - // If there's no need to split the move simply replace its moveId - // with that of the translated move. - removal->moveId = translatedRemoval.moveId; - insertion->moveId = translatedRemoval.moveId; - } - } else { - // If the current range doesn't have prepend flags then insert a new range - // with list indexes from the corresponding insert location. The MoveFlag - // is to notify listItemsInserted that it can skip evaluation of that range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->end() == insertion->index - && it->previous->flags == (it->flags | MovedFlag)) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next; - } - // Clear the changed flags as the item hasn't been removed. - translatedRemoval.flags = 0; - removeFlags = 0; - } - } else if (it->inCache()) { - // If not moving and the current range has the cache flag, insert a new range - // with just the cache flag set to retain caching information for the removed - // range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->flags == CacheFlag) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next; - } - it.index[Cache] += removeCount; - } - if (removeFlags & GroupMask) - translatedRemovals->append(translatedRemoval); - m_end.decrementIndexes(removeCount, removeFlags); - if (it->count == 0 && !it->append()) { - // Erase empty non-append ranges. - *it = erase(*it)->previous; - removed = true; - } else if (relativeIndex <= 0) { - // If the remove started before the current range move the start index of - // the range to the remove index. - it->index = removal->index; - } - } else if (relativeIndex < 0) { - // If the remove was before the current range decrement the start index by the - // number of items removed. - it->index -= itemsRemoved; - - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->end() == it->index - && it->previous->flags == (it->flags & ~AppendFlag)) { - // Compress ranges made continuous by the removal of separating ranges. - it.decrementIndexes(it->previous->count); - it->previous->count += it->count; - it->previous->flags = it->flags; - *it = erase(*it)->previous; - } - } - } - if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) { - // Compress consecutive cache only ranges. - it.index[Cache] += it->next->count; - it->count += it->next->count; - erase(it->next); - } else if (!removed) { - it.incrementIndexes(it->count); - } - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals list is populated with remove notifications for the affected - groups. -*/ - - -void QQmlListCompositor::listItemsRemoved( - void *list, int index, int count, QVector<Remove> *translatedRemovals) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - - QVector<QQmlChangeSet::Change> removals; - removals.append(QQmlChangeSet::Change(index, count)); - listItemsRemoved(translatedRemovals, list, &removals, nullptr, nullptr); -} - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals and translatedInserts lists are populated with move - notifications for the affected groups. -*/ - -void QQmlListCompositor::listItemsMoved( - void *list, - int from, - int to, - int count, - QVector<Remove> *translatedRemovals, - QVector<Insert> *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count) - Q_ASSERT(count >= 0); - - QVector<QQmlChangeSet::Change> removals; - QVector<QQmlChangeSet::Change> insertions; - QVector<MovedFlags> movedFlags; - removals.append(QQmlChangeSet::Change(from, count, 0)); - insertions.append(QQmlChangeSet::Change(to, count, 0)); - - listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags); - listItemsInserted(translatedInsertions, list, insertions, &movedFlags); -} - -void QQmlListCompositor::listItemsChanged( - QVector<Change> *translatedChanges, - void *list, - const QVector<QQmlChangeSet::Change> &changes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - it.incrementIndexes(it->count); - continue; - } else if (!it->inGroup()) { - continue; - } - for (const QQmlChangeSet::Change &change : changes) { - const int offset = change.index - it->index; - if (offset + change.count > 0 && offset < it->count) { - const int changeOffset = qMax(0, offset); - const int changeCount = qMin(it->count, offset + change.count) - changeOffset; - - Change translatedChange(it, changeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedChange.index[i] += changeOffset; - } - translatedChanges->append(translatedChange); - } - } - it.incrementIndexes(it->count); - } -} - - -/*! - Translates the positions of \a count changed items at \a index in a \a list. - - The \a translatedChanges list is populated with change notifications for the - affected groups. -*/ - -void QQmlListCompositor::listItemsChanged( - void *list, int index, int count, QVector<Change> *translatedChanges) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - QVector<QQmlChangeSet::Change> changes; - changes.append(QQmlChangeSet::Change(index, count)); - listItemsChanged(translatedChanges, list, changes); -} - -void QQmlListCompositor::transition( - Group from, - Group to, - QVector<QQmlChangeSet::Change> *removes, - QVector<QQmlChangeSet::Change> *inserts) -{ - int removeCount = 0; - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it == from && it != to) { - removes->append(QQmlChangeSet::Change(it.index[from]- removeCount, it->count)); - removeCount += it->count; - } else if (it != from && it == to) { - inserts->append(QQmlChangeSet::Change(it.index[to], it->count)); - } - it.incrementIndexes(it->count); - } -} - -/*! - \internal - Writes the name of \a group to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group) -{ - switch (group) { - case QQmlListCompositor::Cache: return debug << "Cache"; - case QQmlListCompositor::Default: return debug << "Default"; - default: return (debug.nospace() << "Group" << int(group)).space(); - } - -} - -/*! - \internal - Writes the contents of \a range to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range) -{ - (debug.nospace() - << "Range(" - << range.list) << ' ' - << range.index << ' ' - << range.count << ' ' - << (range.isUnresolved() ? 'U' : '0') - << (range.append() ? 'A' : '0') - << (range.prepend() ? 'P' : '0'); - for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (range.inGroup(i) ? '1' : '0'); - return (debug - << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0') - << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0')); -} - -static void qt_print_indexes(QDebug &debug, int count, const int *indexes) -{ - for (int i = count - 1; i >= 0; --i) - debug << indexes[i]; -} - -/*! - \internal - Writes the contents of \a it to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it) -{ - (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; - qt_print_indexes(debug, it.groupCount, it.index); - return ((debug << **it).nospace() << ')').space(); -} - -static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change) -{ - debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' '; - for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (change.inGroup(i) ? '1' : '0'); - debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0') - << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'); - int i = QQmlListCompositor::MaximumGroupCount - 1; - for (; i >= 0 && !change.inGroup(i); --i) {} - for (; i >= 0; --i) - debug << ' ' << change.index[i]; - return (debug << ')').maybeSpace(); -} - -/*! - \internal - Writes the contents of \a change to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change) -{ - return qt_print_change(debug, "Change", change); -} - -/*! - \internal - Writes the contents of \a remove to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove) -{ - return qt_print_change(debug, "Remove", remove); -} - -/*! - \internal - Writes the contents of \a insert to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert) -{ - return qt_print_change(debug, "Insert", insert); -} - -/*! - \internal - Writes the contents of \a list to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor &list) -{ - int indexes[QQmlListCompositor::MaximumGroupCount]; - for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i) - indexes[i] = 0; - debug.nospace() << "QQmlListCompositor("; - qt_print_indexes(debug, list.m_groupCount, list.m_end.index); - for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { - (debug << '\n').space(); - qt_print_indexes(debug, list.m_groupCount, indexes); - debug << ' ' << *range; - - for (int i = 0; i < list.m_groupCount; ++i) { - if (range->inGroup(i)) - indexes[i] += range->count; - } - } - return (debug << ')').maybeSpace(); -} - -QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qml/util/qqmllistcompositor_p.h deleted file mode 100644 index 172040559c..0000000000 --- a/src/qml/util/qqmllistcompositor_p.h +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************** -** -** 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 QQMLLISTCOMPOSITOR_P_H -#define QQMLLISTCOMPOSITOR_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 <QtCore/qvector.h> - -#include <private/qqmlchangeset_p.h> - -#include <QtCore/qdebug.h> - -QT_BEGIN_NAMESPACE - -class Q_AUTOTEST_EXPORT QQmlListCompositor -{ -public: - enum { MinimumGroupCount = 3, MaximumGroupCount = 11 }; - - enum Group - { - Cache = 0, - Default = 1, - Persisted = 2 - }; - - enum Flag - { - CacheFlag = 1 << Cache, - DefaultFlag = 1 << Default, - PersistedFlag = 1 << Persisted, - PrependFlag = 0x10000000, - AppendFlag = 0x20000000, - UnresolvedFlag = 0x40000000, - MovedFlag = 0x80000000, - GroupMask = ~(PrependFlag | AppendFlag | UnresolvedFlag | MovedFlag | CacheFlag) - }; - - class Range - { - public: - Range() : next(this), previous(this) {} - Range(Range *next, void *list, int index, int count, uint flags) - : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) { - next->previous = this; previous->next = this; } - - Range *next; - Range *previous; - void *list = nullptr; - int index = 0; - int count = 0; - uint flags = 0; - - inline int start() const { return index; } - inline int end() const { return index + count; } - - inline int groups() const { return flags & GroupMask; } - - inline bool inGroup() const { return flags & GroupMask; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup(int group) const { return flags & (1 << group); } - inline bool isUnresolved() const { return flags & UnresolvedFlag; } - - inline bool prepend() const { return flags & PrependFlag; } - inline bool append() const { return flags & AppendFlag; } - }; - - class Q_AUTOTEST_EXPORT iterator - { - public: - inline iterator(); - inline iterator(const iterator &it); - inline iterator(Range *range, int offset, Group group, int groupCount); - inline ~iterator() {} - - bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; } - bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; } - - bool operator ==(Group group) const { return range->flags & (1 << group); } - bool operator !=(Group group) const { return !(range->flags & (1 << group)); } - - Range *&operator *() { return range; } - Range * const &operator *() const { return range; } - Range *operator ->() { return range; } - const Range *operator ->() const { return range; } - - iterator &operator +=(int difference); - - template<typename T> T *list() const { return static_cast<T *>(range->list); } - int modelIndex() const { return range->index + offset; } - - void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); } - void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); } - - inline void incrementIndexes(int difference, uint flags); - inline void decrementIndexes(int difference, uint flags); - - void setGroup(Group g) { group = g; groupFlag = 1 << g; } - - Range *range = nullptr; - int offset = 0; - Group group = Default; - int groupFlag; - int groupCount = 0; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - }; - - class Q_AUTOTEST_EXPORT insert_iterator : public iterator - { - public: - inline insert_iterator() {} - inline insert_iterator(const iterator &it) : iterator(it) {} - inline insert_iterator(Range *, int, Group, int); - inline ~insert_iterator() {} - - insert_iterator &operator +=(int difference); - }; - - struct Change - { - inline Change() {} - inline Change(const iterator &it, int count, uint flags, int moveId = -1); - int count; - uint flags; - int moveId; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - - inline bool isMove() const { return moveId >= 0; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup() const { return flags & GroupMask; } - inline bool inGroup(int group) const { return flags & (CacheFlag << group); } - - inline int groups() const { return flags & GroupMask; } - }; - - struct Insert : public Change - { - Insert() {} - Insert(const iterator &it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - struct Remove : public Change - { - Remove() {} - Remove(const iterator &it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - QQmlListCompositor(); - ~QQmlListCompositor(); - - int defaultGroups() const { return m_defaultFlags & ~PrependFlag; } - void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } - void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } - void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } - void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } - void setGroupCount(int count); - - int count(Group group) const; - iterator find(Group group, int index); - iterator find(Group group, int index) const; - insert_iterator findInsertPosition(Group group, int index); - - const iterator &end() { return m_end; } - - void append(void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - void insert(Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - iterator insert(iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - - void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts = nullptr); - void setFlags(iterator from, int count, Group group, uint flags, QVector<Insert> *inserts = nullptr); - void setFlags(Group fromGroup, int from, int count, uint flags, QVector<Insert> *inserts = nullptr) { - setFlags(fromGroup, from, count, fromGroup, flags, inserts); } - void setFlags(const iterator from, int count, uint flags, QVector<Insert> *inserts = nullptr) { - setFlags(from, count, from.group, flags, inserts); } - - void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removals = nullptr); - void clearFlags(iterator from, int count, Group group, uint flags, QVector<Remove> *removals = nullptr); - void clearFlags(Group fromGroup, int from, int count, uint flags, QVector<Remove> *removals = nullptr) { - clearFlags(fromGroup, from, count, fromGroup, flags, removals); } - void clearFlags(const iterator &from, int count, uint flags, QVector<Remove> *removals = nullptr) { - clearFlags(from, count, from.group, flags, removals); } - - bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const; - - void move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group group, - QVector<Remove> *removals = nullptr, - QVector<Insert> *inserts = nullptr); - void clear(); - - void listItemsInserted(void *list, int index, int count, QVector<Insert> *inserts); - void listItemsRemoved(void *list, int index, int count, QVector<Remove> *removals); - void listItemsMoved(void *list, int from, int to, int count, QVector<Remove> *removals, QVector<Insert> *inserts); - void listItemsChanged(void *list, int index, int count, QVector<Change> *changes); - - void transition( - Group from, - Group to, - QVector<QQmlChangeSet::Change> *removes, - QVector<QQmlChangeSet::Change> *inserts); - -private: - Range m_ranges; - iterator m_end; - iterator m_cacheIt; - int m_groupCount; - int m_defaultFlags; - int m_removeFlags; - int m_moveId; - - inline Range *insert(Range *before, void *list, int index, int count, uint flags); - inline Range *erase(Range *range); - - struct MovedFlags - { - MovedFlags() {} - MovedFlags(int moveId, uint flags) : moveId(moveId), flags(flags) {} - - int moveId; - uint flags; - }; - - void listItemsRemoved( - QVector<Remove> *translatedRemovals, - void *list, - QVector<QQmlChangeSet::Change> *removals, - QVector<QQmlChangeSet::Change> *insertions = nullptr, - QVector<MovedFlags> *movedFlags = nullptr); - void listItemsInserted( - QVector<Insert> *translatedInsertions, - void *list, - const QVector<QQmlChangeSet::Change> &insertions, - const QVector<MovedFlags> *movedFlags = nullptr); - void listItemsChanged( - QVector<Change> *translatedChanges, - void *list, - const QVector<QQmlChangeSet::Change> &changes); - - friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); -}; - -Q_DECLARE_TYPEINFO(QQmlListCompositor::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlListCompositor::Remove, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE); - -inline QQmlListCompositor::iterator::iterator() {} -inline QQmlListCompositor::iterator::iterator(const iterator &it) - : range(it.range) - , offset(it.offset) - , group(it.group) - , groupFlag(it.groupFlag) - , groupCount(it.groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = it.index[i]; -} - -inline QQmlListCompositor::iterator::iterator( - Range *range, int offset, Group group, int groupCount) - : range(range) - , offset(offset) - , group(group) - , groupFlag(1 << group) - , groupCount(groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = 0; -} - -inline void QQmlListCompositor::iterator::incrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] += difference; - } -} - -inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] -= difference; - } -} - -inline QQmlListCompositor::insert_iterator::insert_iterator( - Range *range, int offset, Group group, int groupCount) - : iterator(range, offset, group, groupCount) {} - -inline QQmlListCompositor::Change::Change(const iterator &it, int count, uint flags, int moveId) - : count(count), flags(flags), moveId(moveId) -{ - for (int i = 0; i < MaximumGroupCount; ++i) - index[i] = it.index[i]; -} - -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri index bebb271f1b..3b121ba3cb 100644 --- a/src/qml/util/util.pri +++ b/src/qml/util/util.pri @@ -1,19 +1,5 @@ SOURCES += \ - $$PWD/qqmlchangeset.cpp \ - $$PWD/qqmllistaccessor.cpp \ - $$PWD/qqmllistcompositor.cpp \ $$PWD/qqmlpropertymap.cpp HEADERS += \ - $$PWD/qqmlchangeset_p.h \ - $$PWD/qqmllistaccessor_p.h \ - $$PWD/qqmllistcompositor_p.h \ $$PWD/qqmlpropertymap.h - -qtConfig(qml-delegate-model) { - SOURCES += \ - $$PWD/qqmladaptormodel.cpp - - HEADERS += \ - $$PWD/qqmladaptormodel_p.h -} |