diff options
Diffstat (limited to 'src/qml/common/qv4compileddata_p.h')
-rw-r--r-- | src/qml/common/qv4compileddata_p.h | 1327 |
1 files changed, 1327 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..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 |