diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-07-10 10:46:05 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-07-11 17:22:52 +0200 |
commit | de0d91abbbcf58a66018a08ca77bb4d63a5efda1 (patch) | |
tree | e960c18118f09b793b54c6767d0ffd4633da7d9d /src/qml/common | |
parent | 1948139d4c7c76817e13334e8528b01093afa69d (diff) |
Split compiler and runtime more clearly
Provide different export macros and different top level headers for
each, don't include runtime headers from compiler sources.
Change-Id: I7dc3f8c95839a00a871ba045ec65af87123154be
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/common')
-rw-r--r-- | src/qml/common/common.pri | 32 | ||||
-rw-r--r-- | src/qml/common/qv4alloca_p.h | 108 | ||||
-rw-r--r-- | src/qml/common/qv4calldata_p.h | 123 | ||||
-rw-r--r-- | src/qml/common/qv4compileddata_p.h | 1327 | ||||
-rw-r--r-- | src/qml/common/qv4staticvalue_p.h | 558 | ||||
-rw-r--r-- | src/qml/common/qv4stringtoarrayindex_p.h | 96 |
6 files changed, 2243 insertions, 1 deletions
diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri index 9de4fcae16..bcc3ea0fa0 100644 --- a/src/qml/common/common.pri +++ b/src/qml/common/common.pri @@ -1,5 +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/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/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h new file mode 100644 index 0000000000..65c3e4d65a --- /dev/null +++ b/src/qml/common/qv4alloca_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 QV4_ALLOCA_H +#define QV4_ALLOCA_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> + +#if QT_CONFIG(alloca_h) +# include <alloca.h> +#elif QT_CONFIG(alloca_malloc_h) +# include <malloc.h> +// This does not matter unless compiling in strict standard mode. +# ifdef Q_CC_MSVC +# define alloca _alloca +# endif +#else +# include <stdlib.h> +#endif + +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast<type*>(alloca(size)) + +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); memset(m_data, 0, size); } +private: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = nullptr + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast<type*>(_qt_alloca_##name.data()) + +#endif + +#endif diff --git a/src/qml/common/qv4calldata_p.h b/src/qml/common/qv4calldata_p.h new file mode 100644 index 0000000000..5a5280cb86 --- /dev/null +++ b/src/qml/common/qv4calldata_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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 QV4CALLDATA_P_H +#define QV4CALLDATA_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/qv4staticvalue_p.h> + +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 + }; + + 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]; + + static Q_DECL_CONSTEXPR int HeaderSize() + { + return offsetof(CallData, args) / sizeof(QV4::StaticValue); + } + + template<typename Value> + Value *argValues(); + + template<typename Value> + const Value *argValues() const; +}; + +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)); + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4CALLDATA_P_H diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h new file mode 100644 index 0000000000..c3ddce5884 --- /dev/null +++ b/src/qml/common/qv4compileddata_p.h @@ -0,0 +1,1327 @@ +/**************************************************************************** +** +** 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 QV4COMPILEDDATA_P_H +#define QV4COMPILEDDATA_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 <QtCore/qstring.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. +// +// 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 QQmlTypeNameCache; +class QQmlType; +class QQmlEngine; + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace Heap { +struct Module; +struct String; +struct InternalClass; +}; + +struct Function; +class EvalISelFactory; + +namespace CompiledData { + +struct String; +struct Function; +struct Lookup; +struct RegExp; +struct Unit; + +template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const> +struct TableIterator +{ + TableIterator(const Container *container, int index) : container(container), index(index) {} + const Container *container; + int index; + + const ItemType *operator->() { return (container->*IndexedGetter)(index); } + void operator++() { ++index; } + bool operator==(const TableIterator &rhs) const { return index == rhs.index; } + bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } +}; + +struct Location +{ + union { + quint32 _dummy; + quint32_le_bitfield<0, 20> line; + quint32_le_bitfield<20, 12> column; + }; + + Location() : _dummy(0) { } + + inline bool operator<(const Location &other) const { + return line < other.line || + (line == other.line && column < other.column); + } +}; +static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct RegExp +{ + enum Flags : unsigned int { + RegExp_NoFlags = 0x0, + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04, + RegExp_Unicode = 0x08, + RegExp_Sticky = 0x10 + }; + union { + quint32 _dummy; + quint32_le_bitfield<0, 5> flags; + quint32_le_bitfield<5, 27> stringIndex; + }; + + RegExp() : _dummy(0) { } +}; +static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Lookup +{ + enum Type : unsigned int { + Type_Getter = 0, + Type_Setter = 1, + Type_GlobalGetter = 2, + Type_QmlContextPropertyGetter = 3 + }; + + union { + quint32 _dummy; + quint32_le_bitfield<0, 4> type_and_flags; + quint32_le_bitfield<4, 28> nameIndex; + }; + + Lookup() : _dummy(0) { } +}; +static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct JSClassMember +{ + union { + quint32 _dummy; + quint32_le_bitfield<0, 31> nameOffset; + quint32_le_bitfield<31, 1> isAccessor; + }; + + JSClassMember() : _dummy(0) { } +}; +static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct JSClass +{ + quint32_le nMembers; + // JSClassMember[nMembers] + + static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } +}; +static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +// This data structure is intended to be binary compatible with QStringData/QStaticStringData on +// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped +// from a file must be castable to a QStringData regardless of the pointer size. With the first +// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a +// ptrdiff_t and thus variable in size. +// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while +// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain +// the same value. +struct String +{ + qint32_le refcount; // -1 + qint32_le size; + quint32_le allocAndCapacityReservedFlag; // 0 + quint32_le offsetOn32Bit; + quint64_le offsetOn64Bit; + // uint16 strdata[] + + static int calculateSize(const QString &str) { + return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7; + } +}; +static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +// Ensure compatibility with QString +static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location"); +static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location"); +static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location"); +static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location"); +#if QT_POINTER_SIZE == 8 +static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location"); +#else +static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location"); +#endif + +struct CodeOffsetToLine { + quint32_le codeOffset; + quint32_le line; +}; +static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Block +{ + quint32_le nLocals; + quint32_le localsOffset; + quint16_le sizeOfLocalTemporalDeadZone; + quint16_le padding; + + const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } + + static int calculateSize(int nLocals) { + int trailingData = nLocals*sizeof (quint32); + size_t size = align(align(sizeof(Block)) + size_t(trailingData)); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; +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 +{ + enum Flags : unsigned int { + IsStrict = 0x1, + IsArrowFunction = 0x2, + IsGenerator = 0x4 + }; + + // Absolute offset into file where the code for this function is located. + quint32_le codeOffset; + quint32_le codeSize; + + quint32_le nameIndex; + 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; + size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } + quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers + quint16_le sizeOfLocalTemporalDeadZone; + quint16_le firstTemporalDeadZoneRegister; + quint16_le sizeOfRegisterTemporalDeadZone; + quint16_le nRegisters; + Location location; + + quint32_le nLabelInfos; + size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } + + // Keep all unaligned data at the end + quint8 flags; + quint8 padding1; + + // quint32 formalsIndex[nFormals] + // quint32 localsIndex[nLocals] + + 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 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()); } + + 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 * 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); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; +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 { + Regular, + Getter, + Setter + }; + + quint32_le name; + quint32_le type; + quint32_le function; +}; +static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Class +{ + quint32_le nameIndex; + quint32_le scopeIndex; + quint32_le constructorFunction; + quint32_le nStaticMethods; + quint32_le nMethods; + quint32_le methodTableOffset; + + const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); } + + static int calculateSize(int nStaticMethods, int nMethods) { + int trailingData = (nStaticMethods + nMethods) * sizeof(Method); + size_t size = align(sizeof(Class) + trailingData); + Q_ASSERT(size < INT_MAX); + return int(size); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } +}; +static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct TemplateObject +{ + quint32_le size; + + static int calculateSize(int size) { + int trailingData = 2 * size * sizeof(quint32_le); + size_t s = align(sizeof(TemplateObject) + trailingData); + Q_ASSERT(s < INT_MAX); + return int(s); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } + + const quint32_le *stringTable() const { + return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1)); + } + + uint stringIndexAt(uint i) const { + return stringTable()[i]; + } + uint rawStringIndexAt(uint i) const { + return stringTable()[size + i]; + } +}; +static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct ExportEntry +{ + quint32_le exportName; + quint32_le moduleRequest; + quint32_le importName; + quint32_le localName; + Location location; +}; +static_assert(sizeof(ExportEntry) == 20, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct ImportEntry +{ + quint32_le moduleRequest; + quint32_le importName; + quint32_le localName; + Location location; +}; +static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +// Qml data structures + +struct TranslationData +{ + quint32_le stringIndex; + quint32_le commentIndex; + qint32_le number; + quint32_le padding; +}; +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 Binding +{ + quint32_le propertyNameIndex; + + enum ValueType : unsigned int { + Type_Invalid, + Type_Boolean, + Type_Number, + Type_String, + Type_Null, + Type_Translation, + Type_TranslationById, + Type_Script, + Type_Object, + Type_AttachedProperty, + Type_GroupProperty + }; + + enum Flags : unsigned int { + IsSignalHandlerExpression = 0x1, + IsSignalHandlerObject = 0x2, + IsOnAssignment = 0x4, + InitializerForReadOnlyDeclaration = 0x8, + IsResolvedEnum = 0x10, + IsListItem = 0x20, + IsBindingToAlias = 0x40, + IsDeferredBinding = 0x80, + IsCustomParserBinding = 0x100, + IsFunctionExpression = 0x200 + }; + + union { + quint32_le_bitfield<0, 16> flags; + quint32_le_bitfield<16, 16> type; + }; + union { + bool b; + quint32_le constantValueIndex; + quint32_le compiledScriptIndex; // used when Type_Script + quint32_le objectIndex; + quint32_le translationDataIndex; // used when Type_Translation + quint32 nullMarker; + } value; + quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) + + Location location; + Location valueLocation; + + bool isValueBinding() const + { + if (type == Type_AttachedProperty + || type == Type_GroupProperty) + return false; + if (flags & IsSignalHandlerExpression + || flags & IsSignalHandlerObject) + return false; + return true; + } + + bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); } + bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); } + + bool isSignalHandler() const + { + if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isAttachedProperty()); + Q_ASSERT(!isGroupProperty()); + return true; + } + return false; + } + + bool isAttachedProperty() const + { + if (type == Type_AttachedProperty) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isSignalHandler()); + Q_ASSERT(!isGroupProperty()); + return true; + } + return false; + } + + bool isGroupProperty() const + { + if (type == Type_GroupProperty) { + Q_ASSERT(!isValueBinding()); + Q_ASSERT(!isSignalHandler()); + Q_ASSERT(!isAttachedProperty()); + return true; + } + return false; + } + + bool isFunctionExpression() const { return (flags & IsFunctionExpression); } + + //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(); } + + bool valueAsBoolean() const + { + if (type == Type_Boolean) + return value.b; + return false; + } + +}; + +static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct EnumValue +{ + quint32_le nameIndex; + qint32_le value; + Location location; +}; +static_assert(sizeof(EnumValue) == 12, "EnumValue structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Enum +{ + quint32_le nameIndex; + quint32_le nEnumValues; + Location location; + + const EnumValue *enumValueAt(int idx) const { + return reinterpret_cast<const EnumValue*>(this + 1) + idx; + } + + static int calculateSize(int nEnumValues) { + return (sizeof(Enum) + + nEnumValues * sizeof(EnumValue) + + 7) & ~0x7; + } + + // --- QQmlPropertyCacheCreatorInterface + const EnumValue *enumValuesBegin() const { return enumValueAt(0); } + const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); } + int enumValueCount() const { return nEnumValues; } + // --- +}; +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 Signal +{ + quint32_le nameIndex; + quint32_le nParameters; + Location location; + // Parameter parameters[1]; + + const Parameter *parameterAt(int idx) const { + return reinterpret_cast<const Parameter*>(this + 1) + idx; + } + + static int calculateSize(int nParameters) { + return (sizeof(Signal) + + nParameters * sizeof(Parameter) + + 7) & ~0x7; + } + + // --- QQmlPropertyCacheCceatorInterface + const Parameter *parametersBegin() const { return parameterAt(0); } + const Parameter *parametersEnd() const { return parameterAt(nParameters); } + int parameterCount() const { return nParameters; } + // --- +}; +static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Property +{ + quint32_le nameIndex; + union { + quint32_le_bitfield<0, 29> builtinTypeOrTypeNameIndex; + quint32_le_bitfield<29, 1> isBuiltinType; + quint32_le_bitfield<30, 1> isList; + quint32_le_bitfield<31, 1> isReadOnly; + }; + + 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) == 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 { + IsReadOnly = 0x1, + Resolved = 0x2, + AliasPointsToPointerObject = 0x4 + }; + union { + quint32_le_bitfield<0, 29> nameIndex; + quint32_le_bitfield<29, 3> flags; + }; + union { + quint32_le idIndex; // string index + quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) + quint32_le_bitfield<31, 1> aliasToLocalAlias; + }; + union { + quint32_le propertyNameIndex; // string index + qint32_le encodedMetaPropertyIndex; + quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) + }; + Location location; + Location referenceLocation; + + bool isObjectAlias() const { + Q_ASSERT(flags & Resolved); + return encodedMetaPropertyIndex == -1; + } +}; +static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Object +{ + enum Flags : unsigned int { + NoFlag = 0x0, + IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary + HasDeferredBindings = 0x2, // any of the bindings are deferred + HasCustomParserBindings = 0x4 + }; + + // Depending on the use, this may be the type name to instantiate before instantiating this + // object. For grouped properties the type name will be empty and for attached properties + // it will be the name of the attached type. + quint32_le inheritedTypeNameIndex; + quint32_le idNameIndex; + union { + quint32_le_bitfield<0, 15> flags; + quint32_le_bitfield<15, 1> defaultPropertyIsAlias; + qint32_le_bitfield<16, 16> id; + }; + qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object + quint16_le nFunctions; + quint16_le nProperties; + quint32_le offsetToFunctions; + quint32_le offsetToProperties; + quint32_le offsetToAliases; + quint16_le nAliases; + quint16_le nEnums; + quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects + quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects + quint16_le nSignals; + quint16_le nBindings; + quint32_le offsetToBindings; + quint32_le nNamedObjectsInComponent; + quint32_le offsetToNamedObjectsInComponent; + Location location; + Location locationOfIdProperty; +// Function[] +// Property[] +// Signal[] +// Binding[] + + static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent) + { + return ( sizeof(Object) + + nFunctions * sizeof(quint32) + + nProperties * sizeof(Property) + + nAliases * sizeof(Alias) + + nEnums * sizeof(quint32) + + nSignals * sizeof(quint32) + + nBindings * sizeof(Binding) + + nNamedObjectsInComponent * sizeof(int) + + 0x7 + ) & ~0x7; + } + + const quint32_le *functionOffsetTable() const + { + return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions); + } + + const Property *propertyTable() const + { + return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties); + } + + const Alias *aliasTable() const + { + return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases); + } + + const Binding *bindingTable() const + { + return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings); + } + + const Enum *enumAt(int idx) const + { + const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset); + } + + const Signal *signalAt(int idx) const + { + const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); + } + + const quint32_le *namedObjectsInComponentTable() const + { + return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); + } + + // --- QQmlPropertyCacheCreator interface + int propertyCount() const { return nProperties; } + int aliasCount() const { return nAliases; } + int enumCount() const { return nEnums; } + int signalCount() const { return nSignals; } + int functionCount() const { return nFunctions; } + + const Binding *bindingsBegin() const { return bindingTable(); } + const Binding *bindingsEnd() const { return bindingTable() + nBindings; } + + const Property *propertiesBegin() const { return propertyTable(); } + const Property *propertiesEnd() const { return propertyTable() + nProperties; } + + const Alias *aliasesBegin() const { return aliasTable(); } + const Alias *aliasesEnd() const { return aliasTable() + nAliases; } + + typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator; + EnumIterator enumsBegin() const { return EnumIterator(this, 0); } + EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); } + + typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator; + SignalIterator signalsBegin() const { return SignalIterator(this, 0); } + SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } + + int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } + // --- +}; +static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Import +{ + enum ImportType : unsigned int { + ImportLibrary = 0x1, + ImportFile = 0x2, + ImportScript = 0x3 + }; + quint32_le type; + + quint32_le uriIndex; + quint32_le qualifierIndex; + + qint32_le majorVersion; + qint32_le minorVersion; + + Location location; + + Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } +}; +static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct QmlUnit +{ + quint32_le nImports; + quint32_le offsetToImports; + quint32_le nObjects; + quint32_le offsetToObjects; + + const Import *importAt(int idx) const { + return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); + } + + const Object *objectAt(int idx) const { + const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset); + } +}; +static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +enum { QmlCompileHashSpace = 48 }; +static const char magic_str[] = "qv4cdata"; + +struct Unit +{ + // DO NOT CHANGE THESE FIELDS EVER + char magic[8]; + quint32_le version; + quint32_le qtVersion; + qint64_le sourceTimeStamp; + quint32_le unitSize; // Size of the Unit and any depending data. + // END DO NOT CHANGE THESE FIELDS EVER + + char libraryVersionHash[QmlCompileHashSpace]; + + char md5Checksum[16]; // checksum of all bytes following this field. + char dependencyMD5Checksum[16]; + + enum : unsigned int { + IsJavascript = 0x1, + StaticData = 0x2, // Unit data persistent in memory? + IsSingleton = 0x4, + IsSharedLibrary = 0x8, // .pragma shared? + IsESModule = 0x10, + PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation + }; + quint32_le flags; + quint32_le stringTableSize; + quint32_le offsetToStringTable; + quint32_le functionTableSize; + quint32_le offsetToFunctionTable; + quint32_le classTableSize; + quint32_le offsetToClassTable; + quint32_le templateObjectTableSize; + quint32_le offsetToTemplateObjectTable; + quint32_le blockTableSize; + quint32_le offsetToBlockTable; + quint32_le lookupTableSize; + quint32_le offsetToLookupTable; + quint32_le regexpTableSize; + quint32_le offsetToRegexpTable; + quint32_le constantTableSize; + quint32_le offsetToConstantTable; + quint32_le jsClassTableSize; + quint32_le offsetToJSClassTable; + quint32_le translationTableSize; + quint32_le offsetToTranslationTable; + quint32_le localExportEntryTableSize; + quint32_le offsetToLocalExportEntryTable; + quint32_le indirectExportEntryTableSize; + quint32_le offsetToIndirectExportEntryTable; + quint32_le starExportEntryTableSize; + quint32_le offsetToStarExportEntryTable; + quint32_le importEntryTableSize; + quint32_le offsetToImportEntryTable; + quint32_le moduleRequestTableSize; + quint32_le offsetToModuleRequestTable; + qint32_le indexOfRootFunction; + quint32_le sourceFileIndex; + quint32_le finalUrlIndex; + + quint32_le offsetToQmlUnit; + + /* QML specific fields */ + + const QmlUnit *qmlUnit() const { + return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit); + } + + QmlUnit *qmlUnit() { + return reinterpret_cast<QmlUnit *>(reinterpret_cast<char *>(this) + offsetToQmlUnit); + } + + bool isSingleton() const { + return flags & Unit::IsSingleton; + } + /* end QML specific fields*/ + + QString stringAtInternal(int idx) const { + Q_ASSERT(idx < int(stringTableSize)); + const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable); + const quint32_le offset = offsetTable[idx]; + const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset); + if (str->size == 0) + return QString(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + if (flags & StaticData) { + const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) }; + return QString(holder); + } + const QChar *characters = reinterpret_cast<const QChar *>(str + 1); + return QString(characters, str->size); +#else + const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1); + QString qstr(str->size, Qt::Uninitialized); + QChar *ch = qstr.data(); + for (int i = 0; i < str->size; ++i) + ch[i] = QChar(characters[i]); + return qstr; +#endif + } + + const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } + const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } + const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); } + const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } + + const Function *functionAt(int idx) const { + const quint32_le *offsetTable = functionOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); + } + + const Class *classAt(int idx) const { + const quint32_le *offsetTable = classOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); + } + + const TemplateObject *templateObjectAt(int idx) const { + const quint32_le *offsetTable = templateObjectOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset); + } + + const Block *blockAt(int idx) const { + const quint32_le *offsetTable = blockOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset); + } + + const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); } + const RegExp *regexpAt(int index) const { + return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp)); + } + const quint64_le *constants() const { + return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable); + } + + const JSClassMember *jsClassAt(int idx, int *nMembers) const { + const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable); + const quint32_le offset = offsetTable[idx]; + const char *ptr = reinterpret_cast<const char *>(this) + offset; + const JSClass *klass = reinterpret_cast<const JSClass *>(ptr); + *nMembers = klass->nMembers; + return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass)); + } + + const TranslationData *translations() const { + return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable); + } + + const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); } + const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); } + const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); } + const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); } + + const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); } +}; + +static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct TypeReference +{ + TypeReference(const Location &loc) + : location(loc) + , needsCreation(false) + , errorWhenNotFound(false) + {} + Location location; // first use + bool needsCreation : 1; // whether the type needs to be creatable or not + bool errorWhenNotFound: 1; +}; + +// Map from name index to location of first use. +struct TypeReferenceMap : QHash<int, TypeReference> +{ + TypeReference &add(int nameIndex, const Location &loc) { + Iterator it = find(nameIndex); + if (it != end()) + return *it; + return *insert(nameIndex, loc); + } + + template <typename CompiledObject> + void collectFromObject(const CompiledObject *obj) + { + if (obj->inheritedTypeNameIndex != 0) { + TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location); + r.needsCreation = true; + r.errorWhenNotFound = true; + } + + auto prop = obj->propertiesBegin(); + auto propEnd = obj->propertiesEnd(); + for ( ; prop != propEnd; ++prop) { + if (!prop->isBuiltinType) { + TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location); + r.errorWhenNotFound = true; + } + } + + auto binding = obj->bindingsBegin(); + auto bindingEnd = obj->bindingsEnd(); + for ( ; binding != bindingEnd; ++binding) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) + this->add(binding->propertyNameIndex, binding->location); + } + } + + template <typename Iterator> + void collectFromObjects(Iterator it, Iterator end) + { + for (; it != end; ++it) + collectFromObject(*it); + } +}; + +using DependentTypesHasher = std::function<QByteArray()>; + +// This is how this hooks into the existing structures: + +struct CompilationUnitBase +{ + Q_DISABLE_COPY(CompilationUnitBase) + + CompilationUnitBase() = default; + ~CompilationUnitBase() = default; + + CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); } + + 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; + } + + // pointers either to data->constants() or little-endian memory copy. + 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 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 CompilationUnit : public CompilationUnitBase +{ + Q_DISABLE_COPY(CompilationUnit) + + const Unit *data = nullptr; + const QmlUnit *qmlData = nullptr; + QStringList dynamicStrings; +public: + using CompiledObject = CompiledData::Object; + + CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), + const QString &finalUrlString = QString()) + { + setUnitData(unitData, nullptr, fileName, finalUrlString); + } + + ~CompilationUnit() + { + if (data) { + if (data->qmlUnit() != qmlData) + free(const_cast<QmlUnit *>(qmlData)); + qmlData = nullptr; + + 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 + + delete [] imports; + imports = nullptr; + } + + CompilationUnit(CompilationUnit &&other) noexcept + { + *this = std::move(other); + } + + 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; + } + + const Unit *unitData() const { return data; } + + 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 + + m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); + m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); + } + + QString stringAt(int index) const + { + if (uint(index) >= data->stringTableSize) + return dynamicStrings.at(index - data->stringTableSize); + return data->stringAtInternal(index); + } + + QString fileName() const { return m_fileName; } + QString finalUrlString() const { return m_finalUrlString; } + + Heap::Module *module() const { return m_module; } + void setModule(Heap::Module *module) { m_module = module; } + +private: + QString m_fileName; // initialized from data->sourceFileIndex + QString m_finalUrlString; // initialized from data->finalUrlIndex + + Heap::Module *m_module = nullptr; +}; + +class SaveableUnitPointer +{ + Q_DISABLE_COPY_MOVE(SaveableUnitPointer) +public: + 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 + Q_UNUSED(outputFileName) + *errorString = QStringLiteral("features.temporaryfile is disabled."); + return false; +#endif + } + +private: + const Unit *unit; + quint32 temporaryFlags; + + quint32_le &mutableFlags() const + { + return const_cast<Unit *>(unit)->flags; + } + + template<typename Char> + const Char *data() const + { + Q_STATIC_ASSERT(sizeof(Char) == 1); + const Char *dataPtr; + memcpy(&dataPtr, &unit, sizeof(dataPtr)); + return dataPtr; + } + + quint32 size() const + { + return unit->unitSize; + } +}; + + +} // CompiledData namespace +} // QV4 namespace + +Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h new file mode 100644 index 0000000000..8160bbb748 --- /dev/null +++ b/src/qml/common/qv4staticvalue_p.h @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** 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> + +#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 |