Revision control

Copy as Markdown

// 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
// Qt-Security score:significant
#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 <optional>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qhash.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qlocale.h>
#include <QtCore/qscopeguard.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qurl.h>
#include <QtCore/qvector.h>
#include <QtCore/qtyperevision.h>
#include <QtCore/qxpfunctional.h>
#if QT_CONFIG(temporaryfile)
#include <QtCore/qsavefile.h>
#endif
#include <private/qendian_p.h>
#include <private/qqmlnullablevalue_p.h>
#include <private/qqmlpropertycachevector_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmltype_p.h>
#include <private/qv4compilationunitmapper_p.h>
#include <private/qv4staticvalue_p.h>
#include <functional>
#include <limits.h>
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
//
// IMPORTANT:
//
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
// Also update the comparison functions in qqmlpreviewdiff.cpp when you change the data structures.
#define QV4_DATA_STRUCTURE_VERSION 0x53 // Stop folding qualified enums at compile time
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 = QList<const QQmlPropertyData *>;
// map from name index
struct ResolvedTypeReferenceMap: public QHash<int, ResolvedTypeReference*>
{
bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const;
};
struct String;
struct Function;
struct Lookup;
struct RegExp;
struct Unit;
template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const>
struct TableIterator
{
TableIterator(const Container *container, int index) : container(container), index(index) {}
const Container *container;
int index;
const ItemType *operator->() const { return (container->*IndexedGetter)(index); }
ItemType operator*() const {return *operator->();}
TableIterator &operator++()
{
++index;
return *this;
}
TableIterator operator++(int)
{
TableIterator result(container, index);
++index;
return result;
}
TableIterator operator+(int offset) const { return TableIterator(container, index + offset); }
TableIterator operator-(int offset) const { return TableIterator(container, index - offset); }
int operator-(TableIterator other) const { return index - other.index; }
bool operator==(const TableIterator &rhs) const { return index == rhs.index; }
bool operator!=(const TableIterator &rhs) const { return index != rhs.index; }
};
struct Location
{
Location() : m_data(QSpecialIntegerBitfieldZero) {}
Location(quint32 l, quint32 c) : Location()
{
m_data.set<LineField>(l);
m_data.set<ColumnField>(c);
Q_ASSERT(m_data.get<LineField>() == l);
Q_ASSERT(m_data.get<ColumnField>() == c);
}
inline bool operator<(const Location &other) const {
return m_data.get<LineField>() < other.m_data.get<LineField>()
|| (m_data.get<LineField>() == other.m_data.get<LineField>()
&& m_data.get<ColumnField>() < other.m_data.get<ColumnField>());
}
friend size_t qHash(const Location &location, size_t seed = 0)
{
return QT_PREPEND_NAMESPACE(qHash)(location.m_data.data(), seed);
}
friend bool comparesEqual(const Location &a, const Location &b) noexcept
{
return a.m_data.data()== b.m_data.data();
}
Q_DECLARE_EQUALITY_COMPARABLE(Location)
void set(quint32 line, quint32 column)
{
m_data.set<LineField>(line);
m_data.set<ColumnField>(column);
}
quint32 line() const { return m_data.get<LineField>(); }
quint32 column() const { return m_data.get<ColumnField>(); }
private:
using LineField = quint32_le_bitfield_member<0, 20>;
using ColumnField = quint32_le_bitfield_member<20, 12>;
quint32_le_bitfield_union<LineField, ColumnField> m_data;
};
static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct RegExp
{
enum Flag : quint8 {
RegExp_NoFlags = 0x0,
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04,
RegExp_Sticky = 0x08,
RegExp_Unicode = 0x10,
};
Q_DECLARE_FLAGS(Flags, Flag);
RegExp() : m_data(QSpecialIntegerBitfieldZero) {}
RegExp(quint32 flags, quint32 stringIndex) : RegExp()
{
m_data.set<FlagsField>(flags);
m_data.set<StringIndexField>(stringIndex);
}
quint32 flags() const { return m_data.get<FlagsField>(); }
quint32 stringIndex() const { return m_data.get<StringIndexField>(); }
private:
using FlagsField = quint32_le_bitfield_member<0, 5>;
using StringIndexField = quint32_le_bitfield_member<5, 27>;
quint32_le_bitfield_union<FlagsField, StringIndexField> m_data;
};
static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Lookup
{
enum Type : unsigned int {
Type_Getter = 0,
Type_Setter = 1,
Type_GlobalGetter = 2,
Type_QmlContextPropertyGetter = 3
};
enum Mode : unsigned int {
Mode_ForStorage = 0,
Mode_ForCall = 1
};
quint32 type() const { return m_data.get<TypeField>(); }
quint32 nameIndex() const { return m_data.get<NameIndexField>(); }
quint32 mode() const { return m_data.get<ModeField>(); }
Lookup() : m_data(QSpecialIntegerBitfieldZero) {}
Lookup(Type type, Mode mode, quint32 nameIndex) : Lookup()
{
m_data.set<TypeField>(type);
m_data.set<ModeField>(mode);
m_data.set<NameIndexField>(nameIndex);
}
private:
using TypeField = quint32_le_bitfield_member<0, 2>;
using ModeField = quint32_le_bitfield_member<2, 1>;
// 1 bit left
using NameIndexField = quint32_le_bitfield_member<4, 28>;
quint32_le_bitfield_union<TypeField, ModeField, NameIndexField> m_data;
};
static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
JSClassMember() : m_data(QSpecialIntegerBitfieldZero) {}
void set(quint32 nameOffset, bool isAccessor)
{
m_data.set<NameOffsetField>(nameOffset);
m_data.set<IsAccessorField>(isAccessor ? 1 : 0);
}
quint32 nameOffset() const { return m_data.get<NameOffsetField>(); }
bool isAccessor() const { return m_data.get<IsAccessorField>() != 0; }
private:
using NameOffsetField = quint32_le_bitfield_member<0, 31>;
using IsAccessorField = quint32_le_bitfield_member<31, 1>;
quint32_le_bitfield_union<NameOffsetField, IsAccessorField> m_data;
};
static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClass
{
quint32_le nMembers;
// JSClassMember[nMembers]
static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct String
{
qint32_le size;
static int calculateSize(const QString &str) {
// we cannot enconuter strings larger than INT_MAX anyway, as such a string
// would already break in other parts of the compilation process
return (sizeof(String) + (int(str.size()) + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
static_assert (sizeof (String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct CodeOffsetToLineAndStatement {
quint32_le codeOffset;
qint32_le line; // signed because debug instructions get negative line numbers
quint32_le statement;
};
static_assert(sizeof(CodeOffsetToLineAndStatement) == 12, "CodeOffsetToLineAndStatement structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Block
{
quint32_le nLocals;
quint32_le localsOffset;
quint16_le sizeOfLocalTemporalDeadZone;
quint16_le padding;
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
static int calculateSize(int nLocals) {
int trailingData = nLocals*sizeof (quint32);
size_t size = align(align(sizeof(Block)) + size_t(trailingData));
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
enum class NamedBuiltin: unsigned int {
Void, Var, Int, Bool, Real, String, Url, DateTime, RegExp
};
enum class CommonType : unsigned int {
// Actual named builtins
Void = uint(NamedBuiltin::Void),
Var = uint(NamedBuiltin::Var),
Int = uint(NamedBuiltin::Int),
Bool = uint(NamedBuiltin::Bool),
Real = uint(NamedBuiltin::Real),
String = uint(NamedBuiltin::String),
Url = uint(NamedBuiltin::Url),
DateTime = uint(NamedBuiltin::DateTime),
RegExp = uint(NamedBuiltin::RegExp),
// Optimization for very common other types
Time, Date, Rect, Point, Size,
// No type specified or not recognized
Invalid
};
struct ParameterType
{
enum Flag {
NoFlag = 0x0,
Common = 0x1,
List = 0x2,
};
Q_DECLARE_FLAGS(Flags, Flag);
void set(Flags flags, quint32 typeNameIndexOrCommonType)
{
m_data.set<IsListField>(flags.testFlag(List) ? 1 : 0);
m_data.set<IndexIsCommonTypeField>(flags.testFlag(Common) ? 1 : 0);
m_data.set<TypeNameIndexOrCommonTypeField>(typeNameIndexOrCommonType);
}
bool indexIsCommonType() const
{
return m_data.get<IndexIsCommonTypeField>() != 0;
}
bool isList() const
{
return m_data.get<IsListField>() != 0;
}
quint32 typeNameIndexOrCommonType() const
{
return m_data.get<TypeNameIndexOrCommonTypeField>();
}
private:
using IndexIsCommonTypeField = quint32_le_bitfield_member<0, 1>;
using IsListField = quint32_le_bitfield_member<1, 1>;
using TypeNameIndexOrCommonTypeField = quint32_le_bitfield_member<2, 30>;
quint32_le_bitfield_union<IndexIsCommonTypeField, IsListField, TypeNameIndexOrCommonTypeField> m_data;
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Parameter
{
quint32_le nameIndex;
ParameterType type;
};
static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
IsArrowFunction = 0x2,
IsGenerator = 0x4,
IsClosureWrapper = 0x8,
};
// Absolute offset into file where the code for this function is located.
quint32_le codeOffset;
quint32_le codeSize;
quint32_le nameIndex;
quint16_le length;
quint16_le nFormals;
quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData.
ParameterType returnType;
quint32_le localsOffset;
quint16_le nLocals;
quint16_le nLineAndStatementNumbers;
size_t lineAndStatementNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
quint32_le nRegisters;
Location location;
quint32_le nLabelInfos;
quint16_le sizeOfLocalTemporalDeadZone;
quint16_le firstTemporalDeadZoneRegister;
quint16_le sizeOfRegisterTemporalDeadZone;
size_t labelInfosOffset() const
{
return lineAndStatementNumberOffset() + nLineAndStatementNumbers * sizeof(CodeOffsetToLineAndStatement);
}
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
const Parameter *formalsTable() const
{
return reinterpret_cast<const Parameter *>(
reinterpret_cast<const char *>(this) + formalsOffset);
}
const quint32_le *localsTable() const
{
return reinterpret_cast<const quint32_le *>(
reinterpret_cast<const char *>(this) + localsOffset);
}
const CodeOffsetToLineAndStatement *lineAndStatementNumberTable() const
{
return reinterpret_cast<const CodeOffsetToLineAndStatement *>(
reinterpret_cast<const char *>(this) + lineAndStatementNumberOffset());
}
// --- QQmlPropertyCacheCreator interface
const Parameter *formalsBegin() const { return formalsTable(); }
const Parameter *formalsEnd() const { return formalsTable() + nFormals; }
// ---
const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); }
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
static int calculateSize(
int nFormals, int nLocals, int nLinesAndStatements, int labelInfoSize, int codeSize)
{
const size_t trailingData = nFormals * sizeof(Parameter)
+ (nLocals + labelInfoSize) * sizeof (quint32)
+ nLinesAndStatements * sizeof(CodeOffsetToLineAndStatement);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
Regular,
Getter,
Setter
};
quint32_le name;
quint32_le type;
quint32_le function;
};
static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Class
{
quint32_le nameIndex;
quint32_le scopeIndex;
quint32_le constructorFunction;
quint32_le nStaticMethods;
quint32_le nMethods;
quint32_le methodTableOffset;
const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
static int calculateSize(int nStaticMethods, int nMethods) {
int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
size_t size = align(sizeof(Class) + trailingData);
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TemplateObject
{
quint32_le size;
static int calculateSize(int size) {
int trailingData = 2 * size * sizeof(quint32_le);
size_t s = align(sizeof(TemplateObject) + trailingData);
Q_ASSERT(s < INT_MAX);
return int(s);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
const quint32_le *stringTable() const {
return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1));
}
uint stringIndexAt(uint i) const {
return stringTable()[i];
}
uint rawStringIndexAt(uint i) const {
return stringTable()[size + i];
}
};
static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct ExportEntry
{
quint32_le exportName;
quint32_le moduleRequest;
quint32_le importName;
quint32_le localName;
Location location;
};
static_assert(sizeof(ExportEntry) == 20, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct ImportEntry
{
quint32_le moduleRequest;
quint32_le importName;
quint32_le localName;
Location location;
};
static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Qml data structures
struct TranslationData
{
enum { NoContextIndex = std::numeric_limits<quint32>::max() };
quint32_le stringIndex;
quint32_le commentIndex;
qint32_le number;
quint32_le contextIndex;
};
static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Binding
{
quint32_le propertyNameIndex;
enum Type : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
Type_String,
Type_Null,
Type_Translation,
Type_TranslationById,
Type_Script,
Type_Object,
Type_AttachedProperty,
Type_GroupedProperty
};
enum Flag : unsigned int {
IsOnAssignment = 0x4,
InitializerForReadOnlyDeclaration = 0x8,
IsListItem = 0x20,
IsFunctionExpression = 0x200
};
Q_DECLARE_FLAGS(Flags, Flag);
using FlagsField = quint32_le_bitfield_member<0, 16>;
using TypeField = quint32_le_bitfield_member<16, 16>;
quint32_le_bitfield_union<FlagsField, TypeField> flagsAndType;
void clearFlags() { flagsAndType.set<FlagsField>(0); }
void setFlag(Flag flag) { flagsAndType.set<FlagsField>(flagsAndType.get<FlagsField>() | flag); }
bool hasFlag(Flag flag) const { return Flags(flagsAndType.get<FlagsField>()) & flag; }
Flags flags() const { return Flags(flagsAndType.get<FlagsField>()); }
void setType(Type type) { flagsAndType.set<TypeField>(type); }
Type type() const { return Type(flagsAndType.get<TypeField>()); }
union {
bool b;
quint32_le constantValueIndex;
quint32_le compiledScriptIndex; // used when Type_Script
quint32_le objectIndex;
quint32_le translationDataIndex; // used when Type_Translation
quint32 nullMarker;
} value;
quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
bool isAttachedProperty() const { return type() == Type_AttachedProperty; }
bool isGroupedProperty() const { return type() == Type_GroupedProperty; }
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;
};
static_assert(sizeof(EnumValue) == 8, "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");
/*
* We aim to minimize the size of a struct as much as possible, while preserving at least 28 bits
* for each index.
*
* Note that Name and Type indices are provided by StringTableGenerator, containing a hashmap
* (and a list) of unique strings within one Compilation Unit. It sounds rather unrealistic to have
* 2^28 (260+ million) unique strings within 1 CU (even if it's some form of global registry), not
* to mention the amount of memory needed to maintain such a StringTableGenerator.
* Therefore, after some preliminary analysis, it seems that even 20 bits
* should be a rather conservative cap.
*
* However it doesn't seem to easily provide many benefits atm other than better (logically) grouped
* unions like, let's say nameIndexAndAttributes.
*
* Atm 32-bit member nameIndexAndVirtSpecifiers looks smth like this:
*
* NameIndexField IsVirtualField IsOverrideField IsFinalField
* |10100000101000111000001110000| 0 | 1 | 0 |
*
*/
struct Property
{
private:
using NameIndexField = quint32_le_bitfield_member<0, 29>;
using IsVirtualField = quint32_le_bitfield_member<29, 1>;
using IsOverrideField = quint32_le_bitfield_member<30, 1>;
using IsFinalField = quint32_le_bitfield_member<31, 1>;
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_bitfield_union<NameIndexField, IsVirtualField, IsOverrideField, IsFinalField>
nameIndexAndVirtSpecifiers;
quint32_le_bitfield_union<
CommonTypeOrTypeNameIndexField,
IsRequiredField,
IsCommonTypeField,
IsListField,
IsReadOnlyField> data;
Location location;
quint32 nameIndex() const { return nameIndexAndVirtSpecifiers.get<NameIndexField>(); }
void setNameIndex(int nameIndex) { nameIndexAndVirtSpecifiers.set<NameIndexField>(nameIndex); }
bool isVirtual() const { return nameIndexAndVirtSpecifiers.get<IsVirtualField>(); }
void setIsVirtual(bool isVirtual)
{
nameIndexAndVirtSpecifiers.set<IsVirtualField>(isVirtual ? 1 : 0);
}
bool isOverride() const { return nameIndexAndVirtSpecifiers.get<IsOverrideField>(); }
void setIsOverride(bool isOverride)
{
nameIndexAndVirtSpecifiers.set<IsOverrideField>(isOverride ? 1 : 0);
}
bool isFinal() const { return nameIndexAndVirtSpecifiers.get<IsFinalField>(); }
void setIsFinal(bool isFinal) { nameIndexAndVirtSpecifiers.set<IsFinalField>(isFinal ? 1 : 0); }
void setCommonType(CommonType t)
{
data.set<CommonTypeOrTypeNameIndexField>(static_cast<quint32>(t));
data.set<IsCommonTypeField>(true);
}
CommonType commonType() const {
if (isCommonType())
return CommonType(data.get<CommonTypeOrTypeNameIndexField>());
return CommonType::Invalid;
}
void setTypeNameIndex(int nameIndex)
{
data.set<CommonTypeOrTypeNameIndexField>(nameIndex);
data.set<IsCommonTypeField>(false);
}
int typeNameIndex() const
{
return data.get<IsCommonTypeField>() ? -1 : data.get<CommonTypeOrTypeNameIndexField>();
}
bool isCommonType() const { return data.get<IsCommonTypeField>(); }
uint commonTypeOrTypeNameIndex() const { return data.get<CommonTypeOrTypeNameIndexField>(); }
bool isList() const { return data.get<IsListField>(); }
void setIsList(bool isList) { data.set<IsListField>(isList); }
bool isRequired() const { return data.get<IsRequiredField>(); }
void setIsRequired(bool isRequired) { data.set<IsRequiredField>(isRequired); }
bool isReadOnly() const { return data.get<IsReadOnlyField>(); }
void setIsReadOnly(bool isReadOnly) { data.set<IsReadOnlyField>(isReadOnly); }
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct RequiredPropertyExtraData {
quint32_le nameIndex;
};
static_assert (sizeof(RequiredPropertyExtraData) == 4, "RequiredPropertyExtraData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias
{
bool isReadOnly() const { return m_nameIndexAndFlags.get<IsReadOnlyField>(); }
void setIsReadOnly(bool isReadOnly) { m_nameIndexAndFlags.set<IsReadOnlyField>(isReadOnly); }
quint32 nameIndex() const { return m_nameIndexAndFlags.get<NameIndexField>(); }
void setNameIndex(quint32 nameIndex)
{
m_nameIndexAndFlags.set<NameIndexField>(nameIndex);
}
quint32 idIndex() const { return m_idIndexField; }
void setIdIndex(quint32 idIndex) { m_idIndexField = idIndex; }
quint32 propertyNameIndex() const { return m_propertyNameIndex; }
void setPropertyNameIndex(quint32 propertyNameIndex)
{
m_propertyNameIndex = propertyNameIndex;
}
Location location() const { return m_location; }
void setLocation(Location location) { m_location = location; }
Location referenceLocation() const { return m_referenceLocation; }
void setReferenceLocation(Location referenceLocation)
{
m_referenceLocation = referenceLocation;
}
private:
using NameIndexField = quint32_le_bitfield_member<0, 31>;
using IsReadOnlyField = quint32_le_bitfield_member<31, 1>;
quint32_le_bitfield_union<NameIndexField, IsReadOnlyField> m_nameIndexAndFlags;
quint32_le m_idIndexField;
quint32_le m_propertyNameIndex; // string index
Location m_location;
Location m_referenceLocation;
};
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
IsInlineComponentRoot = 0x8,
IsPartOfInlineComponent = 0x10
};
Q_DECLARE_FLAGS(Flags, Flag);
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
quint32_le inheritedTypeNameIndex;
quint32_le idNameIndex;
quint32_le_bitfield_union<FlagsField, DefaultPropertyIsAliasField, IdField>
flagsAndDefaultPropertyIsAliasAndId;
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
quint16_le nFunctions;
quint16_le nProperties;
quint32_le offsetToFunctions;
quint32_le offsetToProperties;
quint32_le offsetToAliases;
quint16_le nAliases;
quint16_le nEnums;
quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects
quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
quint16_le nSignals;
quint16_le nBindings;
quint32_le offsetToBindings;
quint32_le nNamedObjectsInComponent;
quint32_le offsetToNamedObjectsInComponent;
Location location;
Location locationOfIdProperty;
quint32_le offsetToInlineComponents;
quint16_le nInlineComponents;
quint32_le offsetToRequiredPropertyExtraData;
quint16_le nRequiredPropertyExtraData;
// Function[]
// Property[]
// Signal[]
// Binding[]
// InlineComponent[]
// RequiredPropertyExtraData[]
Flags flags() const
{
return Flags(flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>());
}
bool hasFlag(Flag flag) const
{
return flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() & flag;
}
void setFlag(Flag flag)
{
flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(
flagsAndDefaultPropertyIsAliasAndId.get<FlagsField>() | flag);
}
void setFlags(Flags flags)
{
flagsAndDefaultPropertyIsAliasAndId.set<FlagsField>(flags);
}
bool hasAliasAsDefaultProperty() const
{
return flagsAndDefaultPropertyIsAliasAndId.get<DefaultPropertyIsAliasField>();
}
void setHasAliasAsDefaultProperty(bool defaultAlias)
{
flagsAndDefaultPropertyIsAliasAndId.set<DefaultPropertyIsAliasField>(defaultAlias);
}
qint32 objectId() const
{
return flagsAndDefaultPropertyIsAliasAndId.get<IdField>();
}
void setObjectId(qint32 id)
{
flagsAndDefaultPropertyIsAliasAndId.set<IdField>(id);
}
static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents, int nRequiredPropertyExtraData)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
+ nProperties * sizeof(Property)
+ nAliases * sizeof(Alias)
+ nEnums * sizeof(quint32)
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ nNamedObjectsInComponent * sizeof(int)
+ nInlineComponents * sizeof(InlineComponent)
+ nRequiredPropertyExtraData * sizeof(RequiredPropertyExtraData)
+ 0x7
) & ~0x7;
}
const quint32_le *functionOffsetTable() const
{
return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
}
const Property *propertyTable() const
{
return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties);
}
const Alias *aliasTable() const
{
return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases);
}
const Binding *bindingTable() const
{
return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings);
}
const Enum *enumAt(int idx) const
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset);
}
const Signal *signalAt(int idx) const
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
const InlineComponent *inlineComponentAt(int idx) const
{
return inlineComponentTable() + idx;
}
const quint32_le *namedObjectsInComponentTable() const
{
return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
}
const InlineComponent *inlineComponentTable() const
{
return reinterpret_cast<const InlineComponent*>(reinterpret_cast<const char *>(this) + offsetToInlineComponents);
}
const RequiredPropertyExtraData *requiredPropertyExtraDataAt(int idx) const
{
return requiredPropertyExtraDataTable() + idx;
}
const RequiredPropertyExtraData *requiredPropertyExtraDataTable() const
{
return reinterpret_cast<const RequiredPropertyExtraData*>(reinterpret_cast<const char *>(this) + offsetToRequiredPropertyExtraData);
}
// --- QQmlPropertyCacheCreator interface
int propertyCount() const { return nProperties; }
int aliasCount() const { return nAliases; }
int enumCount() const { return nEnums; }
int signalCount() const { return nSignals; }
int functionCount() const { return nFunctions; }
int inlineComponentCount() const { return nInlineComponents; }
const Binding *bindingsBegin() const { return bindingTable(); }
const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
int bindingCount() const { return nBindings; }
const Property *propertiesBegin() const { return propertyTable(); }
const Property *propertiesEnd() const { return propertyTable() + nProperties; }
const Alias *aliasesBegin() const { return aliasTable(); }
const Alias *aliasesEnd() const { return aliasTable() + nAliases; }
typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator;
EnumIterator enumsBegin() const { return EnumIterator(this, 0); }
EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); }
typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator;
SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
typedef TableIterator<InlineComponent, Object, &Object::inlineComponentAt> InlineComponentIterator;
InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);}
InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);}
typedef TableIterator<RequiredPropertyExtraData, Object, &Object::requiredPropertyExtraDataAt> RequiredPropertyExtraDataIterator;
RequiredPropertyExtraDataIterator requiredPropertyExtraDataBegin() const {return RequiredPropertyExtraDataIterator(this, 0); }
RequiredPropertyExtraDataIterator requiredPropertyExtraDataEnd() const {return RequiredPropertyExtraDataIterator(this, nRequiredPropertyExtraData); }
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
static_assert(sizeof(Object) == 84, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
enum ImportType : unsigned int {
ImportLibrary = 0x1,
ImportFile = 0x2,
ImportScript = 0x3,
ImportInlineComponent = 0x4
};
quint32_le type;
quint32_le uriIndex;
quint32_le qualifierIndex;
Location location;
QTypeRevision version;
quint16_le reserved;
Import()
{
type = 0; uriIndex = 0; qualifierIndex = 0; version = QTypeRevision::zero(); reserved = 0;
}
};
static_assert(sizeof(Import) == 20, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct QmlUnit
{
quint32_le nImports;
quint32_le offsetToImports;
quint32_le nObjects;
quint32_le offsetToObjects;
const Import *importAt(int idx) const {
return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
}
const Object *objectAt(int idx) const {
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
}
};
static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
static const char magic_str[] = "qv4cdata";
struct Unit
{
// DO NOT CHANGE THESE FIELDS EVER
char magic[8];
quint32_le version;
// END DO NOT CHANGE THESE FIELDS EVER
quint32_le reserved; // For predictable alignment and size. Used to be Qt version.
// A sourceTimeStamp of -1 marks a unit that is validated by source content hashing rather
// than by time stamp. In that case sourceChecksum holds the checksum of the source code and
// is compared against the freshly computed checksum of the source on load. This is used for
// cache files generated at run time, where the source's time stamp may be unreliable (e.g.
// reproducible builds that pin mtimes via SOURCE_DATE_EPOCH). Units generated by qmlcachegen
// at compile time keep their actual time stamp and leave sourceChecksum zeroed.
qint64_le sourceTimeStamp;
quint32_le unitSize; // Size of the Unit and any depending data.
// Kept before md5Checksum, and therefore outside its coverage, so that it can be patched in
// when saving a unit to disk without invalidating md5Checksum.
char sourceChecksum[16];
char md5Checksum[16]; // checksum of all bytes following this field.
char dependencyMD5Checksum[16];
enum : unsigned int {
IsJavascript = 0x1,
StaticData = 0x2, // Unit data persistent in memory?
IsSingleton = 0x4,
IsSharedLibrary = 0x8, // .pragma shared?
IsESModule = 0x10,
PendingTypeCompilation = 0x20, // the QML data structures present are incomplete and require type compilation
IsStrict = 0x40,
ListPropertyAssignReplaceIfDefault = 0x80,
ListPropertyAssignReplaceIfNotDefault = 0x100,
ListPropertyAssignReplace
= ListPropertyAssignReplaceIfDefault | ListPropertyAssignReplaceIfNotDefault,
ComponentsBound = 0x200,
FunctionSignaturesIgnored = 0x400,
NativeMethodsAcceptThisObject = 0x800,
ValueTypesCopied = 0x1000,
ValueTypesAddressable = 0x2000,
ValueTypesAssertable = 0x4000,
};
quint32_le flags;
quint32_le stringTableSize;
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
quint32_le classTableSize;
quint32_le offsetToClassTable;
quint32_le templateObjectTableSize;
quint32_le offsetToTemplateObjectTable;
quint32_le blockTableSize;
quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
quint32_le offsetToRegexpTable;
quint32_le constantTableSize;
quint32_le offsetToConstantTable;
quint32_le jsClassTableSize;
quint32_le offsetToJSClassTable;
quint32_le translationTableSize;
quint32_le offsetToTranslationTable;
quint32_le localExportEntryTableSize;
quint32_le offsetToLocalExportEntryTable;
quint32_le indirectExportEntryTableSize;
quint32_le offsetToIndirectExportEntryTable;
quint32_le starExportEntryTableSize;
quint32_le offsetToStarExportEntryTable;
quint32_le importEntryTableSize;
quint32_le offsetToImportEntryTable;
quint32_le moduleRequestTableSize;
quint32_le offsetToModuleRequestTable;
qint32_le indexOfRootFunction;
quint32_le sourceFileIndex;
quint32_le finalUrlIndex;
quint32_le offsetToQmlUnit;
/* QML specific fields */
const QmlUnit *qmlUnit() const {
return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit);
}
QmlUnit *qmlUnit() {
return reinterpret_cast<QmlUnit *>(reinterpret_cast<char *>(this) + offsetToQmlUnit);
}
bool isSingleton() const {
return flags & Unit::IsSingleton;
}
bool isListPropertyAssignReplace() const {
// ListPropertyAssignReplace is the combination of both replace bits, so it must be tested
// for an exact match.
return (flags & Unit::ListPropertyAssignReplace) == Unit::ListPropertyAssignReplace;
}
bool isListPropertyAssignReplaceIfNotDefault() const {
return flags & Unit::ListPropertyAssignReplaceIfNotDefault;
}
/* end QML specific fields*/
QString stringAtInternal(uint idx) const {
Q_ASSERT(idx < stringTableSize);
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
Q_ASSERT(str->size >= 0);
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
if (flags & StaticData)
return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
QString qstr(str->size, Qt::Uninitialized);
QChar *ch = qstr.data();
for (int i = 0; i < str->size; ++i)
ch[i] = QChar(quint16(characters[i]));
return qstr;
#endif
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); }
const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
const Class *classAt(int idx) const {
const quint32_le *offsetTable = classOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
}
const TemplateObject *templateObjectAt(int idx) const {
const quint32_le *offsetTable = templateObjectOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset);
}
const Block *blockAt(int idx) const {
const quint32_le *offsetTable = blockOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
}
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
const quint64_le *constants() const {
return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
const quint32_le offset = offsetTable[idx];
const char *ptr = reinterpret_cast<const char *>(this) + offset;
const JSClass *klass = reinterpret_cast<const JSClass *>(ptr);
*nMembers = klass->nMembers;
return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
}
const TranslationData *translations() const {
return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
}
const quint32_le *translationContextIndex() const{
if ( translationTableSize == 0)
return nullptr;
return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this))
+ offsetToTranslationTable
+ translationTableSize * sizeof(CompiledData::TranslationData)); }
quint32_le *translationContextIndex() {
if ( translationTableSize == 0)
return nullptr;
return reinterpret_cast<quint32_le*>((reinterpret_cast<char *>(this))
+ offsetToTranslationTable
+ translationTableSize * sizeof(CompiledData::TranslationData)); }
const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
};
static_assert(sizeof(Unit) == 216, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// The runtime data holds what we can only calculate once the types a unit refers to are resolved.
// It is stored separately from the unit, in tables parallel to the unit's objects and bindings.
struct RuntimeBinding
{
enum Flag : quint32 {
IsDeferredBinding = 0x1,
IsCustomParserBinding = 0x2,
IsBindingToAlias = 0x4,
IsSignalHandlerExpression = 0x8,
IsSignalHandlerObject = 0x10,
IsPropertyObserver = 0x20,
IsFoldedEnum = 0x40,
};
quint32_le flags;
qint32_le enumValue; // only valid when IsFoldedEnum is set
};
using RuntimeBindingIndex = quint16_le;
struct RuntimeObject
{
enum Flag : quint32 {
HasDeferredBindings = 0x1,
HasCustomParserBindings = 0x2,
PopulatedByDeferredBinding = 0x4,
};
quint32_le flags;
quint32_le offsetToBindingOrder;
const RuntimeBinding *bindingTable() const
{
return reinterpret_cast<const RuntimeBinding *>(
reinterpret_cast<const char *>(this) + sizeof(RuntimeObject));
}
RuntimeBinding *bindingTable()
{
return reinterpret_cast<RuntimeBinding *>(
reinterpret_cast<char *>(this) + sizeof(RuntimeObject));
}
const RuntimeBindingIndex *bindingOrder() const
{
return reinterpret_cast<const RuntimeBindingIndex *>(
reinterpret_cast<const char *>(this) + offsetToBindingOrder);
}
RuntimeBindingIndex *bindingOrder()
{
return reinterpret_cast<RuntimeBindingIndex *>(
reinterpret_cast<char *>(this) + offsetToBindingOrder);
}
};
struct RuntimeDataBlock
{
quint32_le nObjects;
const RuntimeObject *objectAt(int idx) const
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(
reinterpret_cast<const char *>(this) + sizeof(RuntimeDataBlock));
return reinterpret_cast<const RuntimeObject *>(
reinterpret_cast<const char *>(this) + offsetTable[idx]);
}
RuntimeObject *objectAt(int idx)
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(
reinterpret_cast<const char *>(this) + sizeof(RuntimeDataBlock));
return reinterpret_cast<RuntimeObject *>(
reinterpret_cast<char *>(this) + offsetTable[idx]);
}
};
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 binding = obj->bindingsBegin();
auto const bindingEnd = obj->bindingsEnd();
for ( ; binding != bindingEnd; ++binding) {
if (binding->type() == QV4::CompiledData::Binding::Type_AttachedProperty)
this->add(binding->propertyNameIndex, binding->location);
}
auto ic = obj->inlineComponentsBegin();
auto const icEnd = obj->inlineComponentsEnd();
for (; ic != icEnd; ++ic) {
this->add(ic->nameIndex, ic->location);
}
}
template <typename Iterator>
void collectFromObjects(Iterator it, Iterator end)
{
for (; it != end; ++it)
collectFromObject(*it);
}
};
using DependentTypesHasher = std::function<QByteArray()>;
struct InlineComponentData {
InlineComponentData() = default;
InlineComponentData(const QQmlType &qmlType, int objectIndex, int nameIndex)
: qmlType(qmlType)
, objectIndex(objectIndex)
, nameIndex(nameIndex)
{}
QQmlType qmlType;
int objectIndex = -1;
int nameIndex = -1;
};
struct CompilationUnit final : public QQmlRefCounted<CompilationUnit>
{
Q_DISABLE_COPY_MOVE(CompilationUnit)
const Unit *data = nullptr;
const QmlUnit *qmlData = nullptr;
const RuntimeDataBlock *runtimeData = nullptr;
QByteArray runtimeDataStorage;
QStringList dynamicStrings;
const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions = nullptr;
LookupValidationFn validateLookupSignatures = nullptr;
// pointers either to data->constants() or little-endian memory copy.
const StaticValue *constants = nullptr;
std::unique_ptr<CompilationUnitMapper> backingFile;
QHash<QString, InlineComponentData> inlineComponentData;
// index is object index. This allows fast access to the
// property data when initializing bindings, avoiding expensive
// lookups by string (property name).
QList<BindingPropertyData> bindingPropertyDataPerObject;
ResolvedTypeReferenceMap resolvedTypes;
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QQmlPropertyCacheVector propertyCaches;
QQmlType qmlType;
QList<QQmlRefPointer<QQmlScriptData>> dependentScripts;
public:
// --- interface for QQmlPropertyCacheCreator
using CompiledObject = const CompiledData::Object;
using CompiledFunction = const CompiledData::Function;
using CompiledBinding = const CompiledData::Binding;
// Empty dummy. We don't need to do this when loading from cache.
class IdToObjectMap
{
public:
void insert(int, int) {}
void clear() {}
// We have already checked uniqueness of IDs when creating the CU
bool contains(int) { return false; }
};
explicit CompilationUnit(const Unit *unitData,
const QQmlPrivate::AOTCompiledFunction *aotCompiledFunctions,
LookupValidationFn validateLookupSignatures,
const QString &fileName = QString(),
const QString &finalUrlString = QString())
: CompilationUnit(unitData, fileName, finalUrlString)
{
this->aotCompiledFunctions = aotCompiledFunctions;
this->validateLookupSignatures = validateLookupSignatures;
}
Q_QML_EXPORT CompilationUnit(
const Unit *unitData = nullptr, const QString &fileName = QString(),
const QString &finalUrlString = QString());
Q_QML_EXPORT ~CompilationUnit();
const Unit *unitData() const { return data; }
void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
const QString &fileName = QString(), const QString &finalUrlString = QString())
{
data = unitData;
qmlData = nullptr;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
delete [] constants;
#endif
constants = nullptr;
m_fileName.clear();
m_finalUrlString.clear();
if (!data)
return;
qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize];
const quint64_le *littleEndianConstants = data->constants();
for (uint i = 0; i < data->constantTableSize; ++i)
bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]);
constants = bigEndianConstants;
#else
constants = reinterpret_cast<const StaticValue*>(data->constants());
#endif
m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
QString stringAt(uint index) const
{
if (index < data->stringTableSize)
return data->stringAtInternal(index);
const qsizetype dynamicIndex = index - data->stringTableSize;
Q_ASSERT(dynamicIndex < dynamicStrings.size());
return dynamicStrings.at(dynamicIndex);
}
QString fileName() const { return m_fileName; }
QString finalUrlString() const { return m_finalUrlString; }
QString bindingValueAsString(const CompiledData::Binding *binding) const
{
using namespace CompiledData;
switch (binding->type()) {
case Binding::Type_Script:
case Binding::Type_String:
return stringAt(binding->stringIndex);
case Binding::Type_Null:
return QStringLiteral("null");
case Binding::Type_Boolean:
return binding->value.b ? QStringLiteral("true") : QStringLiteral("false");
case Binding::Type_Number:
return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest);
case Binding::Type_Invalid:
return QString();
case Binding::Type_TranslationById:
case Binding::Type_Translation:
return stringAt(data->translations()[binding->value.translationDataIndex].stringIndex);
default:
break;
}
return QString();
}
QString bindingValueAsScriptString(const CompiledData::Binding *binding) const
{
return (binding->type() == CompiledData::Binding::Type_String)
? CompiledData::Binding::escapedString(stringAt(binding->stringIndex))
: bindingValueAsString(binding);
}
double bindingValueAsNumber(const CompiledData::Binding *binding) const
{
if (binding->type() != CompiledData::Binding::Type_Number)
return 0.0;
return constants[binding->value.constantValueIndex].doubleValue();
}
Q_QML_EXPORT static QString localCacheFilePath(const QUrl &url);
// sourceChecksum is invoked to obtain the checksum of the current source code. It is only
// called when the cached unit is in content-hash mode (sourceTimeStamp == -1), so units
// validated by time stamp never pay the cost of reading and hashing the source.
Q_QML_EXPORT bool loadFromDisk(
const QUrl &url, const QDateTime &sourceTimeStamp,
qxp::function_ref<QByteArray() const> sourceChecksum, QString *errorString);
// saveToDisk always stores the unit in content-hash mode: it sets sourceTimeStamp to -1 and
// writes the result of sourceChecksum() into the unit. It is used for cache files generated
// at run time.
Q_QML_EXPORT bool saveToDisk(
const QUrl &unitUrl, qxp::function_ref<QByteArray() const> sourceChecksum,
QString *errorString) const;
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.isEmpty()
? QQmlPropertyCache::ConstPtr()
: propertyCaches.at(/*root object*/0);
}
int objectCount() const { return qmlData->nObjects; }
// Resolves implicit component wrapper indices (>= objectCount()) to the
// wrapped child object index. Returns the index unchanged for real objects.
int resolvedIndex(int index) const
{
if (index < objectCount())
return index;
const QQmlPropertyData *p = propertyCaches.at(index)->defaultProperty();
Q_ASSERT(p && p->isComponentWrapper());
return p->wrappedObjectIndex();
}
const CompiledObject *objectAt(int index) const
{
return qmlData->objectAt(resolvedIndex(index));
}
const RuntimeObject *runtimeObjectAt(int index) const
{
Q_ASSERT(runtimeData);
return runtimeData->objectAt(resolvedIndex(index));
}
bool objectHasDeferredBindings(int objectIndex) const
{
return runtimeObjectAt(objectIndex)->flags & RuntimeObject::HasDeferredBindings;
}
bool objectHasCustomParserBindings(int objectIndex) const
{
return runtimeObjectAt(objectIndex)->flags & RuntimeObject::HasCustomParserBindings;
}
bool objectIsPopulatedByDeferredBinding(int objectIndex) const
{
return runtimeObjectAt(objectIndex)->flags & RuntimeObject::PopulatedByDeferredBinding;
}
bool bindingIsDeferred(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& RuntimeBinding::IsDeferredBinding;
}
bool bindingIsCustomParser(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& RuntimeBinding::IsCustomParserBinding;
}
bool bindingIsSignalHandlerExpression(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& RuntimeBinding::IsSignalHandlerExpression;
}
bool bindingIsSignalHandlerObject(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& RuntimeBinding::IsSignalHandlerObject;
}
bool bindingIsPropertyObserver(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& RuntimeBinding::IsPropertyObserver;
}
bool bindingIsSignalHandler(int objectIndex, int bindingIndex) const
{
return runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex].flags
& (RuntimeBinding::IsSignalHandlerExpression | RuntimeBinding::IsSignalHandlerObject
| RuntimeBinding::IsPropertyObserver);
}
std::optional<int> bindingFoldedEnum(int objectIndex, int bindingIndex) const
{
const RuntimeBinding &binding = runtimeObjectAt(objectIndex)->bindingTable()[bindingIndex];
if (binding.flags & RuntimeBinding::IsFoldedEnum)
return int(binding.enumValue);
return std::nullopt;
}
const RuntimeBindingIndex *bindingApplicationOrder(int objectIndex) const
{
return runtimeObjectAt(objectIndex)->bindingOrder();
}
void allocateRuntimeData();
RuntimeDataBlock *runtimeDataForWrite()
{
return reinterpret_cast<RuntimeDataBlock *>(runtimeDataStorage.data());
}
// Returns the index of the implicit component wrapper, or -1.
// Scans property caches at indices >= objectCount().
int implicitComponentForObject(int index) const
{
for (int i = objectCount(), end = propertyCaches.count(); i < end; ++i) {
const QQmlPropertyData *p = propertyCaches.at(i)->defaultProperty();
Q_ASSERT(p && p->isComponentWrapper());
if (p->wrappedObjectIndex() == index)
return i;
}
return -1;
}
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
{
const CompiledData::Unit *unit = unitData();
if (unit->isListPropertyAssignReplace())
return ListPropertyAssignBehavior::Replace;
if (unit->isListPropertyAssignReplaceIfNotDefault())
return ListPropertyAssignBehavior::ReplaceIfNotDefault;
return ListPropertyAssignBehavior::Append;
}
bool ignoresFunctionSignature() const
{
return unitData()->flags & CompiledData::Unit::FunctionSignaturesIgnored;
}
bool nativeMethodsAcceptThisObjects() const
{
return unitData()->flags & CompiledData::Unit::NativeMethodsAcceptThisObject;
}
bool valueTypesAreCopied() const
{
return unitData()->flags & CompiledData::Unit::ValueTypesCopied;
}
bool valueTypesAreAddressable() const
{
return unitData()->flags & CompiledData::Unit::ValueTypesAddressable;
}
bool valueTypesAreAssertable() const
{
return unitData()->flags & CompiledData::Unit::ValueTypesAssertable;
}
bool componentsAreBound() const
{
return unitData()->flags & CompiledData::Unit::ComponentsBound;
}
bool isESModule() const
{
return unitData()->flags & CompiledData::Unit::IsESModule;
}
bool isSharedLibrary() const
{
return unitData()->flags & CompiledData::Unit::IsSharedLibrary;
}
struct FunctionIterator
{
FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index)
: unit(unit), object(object), index(index) {}
const CompiledData::Unit *unit;
const CompiledObject *object;
int index;
const CompiledFunction *operator->() const
{
return unit->functionAt(object->functionOffsetTable()[index]);
}
void operator++() { ++index; }
bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
};
FunctionIterator objectFunctionsBegin(const CompiledObject *object) const
{
return FunctionIterator(unitData(), object, 0);
}
FunctionIterator objectFunctionsEnd(const CompiledObject *object) const
{
return FunctionIterator(unitData(), object, object->nFunctions);
}
QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const;
QMetaType metaType() const { return qmlType.typeId(); }
private:
QString m_fileName; // initialized from data->sourceFileIndex
QString m_finalUrlString; // initialized from data->finalUrlIndex
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
};
class SaveableUnitPointer
{
Q_DISABLE_COPY_MOVE(SaveableUnitPointer)
public:
SaveableUnitPointer(const Unit *unit, quint32 temporaryFlags = Unit::StaticData) :
unit(unit),
temporaryFlags(temporaryFlags)
{
}
~SaveableUnitPointer() = default;
template<typename Char>
bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
{
const quint32_le oldFlags = mutableFlags();
auto cleanup = qScopeGuard([this, oldFlags]() { mutableFlags() = oldFlags; });
mutableFlags() |= temporaryFlags;
return writer(data<Char>(), size());
}
static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size,
QString *errorString)
{
#if QT_CONFIG(temporaryfile)
QSaveFile cacheFile(outputFileName);
if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)
|| cacheFile.write(data, size) != size
|| !cacheFile.commit()) {
*errorString = cacheFile.errorString();
return false;
}
errorString->clear();
return true;
#else
Q_UNUSED(outputFileName);
*errorString = QStringLiteral("features.temporaryfile is disabled.");
return false;
#endif
}
private:
const Unit *unit;
quint32 temporaryFlags;
quint32_le &mutableFlags() const
{
return const_cast<Unit *>(unit)->flags;
}
template<typename Char>
const Char *data() const
{
Q_STATIC_ASSERT(sizeof(Char) == 1);
const Char *dataPtr;
memcpy(&dataPtr, &unit, sizeof(dataPtr));
return dataPtr;
}
quint32 size() const
{
return unit->unitSize;
}
};
} // CompiledData namespace
} // QV4 namespace
Q_DECLARE_OPERATORS_FOR_FLAGS(QV4::CompiledData::ParameterType::Flags);
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif