/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include 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 0x23 // Remove trace slots class QIODevice; class QQmlPropertyData; class QQmlTypeNameCache; class QQmlScriptData; class QQmlType; class QQmlEngine; namespace QmlIR { struct Document; } namespace QV4 { namespace Heap { struct Module; }; struct Function; class EvalISelFactory; namespace CompiledData { struct String; struct Function; struct Lookup; struct RegExp; struct Unit; template 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) { } Location &operator=(const QQmlJS::AST::SourceLocation &astLocation); 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(reinterpret_cast(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"); // 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. 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 quint32_le *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast(reinterpret_cast(this) + lineNumberOffset()); } // --- QQmlPropertyCacheCreator interface const quint32_le *formalsBegin() const { return formalsTable(); } const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- const quint32_le *labelInfoTable() const { return reinterpret_cast(reinterpret_cast(this) + labelInfosOffset()); } const char *code() const { return reinterpret_cast(this) + codeOffset; } static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + 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) == 52, "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(reinterpret_cast(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(reinterpret_cast(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 Q_QML_EXPORT 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 Q_QML_PRIVATE_EXPORT 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); } static QString escapedString(const QString &string); bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } double valueAsNumber(const Value *constantTable) const { if (type != Type_Number) return 0.0; return constantTable[value.constantValueIndex].doubleValue(); } bool valueAsBoolean() const { if (type == Type_Boolean) 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(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 Parameter { quint32_le nameIndex; quint32_le type; quint32_le customTypeNameIndex; Location location; }; static_assert(sizeof(Parameter) == 16, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Signal { quint32_le nameIndex; quint32_le nParameters; Location location; // Parameter parameters[1]; const Parameter *parameterAt(int idx) const { return reinterpret_cast(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 { enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color, Font, Time, Date, DateTime, Rect, Point, Size, Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, Custom, CustomList }; enum Flags : unsigned int { IsReadOnly = 0x1 }; quint32_le nameIndex; union { quint32_le_bitfield<0, 31> type; quint32_le_bitfield<31, 1> flags; // readonly }; quint32_le customTypeNameIndex; // If type >= Custom Location location; }; static_assert(sizeof(Property) == 16, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); 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(reinterpret_cast(this) + offsetToFunctions); } const Property *propertyTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToProperties); } const Alias *aliasTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToAliases); } const Binding *bindingTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToBindings); } const Enum *enumAt(int idx) const { const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToEnums); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const Signal *signalAt(int idx) const { const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const quint32_le *namedObjectsInComponentTable() const { return reinterpret_cast(reinterpret_cast(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 EnumIterator; EnumIterator enumsBegin() const { return EnumIterator(this, 0); } EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); } typedef TableIterator 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((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } const Object *objectAt(int idx) const { const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToObjects); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(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"; extern const char qml_compile_hash[QmlCompileHashSpace + 1]; 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. void generateChecksum(); 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; bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const; /* QML specific fields */ const QmlUnit *qmlUnit() const { return reinterpret_cast(reinterpret_cast(this) + offsetToQmlUnit); } QmlUnit *qmlUnit() { return reinterpret_cast(reinterpret_cast(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((reinterpret_cast(this)) + offsetToStringTable); const quint32_le offset = offsetTable[idx]; const String *str = reinterpret_cast(reinterpret_cast(this) + offset); if (str->size == 0) return QString(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN if (flags & StaticData) { const QStringDataPtr holder = { const_cast(reinterpret_cast(str)) }; return QString(holder); } const QChar *characters = reinterpret_cast(str + 1); return QString(characters, str->size); #else const quint16_le *characters = reinterpret_cast(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((reinterpret_cast(this)) + offsetToFunctionTable); } const quint32_le *classOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToClassTable); } const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToTemplateObjectTable); } const quint32_le *blockOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToBlockTable); } const Function *functionAt(int idx) const { const quint32_le *offsetTable = functionOffsetTable(); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const Class *classAt(int idx) const { const quint32_le *offsetTable = classOffsetTable(); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const TemplateObject *templateObjectAt(int idx) const { const quint32_le *offsetTable = templateObjectOffsetTable(); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const Block *blockAt(int idx) const { const quint32_le *offsetTable = blockOffsetTable(); const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } const Lookup *lookupTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToLookupTable); } const RegExp *regexpAt(int index) const { return reinterpret_cast(reinterpret_cast(this) + offsetToRegexpTable + index * sizeof(RegExp)); } const quint64_le *constants() const { return reinterpret_cast(reinterpret_cast(this) + offsetToConstantTable); } const JSClassMember *jsClassAt(int idx, int *nMembers) const { const quint32_le *offsetTable = reinterpret_cast(reinterpret_cast(this) + offsetToJSClassTable); const quint32_le offset = offsetTable[idx]; const char *ptr = reinterpret_cast(this) + offset; const JSClass *klass = reinterpret_cast(ptr); *nMembers = klass->nMembers; return reinterpret_cast(ptr + sizeof(JSClass)); } const TranslationData *translations() const { return reinterpret_cast(reinterpret_cast(this) + offsetToTranslationTable); } const ImportEntry *importEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToImportEntryTable); } const ExportEntry *localExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToLocalExportEntryTable); } const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToIndirectExportEntryTable); } const ExportEntry *starExportEntryTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToStarExportEntryTable); } const quint32_le *moduleRequestTable() const { return reinterpret_cast((reinterpret_cast(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 { TypeReference &add(int nameIndex, const Location &loc) { Iterator it = find(nameIndex); if (it != end()) return *it; return *insert(nameIndex, loc); } template 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->type >= QV4::CompiledData::Property::Custom) { TypeReference &r = this->add(prop->customTypeNameIndex, 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 void collectFromObjects(Iterator it, Iterator end) { for (; it != end; ++it) collectFromObject(*it); } }; using DependentTypesHasher = std::function; // index is per-object binding index typedef QVector BindingPropertyData; // This is how this hooks into the existing structures: struct Q_QML_PRIVATE_EXPORT 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. QV4::Heap::String **runtimeStrings = nullptr; // Array const Value* constants = nullptr; QV4::Value *runtimeRegularExpressions = nullptr; QV4::Heap::InternalClass **runtimeClasses = nullptr; const Value** imports = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout::value); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *)); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *)); Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *)); struct Q_QML_PRIVATE_EXPORT 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()); ~CompilationUnit(); 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()); 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; } void unlink(); private: QString m_fileName; // initialized from data->sourceFileIndex QString m_finalUrlString; // initialized from data->finalUrlIndex Heap::Module *m_module = nullptr; public: bool saveToDisk(const QString &outputFileName, QString *errorString) const; }; } // CompiledData namespace } // QV4 namespace Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE #endif