diff options
Diffstat (limited to 'src/qml/common/qv4compileddata_p.h')
-rw-r--r-- | src/qml/common/qv4compileddata_p.h | 1844 |
1 files changed, 1844 insertions, 0 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h new file mode 100644 index 0000000000..c21fc19fa9 --- /dev/null +++ b/src/qml/common/qv4compileddata_p.h @@ -0,0 +1,1844 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#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/qcryptographichash.h> +#include <QtCore/qhash.h> +#include <QtCore/qhashfunctions.h> +#include <QtCore/qlocale.h> +#include <QtCore/qscopeguard.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qurl.h> +#include <QtCore/qvector.h> +#include <QtCore/qversionnumber.h> + +#if QT_CONFIG(temporaryfile) +#include <QtCore/qsavefile.h> +#endif + +#include <private/qendian_p.h> +#include <private/qqmlnullablevalue_p.h> +#include <private/qqmlpropertycachevector_p.h> +#include <private/qqmlrefcount_p.h> +#include <private/qqmltype_p.h> +#include <private/qv4compilationunitmapper_p.h> +#include <private/qv4staticvalue_p.h> + +#include <functional> +#include <limits.h> + +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 0x42 // Change metatype computation of AOT-compiled functions + +class QIODevice; +class QQmlTypeNameCache; +class QQmlType; +class QQmlEngine; +class QQmlPropertyData; +class QQmlScriptData; + +namespace QQmlPrivate { +struct AOTCompiledFunction; +} + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace Heap { +struct Module; +struct String; +struct InternalClass; +}; + +struct Function; +class EvalISelFactory; +class ResolvedTypeReference; + +namespace CompiledData { + +// index is per-object binding index +using BindingPropertyData = QVector<const QQmlPropertyData *>; + +// map from name index +struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*> +{ + bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const; +}; + +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); } + ItemType operator*() {return *operator->();} + 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 +{ + Location() : m_data(QSpecialIntegerBitfieldZero) {} + Location(quint32 l, quint32 c) : Location() + { + m_data.set<LineField>(l); + m_data.set<ColumnField>(c); + Q_ASSERT(m_data.get<LineField>() == l); + Q_ASSERT(m_data.get<ColumnField>() == c); + } + + inline bool operator<(const Location &other) const { + return m_data.get<LineField>() < other.m_data.get<LineField>() + || (m_data.get<LineField>() == other.m_data.get<LineField>() + && m_data.get<ColumnField>() < other.m_data.get<ColumnField>()); + } + + friend size_t qHash(const Location &location, size_t seed = 0) + { + return QT_PREPEND_NAMESPACE(qHash)(location.m_data.data(), seed); + } + + friend bool operator==(const Location &a, const Location &b) + { + return a.m_data.data()== b.m_data.data(); + } + + void set(quint32 line, quint32 column) + { + m_data.set<LineField>(line); + m_data.set<ColumnField>(column); + } + + quint32 line() const { return m_data.get<LineField>(); } + quint32 column() const { return m_data.get<ColumnField>(); } + +private: + using LineField = quint32_le_bitfield_member<0, 20>; + using ColumnField = quint32_le_bitfield_member<20, 12>; + + quint32_le_bitfield_union<LineField, ColumnField> m_data; +}; +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_Sticky = 0x08, + RegExp_Unicode = 0x10, + }; + + RegExp() : m_data(QSpecialIntegerBitfieldZero) {} + RegExp(quint32 flags, quint32 stringIndex) : RegExp() + { + m_data.set<FlagsField>(flags); + m_data.set<StringIndexField>(stringIndex); + } + + quint32 flags() const { return m_data.get<FlagsField>(); } + quint32 stringIndex() const { return m_data.get<StringIndexField>(); } + +private: + using FlagsField = quint32_le_bitfield_member<0, 5>; + using StringIndexField = quint32_le_bitfield_member<5, 27>; + quint32_le_bitfield_union<FlagsField, StringIndexField> m_data; +}; +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 + }; + + enum Mode : unsigned int { + Mode_ForStorage = 0, + Mode_ForCall = 1 + }; + + quint32 type() const { return m_data.get<TypeField>(); } + quint32 nameIndex() const { return m_data.get<NameIndexField>(); } + quint32 mode() const { return m_data.get<ModeField>(); } + + Lookup() : m_data(QSpecialIntegerBitfieldZero) {} + Lookup(Type type, Mode mode, quint32 nameIndex) : Lookup() + { + m_data.set<TypeField>(type); + m_data.set<ModeField>(mode); + m_data.set<NameIndexField>(nameIndex); + } + +private: + using TypeField = quint32_le_bitfield_member<0, 2>; + using ModeField = quint32_le_bitfield_member<2, 1>; + // 1 bit left + using NameIndexField = quint32_le_bitfield_member<4, 28>; + quint32_le_bitfield_union<TypeField, ModeField, NameIndexField> m_data; +}; +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 +{ + JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {} + + void set(quint32 nameOffset, bool isAccessor) + { + m_data.set<NameOffsetField>(nameOffset); + m_data.set<IsAccessorField>(isAccessor ? 1 : 0); + } + + quint32 nameOffset() const { return m_data.get<NameOffsetField>(); } + bool isAccessor() const { return m_data.get<IsAccessorField>() != 0; } + +private: + using NameOffsetField = quint32_le_bitfield_member<0, 31>; + using IsAccessorField = quint32_le_bitfield_member<31, 1>; + quint32_le_bitfield_union<NameOffsetField, IsAccessorField> m_data; +}; +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"); + +struct String +{ + qint32_le size; + + static int calculateSize(const QString &str) { + // we cannot enconuter strings larger than INT_MAX anyway, as such a string + // would already break in other parts of the compilation process + return (sizeof(String) + (int(str.size()) + 1) * sizeof(quint16) + 7) & ~0x7; + } +}; + +static_assert (sizeof (String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct CodeOffsetToLineAndStatement { + quint32_le codeOffset; + qint32_le line; // signed because debug instructions get negative line numbers + quint32_le statement; +}; +static_assert(sizeof(CodeOffsetToLineAndStatement) == 12, "CodeOffsetToLineAndStatement 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 NamedBuiltin: unsigned int { + Void, Var, Int, Bool, Real, String, Url, DateTime, RegExp +}; + +enum class CommonType : unsigned int { + // Actual named builtins + Void = uint(NamedBuiltin::Void), + Var = uint(NamedBuiltin::Var), + Int = uint(NamedBuiltin::Int), + Bool = uint(NamedBuiltin::Bool), + Real = uint(NamedBuiltin::Real), + String = uint(NamedBuiltin::String), + Url = uint(NamedBuiltin::Url), + DateTime = uint(NamedBuiltin::DateTime), + RegExp = uint(NamedBuiltin::RegExp), + + // Optimization for very common other types + Time, Date, Rect, Point, Size, + + // No type specified or not recognized + Invalid +}; + +struct ParameterType +{ + enum Flag { + NoFlag = 0x0, + Common = 0x1, + List = 0x2, + }; + Q_DECLARE_FLAGS(Flags, Flag); + + void set(Flags flags, quint32 typeNameIndexOrCommonType) + { + m_data.set<IsListField>(flags.testFlag(List) ? 1 : 0); + m_data.set<IndexIsCommonTypeField>(flags.testFlag(Common) ? 1 : 0); + m_data.set<TypeNameIndexOrCommonTypeField>(typeNameIndexOrCommonType); + } + + bool indexIsCommonType() const + { + return m_data.get<IndexIsCommonTypeField>() != 0; + } + + bool isList() const + { + return m_data.get<IsListField>() != 0; + } + + quint32 typeNameIndexOrCommonType() const + { + return m_data.get<TypeNameIndexOrCommonTypeField>(); + } + +private: + using IndexIsCommonTypeField = quint32_le_bitfield_member<0, 1>; + using IsListField = quint32_le_bitfield_member<1, 1>; + using TypeNameIndexOrCommonTypeField = quint32_le_bitfield_member<2, 30>; + quint32_le_bitfield_union<IndexIsCommonTypeField, IsListField, TypeNameIndexOrCommonTypeField> m_data; +}; +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, + IsClosureWrapper = 0x8, + }; + + // 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 nLineAndStatementNumbers; + size_t lineAndStatementNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } + quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers + + quint32_le nRegisters; + Location location; + quint32_le nLabelInfos; + + quint16_le sizeOfLocalTemporalDeadZone; + quint16_le firstTemporalDeadZoneRegister; + quint16_le sizeOfRegisterTemporalDeadZone; + + size_t labelInfosOffset() const + { + return lineAndStatementNumberOffset() + nLineAndStatementNumbers * sizeof(CodeOffsetToLineAndStatement); + } + + // 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 CodeOffsetToLineAndStatement *lineAndStatementNumberTable() const + { + return reinterpret_cast<const CodeOffsetToLineAndStatement *>( + reinterpret_cast<const char *>(this) + lineAndStatementNumberOffset()); + } + + // --- 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 nLinesAndStatements, int nInnerfunctions, + int labelInfoSize, int codeSize) + { + int trailingData = nFormals * sizeof(Parameter) + + (nLocals + nInnerfunctions + labelInfoSize) * sizeof (quint32) + + nLinesAndStatements * sizeof(CodeOffsetToLineAndStatement); + 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 +{ + enum { NoContextIndex = std::numeric_limits<quint32>::max() }; + quint32_le stringIndex; + quint32_le commentIndex; + qint32_le number; + quint32_le contextIndex; +}; +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 Type : 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 Flag : unsigned int { + IsSignalHandlerExpression = 0x1, + IsSignalHandlerObject = 0x2, + IsOnAssignment = 0x4, + InitializerForReadOnlyDeclaration = 0x8, + IsResolvedEnum = 0x10, + IsListItem = 0x20, + IsBindingToAlias = 0x40, + IsDeferredBinding = 0x80, + IsCustomParserBinding = 0x100, + IsFunctionExpression = 0x200, + IsPropertyObserver = 0x400 + }; + Q_DECLARE_FLAGS(Flags, Flag); + + using FlagsField = quint32_le_bitfield_member<0, 16>; + using TypeField = quint32_le_bitfield_member<16, 16>; + quint32_le_bitfield_union<FlagsField, TypeField> flagsAndType; + + void clearFlags() { flagsAndType.set<FlagsField>(0); } + void setFlag(Flag flag) { flagsAndType.set<FlagsField>(flagsAndType.get<FlagsField>() | flag); } + bool hasFlag(Flag flag) const { return Flags(flagsAndType.get<FlagsField>()) & flag; } + Flags flags() const { return Flags(flagsAndType.get<FlagsField>()); } + + void setType(Type type) { flagsAndType.set<TypeField>(type); } + Type type() const { return Type(flagsAndType.get<TypeField>()); } + + 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 hasSignalHandlerBindingFlag() const + { + const Flags bindingFlags = flags(); + return bindingFlags & IsSignalHandlerExpression + || bindingFlags & IsSignalHandlerObject + || bindingFlags & IsPropertyObserver; + } + + bool isValueBinding() const + { + switch (type()) { + case Type_AttachedProperty: + case Type_GroupProperty: + return false; + default: + return !hasSignalHandlerBindingFlag(); + } + } + + bool isValueBindingNoAlias() const { return isValueBinding() && !hasFlag(IsBindingToAlias); } + bool isValueBindingToAlias() const { return isValueBinding() && hasFlag(IsBindingToAlias); } + + bool isSignalHandler() const + { + if (hasSignalHandlerBindingFlag()) { + 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 hasFlag(IsFunctionExpression); } + + //reverse of Lexer::singleEscape() + static QString escapedString(const QString &string) + { + QString tmp = QLatin1String("\""); + for (int i = 0; i < string.size(); ++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 + { + const Binding::Type bindingType = type(); + return bindingType == Type_Translation || bindingType == Type_TranslationById; + } + bool evaluatesToString() const { return type() == Type_String || isTranslationBinding(); } + + bool isNumberBinding() const { return type() == Type_Number; } + + 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 InlineComponent +{ + quint32_le objectIndex; + quint32_le nameIndex; + Location location; +}; + +static_assert(sizeof(InlineComponent) == 12, "InlineComponent 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 +{ +private: + using CommonTypeOrTypeNameIndexField = quint32_le_bitfield_member<0, 28>; + using IsRequiredField = quint32_le_bitfield_member<28, 1>; + using IsCommonTypeField = quint32_le_bitfield_member<29, 1>; + using IsListField = quint32_le_bitfield_member<30, 1>; + using IsReadOnlyField = quint32_le_bitfield_member<31, 1>; + +public: + quint32_le nameIndex; + quint32_le_bitfield_union< + CommonTypeOrTypeNameIndexField, + IsRequiredField, + IsCommonTypeField, + IsListField, + IsReadOnlyField> data; + Location location; + + void setCommonType(CommonType t) + { + data.set<CommonTypeOrTypeNameIndexField>(static_cast<quint32>(t)); + data.set<IsCommonTypeField>(true); + } + + CommonType commonType() const { + if (data.get<IsCommonTypeField>() != 0) + return CommonType(data.get<CommonTypeOrTypeNameIndexField>()); + return CommonType::Invalid; + } + + void setTypeNameIndex(int nameIndex) + { + data.set<CommonTypeOrTypeNameIndexField>(nameIndex); + data.set<IsCommonTypeField>(false); + } + + int typeNameIndex() const + { + return data.get<IsCommonTypeField>() ? -1 : data.get<CommonTypeOrTypeNameIndexField>(); + } + + bool isCommonType() const { return data.get<IsCommonTypeField>(); } + uint commonTypeOrTypeNameIndex() const { return data.get<CommonTypeOrTypeNameIndexField>(); } + + bool isList() const { return data.get<IsListField>(); } + void setIsList(bool isList) { data.set<IsListField>(isList); } + + bool isRequired() const { return data.get<IsRequiredField>(); } + void setIsRequired(bool isRequired) { data.set<IsRequiredField>(isRequired); } + + bool isReadOnly() const { return data.get<IsReadOnlyField>(); } + void setIsReadOnly(bool isReadOnly) { data.set<IsReadOnlyField>(isReadOnly); } +}; +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 RequiredPropertyExtraData { + quint32_le nameIndex; +}; + +static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Alias { +private: + using NameIndexField = quint32_le_bitfield_member<0, 29>; + using FlagsField = quint32_le_bitfield_member<29, 3>; + + // object id index (in QQmlContextData::idValues) + using TargetObjectIdField = quint32_le_bitfield_member<0, 31>; + using AliasToLocalAliasField = quint32_le_bitfield_member<31, 1>; + using IdIndexField = quint32_le_bitfield_member<0, 32>; + +public: + + enum Flag : unsigned int { + IsReadOnly = 0x1, + Resolved = 0x2, + AliasPointsToPointerObject = 0x4 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + quint32_le_bitfield_union<NameIndexField, FlagsField> nameIndexAndFlags; + quint32_le_bitfield_union<IdIndexField, TargetObjectIdField, AliasToLocalAliasField> + idIndexAndTargetObjectIdAndAliasToLocalAlias; + + 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 hasFlag(Flag flag) const + { + return nameIndexAndFlags.get<FlagsField>() & flag; + } + + void setFlag(Flag flag) + { + nameIndexAndFlags.set<FlagsField>(nameIndexAndFlags.get<FlagsField>() | flag); + } + + void clearFlags() + { + nameIndexAndFlags.set<FlagsField>(0); + } + + quint32 nameIndex() const + { + return nameIndexAndFlags.get<NameIndexField>(); + } + + void setNameIndex(quint32 nameIndex) + { + nameIndexAndFlags.set<NameIndexField>(nameIndex); + } + + bool isObjectAlias() const + { + Q_ASSERT(hasFlag(Resolved)); + return encodedMetaPropertyIndex == -1; + } + + quint32 idIndex() const + { + return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<IdIndexField>(); + } + + void setIdIndex(quint32 idIndex) + { + idIndexAndTargetObjectIdAndAliasToLocalAlias.set<IdIndexField>(idIndex); + } + + + bool isAliasToLocalAlias() const + { + return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<AliasToLocalAliasField>(); + } + + void setIsAliasToLocalAlias(bool isAliasToLocalAlias) + { + idIndexAndTargetObjectIdAndAliasToLocalAlias.set<AliasToLocalAliasField>(isAliasToLocalAlias); + } + + quint32 targetObjectId() const + { + return idIndexAndTargetObjectIdAndAliasToLocalAlias.get<TargetObjectIdField>(); + } + + void setTargetObjectId(quint32 targetObjectId) + { + idIndexAndTargetObjectIdAndAliasToLocalAlias.set<TargetObjectIdField>(targetObjectId); + } +}; +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 +{ +private: + using FlagsField = quint32_le_bitfield_member<0, 15>; + using DefaultPropertyIsAliasField = quint32_le_bitfield_member<15, 1>; + using IdField = quint32_le_bitfield_member<16, 16, qint32>; +public: + enum Flag : 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, + IsInlineComponentRoot = 0x8, + IsPartOfInlineComponent = 0x10 + }; + Q_DECLARE_FLAGS(Flags, Flag); + + // 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; + quint32_le_bitfield_union<FlagsField, DefaultPropertyIsAliasField, IdField> + flagsAndDefaultPropertyIsAliasAndId; + 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; + quint32_le offsetToInlineComponents; + quint16_le nInlineComponents; + quint32_le offsetToRequiredPropertyExtraData; + quint16_le nRequiredPropertyExtraData; +// Function[] +// Property[] +// Signal[] +// Binding[] +// InlineComponent[] +// RequiredPropertyExtraData[] + + Flags flags() const + { + return Flags(flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>()); + } + + bool hasFlag(Flag flag) const + { + return flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() & flag; + } + + void setFlag(Flag flag) + { + flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>( + flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() | flag); + } + + void setFlags(Flags flags) + { + flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(flags); + } + + bool hasAliasAsDefaultProperty() const + { + return flagsAndDefaultPropertyIsAliasAndId.get<DefaultPropertyIsAliasField>(); + } + + void setHasAliasAsDefaultProperty(bool defaultAlias) + { + flagsAndDefaultPropertyIsAliasAndId.set<DefaultPropertyIsAliasField>(defaultAlias); + } + + qint32 objectId() const + { + return flagsAndDefaultPropertyIsAliasAndId.get<IdField>(); + } + + void setObjectId(qint32 id) + { + flagsAndDefaultPropertyIsAliasAndId.set<IdField>(id); + } + + + static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData) + { + return ( sizeof(Object) + + nFunctions * sizeof(quint32) + + nProperties * sizeof(Property) + + nAliases * sizeof(Alias) + + nEnums * sizeof(quint32) + + nSignals * sizeof(quint32) + + nBindings * sizeof(Binding) + + nNamedObjectsInComponent * sizeof(int) + + nInlineComponents * sizeof(InlineComponent) + + nRequiredPropertyExtraData * sizeof(RequiredPropertyExtraData) + + 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 InlineComponent *inlineComponentAt(int idx) const + { + return inlineComponentTable() + idx; + } + + const quint32_le *namedObjectsInComponentTable() const + { + return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); + } + + const InlineComponent *inlineComponentTable() const + { + return reinterpret_cast<const InlineComponent*>(reinterpret_cast<const char *>(this) + offsetToInlineComponents); + } + + const RequiredPropertyExtraData *requiredPropertyExtraDataAt(int idx) const + { + return requiredPropertyExtraDataTable() + idx; + } + + const RequiredPropertyExtraData *requiredPropertyExtraDataTable() const + { + return reinterpret_cast<const RequiredPropertyExtraData*>(reinterpret_cast<const char *>(this) + offsetToRequiredPropertyExtraData); + } + + // --- 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; } + int bindingCount() const { return 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); } + + typedef TableIterator<InlineComponent, Object, &Object::inlineComponentAt> InlineComponentIterator; + InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);} + InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);} + + typedef TableIterator<RequiredPropertyExtraData, Object, &Object::requiredPropertyExtraDataAt> RequiredPropertyExtraDataIterator; + RequiredPropertyExtraDataIterator requiredPropertyExtraDataBegin() const {return RequiredPropertyExtraDataIterator(this, 0); } + RequiredPropertyExtraDataIterator requiredPropertyExtraDataEnd() const {return RequiredPropertyExtraDataIterator(this, nRequiredPropertyExtraData); } + + int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } + // --- +}; +static_assert(sizeof(Object) == 84, "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, + ImportInlineComponent = 0x4 + }; + quint32_le type; + + quint32_le uriIndex; + quint32_le qualifierIndex; + + Location location; + QTypeRevision version; + quint16_le reserved; + + Import() + { + type = 0; uriIndex = 0; qualifierIndex = 0; version = QTypeRevision::zero(); reserved = 0; + } +}; +static_assert(sizeof(Import) == 20, "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 + IsStrict = 0x40, + ListPropertyAssignReplaceIfDefault = 0x80, + ListPropertyAssignReplaceIfNotDefault = 0x100, + ListPropertyAssignReplace + = ListPropertyAssignReplaceIfDefault | ListPropertyAssignReplaceIfNotDefault, + ComponentsBound = 0x200, + FunctionSignaturesIgnored = 0x400, + NativeMethodsAcceptThisObject = 0x800, + ValueTypesCopied = 0x1000, + ValueTypesAddressable = 0x2000, + ValueTypesAssertable = 0x4000, + }; + 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(uint idx) const { + Q_ASSERT(idx < 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); + Q_ASSERT(str->size >= 0); + if (str->size == 0) + return QString(); +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + const QChar *characters = reinterpret_cast<const QChar *>(str + 1); + if (flags & StaticData) + return QString::fromRawData(characters, str->size); + 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 quint32_le *translationContextIndex() const{ + if ( translationTableSize == 0) + return nullptr; + return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + + offsetToTranslationTable + + translationTableSize * sizeof(CompiledData::TranslationData)); } + + quint32_le *translationContextIndex() { + if ( translationTableSize == 0) + return nullptr; + return reinterpret_cast<quint32_le*>((reinterpret_cast<char *>(this)) + + offsetToTranslationTable + + translationTableSize * sizeof(CompiledData::TranslationData)); } + + 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); } + + bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const; +}; + +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 const propEnd = obj->propertiesEnd(); + for ( ; prop != propEnd; ++prop) { + if (!prop->isCommonType()) { + TypeReference &r = this->add(prop->commonTypeOrTypeNameIndex(), prop->location); + r.errorWhenNotFound = true; + } + } + + auto binding = obj->bindingsBegin(); + auto const bindingEnd = obj->bindingsEnd(); + for ( ; binding != bindingEnd; ++binding) { + if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty) + this->add(binding->propertyNameIndex, binding->location); + } + + auto ic = obj->inlineComponentsBegin(); + auto const icEnd = obj->inlineComponentsEnd(); + for (; ic != icEnd; ++ic) { + this->add(ic->nameIndex, ic->location); + } + } + + template <typename Iterator> + void collectFromObjects(Iterator it, Iterator end) + { + for (; it != end; ++it) + collectFromObject(*it); + } +}; + +using DependentTypesHasher = std::function<QByteArray()>; + +struct InlineComponentData { + + InlineComponentData() = default; + InlineComponentData( + const QQmlType &qmlType, int objectIndex, int nameIndex, int totalObjectCount, + int totalBindingCount, int totalParserStatusCount) + : qmlType(qmlType) + , objectIndex(objectIndex) + , nameIndex(nameIndex) + , totalObjectCount(totalObjectCount) + , totalBindingCount(totalBindingCount) + , totalParserStatusCount(totalParserStatusCount) + {} + + QQmlType qmlType; + int objectIndex = -1; + int nameIndex = -1; + int totalObjectCount = 0; + int totalBindingCount = 0; + int totalParserStatusCount = 0; +}; + +struct CompilationUnit final : public QQmlRefCounted<CompilationUnit> +{ + Q_DISABLE_COPY_MOVE(CompilationUnit) + + const Unit *data = nullptr; + const QmlUnit *qmlData = nullptr; + QStringList dynamicStrings; + const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions = nullptr; + + // pointers either to data->constants() or little-endian memory copy. + const StaticValue *constants = nullptr; + + std::unique_ptr<CompilationUnitMapper> backingFile; + + int m_totalBindingsCount = 0; // Number of bindings used in this type + int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int m_totalObjectCount = 0; // Number of objects explicitly instantiated + + std::unique_ptr<QString> icRootName; + QHash<QString, InlineComponentData> inlineComponentData; + + // 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; + + ResolvedTypeReferenceMap resolvedTypes; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + + QQmlPropertyCacheVector propertyCaches; + + QQmlType qmlType; + + QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; + +public: + // --- interface for QQmlPropertyCacheCreator + using CompiledObject = const CompiledData::Object; + using CompiledFunction = const CompiledData::Function; + using CompiledBinding = const CompiledData::Binding; + + // Empty dummy. We don't need to do this when loading from cache. + class IdToObjectMap + { + public: + void insert(int, int) {} + void clear() {} + + // We have already checked uniqueness of IDs when creating the CU + bool contains(int) { return false; } + }; + + explicit CompilationUnit(const Unit *unitData, const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions, + const QString &fileName = QString(), const QString &finalUrlString = QString()) + : CompilationUnit(unitData, fileName, finalUrlString) + { + this->aotCompiledFunctions = aotCompiledFunctions; + } + + Q_QML_EXPORT CompilationUnit( + const Unit *unitData = nullptr, const QString &fileName = QString(), + const QString &finalUrlString = QString()); + + Q_QML_EXPORT ~CompilationUnit(); + + 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(uint index) const + { + if (index < data->stringTableSize) + return data->stringAtInternal(index); + + const qsizetype dynamicIndex = index - data->stringTableSize; + Q_ASSERT(dynamicIndex < dynamicStrings.size()); + return dynamicStrings.at(dynamicIndex); + } + + QString fileName() const { return m_fileName; } + QString finalUrlString() const { return m_finalUrlString; } + + QString bindingValueAsString(const CompiledData::Binding *binding) const + { + 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(); + case Binding::Type_TranslationById: + case Binding::Type_Translation: + return stringAt(data->translations()[binding->value.translationDataIndex].stringIndex); + default: + break; + } + return QString(); + } + + QString bindingValueAsScriptString(const CompiledData::Binding *binding) const + { + return (binding->type() == CompiledData::Binding::Type_String) + ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) + : bindingValueAsString(binding); + } + + double bindingValueAsNumber(const CompiledData::Binding *binding) const + { + if (binding->type() != CompiledData::Binding::Type_Number) + return 0.0; + return constants[binding->value.constantValueIndex].doubleValue(); + } + + Q_QML_EXPORT static QString localCacheFilePath(const QUrl &url); + Q_QML_EXPORT bool loadFromDisk( + const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); + Q_QML_EXPORT bool saveToDisk(const QUrl &unitUrl, QString *errorString); + + int importCount() const { return qmlData->nImports; } + const CompiledData::Import *importAt(int index) const { return qmlData->importAt(index); } + + Q_QML_EXPORT QStringList moduleRequests() const; + + // 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.isValid()) + m_url = QUrl(fileName()); + return m_url; + } + + QUrl finalUrl() const + { + if (!m_finalUrl.isValid()) + m_finalUrl = QUrl(finalUrlString()); + return m_finalUrl; + } + + ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } + ResolvedTypeReference *resolvedType(QMetaType type) const; + + QQmlPropertyCache::ConstPtr rootPropertyCache() const + { + return propertyCaches.at(/*root object*/0); + } + + int objectCount() const { return qmlData->nObjects; } + const CompiledObject *objectAt(int index) const { return qmlData->objectAt(index); } + + int totalBindingsCount() const; + int totalParserStatusCount() const; + int totalObjectCount() const; + + int inlineComponentId(const QString &inlineComponentName) const + { + for (uint i = 0; i < qmlData->nObjects; ++i) { + auto *object = qmlData->objectAt(i); + for (auto it = object->inlineComponentsBegin(), end = object->inlineComponentsEnd(); + it != end; ++it) { + if (stringAt(it->nameIndex) == inlineComponentName) + return it->objectIndex; + } + } + return -1; + } + + void finalizeCompositeType(const QQmlType &type); + + bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; + + enum class ListPropertyAssignBehavior { Append, Replace, ReplaceIfNotDefault }; + ListPropertyAssignBehavior listPropertyAssignBehavior() const + { + if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplace) + return ListPropertyAssignBehavior::Replace; + if (unitData()->flags & CompiledData::Unit::ListPropertyAssignReplaceIfNotDefault) + return ListPropertyAssignBehavior::ReplaceIfNotDefault; + return ListPropertyAssignBehavior::Append; + } + + bool ignoresFunctionSignature() const + { + return unitData()->flags & CompiledData::Unit::FunctionSignaturesIgnored; + } + + bool nativeMethodsAcceptThisObjects() const + { + return unitData()->flags & CompiledData::Unit::NativeMethodsAcceptThisObject; + } + + bool valueTypesAreCopied() const + { + return unitData()->flags & CompiledData::Unit::ValueTypesCopied; + } + + bool valueTypesAreAddressable() const + { + return unitData()->flags & CompiledData::Unit::ValueTypesAddressable; + } + + bool valueTypesAreAssertable() const + { + return unitData()->flags & CompiledData::Unit::ValueTypesAssertable; + } + + bool componentsAreBound() const + { + return unitData()->flags & CompiledData::Unit::ComponentsBound; + } + + bool isESModule() const + { + return unitData()->flags & CompiledData::Unit::IsESModule; + } + + bool isSharedLibrary() const + { + return unitData()->flags & CompiledData::Unit::IsSharedLibrary; + } + + 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(unitData(), object, 0); + } + + FunctionIterator objectFunctionsEnd(const CompiledObject *object) const + { + return FunctionIterator(unitData(), object, object->nFunctions); + } + + QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const; + QMetaType metaType() const { return qmlType.typeId(); } + +private: + QString m_fileName; // initialized from data->sourceFileIndex + QString m_finalUrlString; // initialized from data->finalUrlIndex + + mutable QQmlNullableValue<QUrl> m_url; + mutable QQmlNullableValue<QUrl> m_finalUrl; +}; + +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 + { + const quint32_le oldFlags = mutableFlags(); + auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; }); + 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_OPERATORS_FOR_FLAGS(QV4::CompiledData::ParameterType::Flags); +Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif |