// 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 #include #include #include #include #include #include #include #include #include #include #if QT_CONFIG(temporaryfile) #include #endif #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 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; // map from name index struct ResolvedTypeReferenceMap: public QHash { bool addToHash(QCryptographicHash *hash, QHash *checksums) const; }; 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); } 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(l); m_data.set(c); Q_ASSERT(m_data.get() == l); Q_ASSERT(m_data.get() == c); } inline bool operator<(const Location &other) const { return m_data.get() < other.m_data.get() || (m_data.get() == other.m_data.get() && m_data.get() < other.m_data.get()); } 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(line); m_data.set(column); } quint32 line() const { return m_data.get(); } quint32 column() const { return m_data.get(); } private: using LineField = quint32_le_bitfield_member<0, 20>; using ColumnField = quint32_le_bitfield_member<20, 12>; quint32_le_bitfield_union 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(flags); m_data.set(stringIndex); } quint32 flags() const { return m_data.get(); } quint32 stringIndex() const { return m_data.get(); } private: using FlagsField = quint32_le_bitfield_member<0, 5>; using StringIndexField = quint32_le_bitfield_member<5, 27>; quint32_le_bitfield_union 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(); } quint32 nameIndex() const { return m_data.get(); } quint32 mode() const { return m_data.get(); } Lookup() : m_data(QSpecialIntegerBitfieldZero) {} Lookup(Type type, Mode mode, quint32 nameIndex) : Lookup() { m_data.set(type); m_data.set(mode); m_data.set(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 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(nameOffset); m_data.set(isAccessor ? 1 : 0); } quint32 nameOffset() const { return m_data.get(); } bool isAccessor() const { return m_data.get() != 0; } private: using NameOffsetField = quint32_le_bitfield_member<0, 31>; using IsAccessorField = quint32_le_bitfield_member<31, 1>; quint32_le_bitfield_union 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(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"); 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(flags.testFlag(List) ? 1 : 0); m_data.set(flags.testFlag(Common) ? 1 : 0); m_data.set(typeNameIndexOrCommonType); } bool indexIsCommonType() const { return m_data.get() != 0; } bool isList() const { return m_data.get() != 0; } quint32 typeNameIndexOrCommonType() const { return m_data.get(); } 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 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( reinterpret_cast(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast( reinterpret_cast(this) + localsOffset); } const CodeOffsetToLineAndStatement *lineAndStatementNumberTable() const { return reinterpret_cast( reinterpret_cast(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(reinterpret_cast(this) + labelInfosOffset()); } const char *code() const { return reinterpret_cast(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(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 TranslationData { enum { NoContextIndex = std::numeric_limits::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 flagsAndType; void clearFlags() { flagsAndType.set(0); } void setFlag(Flag flag) { flagsAndType.set(flagsAndType.get() | flag); } bool hasFlag(Flag flag) const { return Flags(flagsAndType.get()) & flag; } Flags flags() const { return Flags(flagsAndType.get()); } void setType(Type type) { flagsAndType.set(type); } Type type() const { return Type(flagsAndType.get()); } 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(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(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(static_cast(t)); data.set(true); } CommonType commonType() const { if (data.get() != 0) return CommonType(data.get()); return CommonType::Invalid; } void setTypeNameIndex(int nameIndex) { data.set(nameIndex); data.set(false); } int typeNameIndex() const { return data.get() ? -1 : data.get(); } bool isCommonType() const { return data.get(); } uint commonTypeOrTypeNameIndex() const { return data.get(); } bool isList() const { return data.get(); } void setIsList(bool isList) { data.set(isList); } bool isRequired() const { return data.get(); } void setIsRequired(bool isRequired) { data.set(isRequired); } bool isReadOnly() const { return data.get(); } void setIsReadOnly(bool isReadOnly) { data.set(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 nameIndexAndFlags; quint32_le_bitfield_union 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() & flag; } void setFlag(Flag flag) { nameIndexAndFlags.set(nameIndexAndFlags.get() | flag); } void clearFlags() { nameIndexAndFlags.set(0); } quint32 nameIndex() const { return nameIndexAndFlags.get(); } void setNameIndex(quint32 nameIndex) { nameIndexAndFlags.set(nameIndex); } bool isObjectAlias() const { Q_ASSERT(hasFlag(Resolved)); return encodedMetaPropertyIndex == -1; } quint32 idIndex() const { return idIndexAndTargetObjectIdAndAliasToLocalAlias.get(); } void setIdIndex(quint32 idIndex) { idIndexAndTargetObjectIdAndAliasToLocalAlias.set(idIndex); } bool isAliasToLocalAlias() const { return idIndexAndTargetObjectIdAndAliasToLocalAlias.get(); } void setIsAliasToLocalAlias(bool isAliasToLocalAlias) { idIndexAndTargetObjectIdAndAliasToLocalAlias.set(isAliasToLocalAlias); } quint32 targetObjectId() const { return idIndexAndTargetObjectIdAndAliasToLocalAlias.get(); } void setTargetObjectId(quint32 targetObjectId) { idIndexAndTargetObjectIdAndAliasToLocalAlias.set(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 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()); } bool hasFlag(Flag flag) const { return flagsAndDefaultPropertyIsAliasAndId.get() & flag; } void setFlag(Flag flag) { flagsAndDefaultPropertyIsAliasAndId.set( flagsAndDefaultPropertyIsAliasAndId.get() | flag); } void setFlags(Flags flags) { flagsAndDefaultPropertyIsAliasAndId.set(flags); } bool hasAliasAsDefaultProperty() const { return flagsAndDefaultPropertyIsAliasAndId.get(); } void setHasAliasAsDefaultProperty(bool defaultAlias) { flagsAndDefaultPropertyIsAliasAndId.set(defaultAlias); } qint32 objectId() const { return flagsAndDefaultPropertyIsAliasAndId.get(); } void setObjectId(qint32 id) { flagsAndDefaultPropertyIsAliasAndId.set(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(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 InlineComponent *inlineComponentAt(int idx) const { return inlineComponentTable() + idx; } const quint32_le *namedObjectsInComponentTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); } const InlineComponent *inlineComponentTable() const { return reinterpret_cast(reinterpret_cast(this) + offsetToInlineComponents); } const RequiredPropertyExtraData *requiredPropertyExtraDataAt(int idx) const { return requiredPropertyExtraDataTable() + idx; } const RequiredPropertyExtraData *requiredPropertyExtraDataTable() const { return reinterpret_cast(reinterpret_cast(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 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); } typedef TableIterator InlineComponentIterator; InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);} InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);} typedef TableIterator 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((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"; 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, }; 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(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(uint idx) const { Q_ASSERT(idx < 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); Q_ASSERT(str->size >= 0); if (str->size == 0) return QString(); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN const QChar *characters = reinterpret_cast(str + 1); if (flags & StaticData) return QString::fromRawData(characters, str->size); 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 quint32_le *translationContextIndex() const{ if ( translationTableSize == 0) return nullptr; return reinterpret_cast((reinterpret_cast(this)) + offsetToTranslationTable + translationTableSize * sizeof(CompiledData::TranslationData)); } quint32_le *translationContextIndex() { if ( translationTableSize == 0) return nullptr; return reinterpret_cast((reinterpret_cast(this)) + offsetToTranslationTable + translationTableSize * sizeof(CompiledData::TranslationData)); } 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); } 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 { 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 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 void collectFromObjects(Iterator it, Iterator end) { for (; it != end; ++it) collectFromObject(*it); } }; using DependentTypesHasher = std::function; 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 { 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 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 icRootName; QHash inlineComponentData; // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive // lookups by string (property name). QVector bindingPropertyDataPerObject; ResolvedTypeReferenceMap resolvedTypes; QQmlRefPointer typeNameCache; QQmlPropertyCacheVector propertyCaches; QQmlType qmlType; QVector> 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(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 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 m_url; mutable QQmlNullableValue 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 bool saveToDisk(const std::function &writer) const { const quint32_le oldFlags = mutableFlags(); auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; }); mutableFlags() |= temporaryFlags; return writer(data(), 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)->flags; } template 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