blob: 1f5ad9bd86e08e18d128f2d1e758694fda6712e0 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QV4COMPILEDDATA_P_H
#define QV4COMPILEDDATA_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <functional>
#include <QtCore/qstring.h>
#include <QtCore/qscopeguard.h>
#include <QtCore/qvector.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qhash.h>
#if QT_CONFIG(temporaryfile)
#include <QtCore/qsavefile.h>
#endif
#include <private/qendian_p.h>
#include <private/qv4staticvalue_p.h>
#include <functional>
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
//
// IMPORTANT:
//
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
#define QV4_DATA_STRUCTURE_VERSION 0x25 // Extend size of "register" count (nRegisters)
class QIODevice;
class QQmlTypeNameCache;
class QQmlType;
class QQmlEngine;
namespace QmlIR {
struct Document;
}
namespace QV4 {
namespace Heap {
struct Module;
struct String;
struct InternalClass;
};
struct Function;
class EvalISelFactory;
namespace CompiledData {
struct String;
struct Function;
struct Lookup;
struct RegExp;
struct Unit;
template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const>
struct TableIterator
{
TableIterator(const Container *container, int index) : container(container), index(index) {}
const Container *container;
int index;
const ItemType *operator->() { return (container->*IndexedGetter)(index); }
void operator++() { ++index; }
bool operator==(const TableIterator &rhs) const { return index == rhs.index; }
bool operator!=(const TableIterator &rhs) const { return index != rhs.index; }
};
struct Location
{
union {
quint32 _dummy;
quint32_le_bitfield<0, 20> line;
quint32_le_bitfield<20, 12> column;
};
Location() : _dummy(0) { }
inline bool operator<(const Location &other) const {
return line < other.line ||
(line == other.line && column < other.column);
}
};
static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct RegExp
{
enum Flags : unsigned int {
RegExp_NoFlags = 0x0,
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04,
RegExp_Unicode = 0x08,
RegExp_Sticky = 0x10
};
union {
quint32 _dummy;
quint32_le_bitfield<0, 5> flags;
quint32_le_bitfield<5, 27> stringIndex;
};
RegExp() : _dummy(0) { }
};
static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Lookup
{
enum Type : unsigned int {
Type_Getter = 0,
Type_Setter = 1,
Type_GlobalGetter = 2,
Type_QmlContextPropertyGetter = 3
};
union {
quint32 _dummy;
quint32_le_bitfield<0, 4> type_and_flags;
quint32_le_bitfield<4, 28> nameIndex;
};
Lookup() : _dummy(0) { }
};
static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
union {
quint32 _dummy;
quint32_le_bitfield<0, 31> nameOffset;
quint32_le_bitfield<31, 1> isAccessor;
};
JSClassMember() : _dummy(0) { }
};
static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClass
{
quint32_le nMembers;
// JSClassMember[nMembers]
static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
// from a file must be castable to a QStringData regardless of the pointer size. With the first
// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
// ptrdiff_t and thus variable in size.
// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
// the same value.
struct String
{
qint32_le refcount; // -1
qint32_le size;
quint32_le allocAndCapacityReservedFlag; // 0
quint32_le offsetOn32Bit;
quint64_le offsetOn64Bit;
// uint16 strdata[]
static int calculateSize(const QString &str) {
return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Ensure compatibility with QString
static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
#if QT_POINTER_SIZE == 8
static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
#else
static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
#endif
struct CodeOffsetToLine {
quint32_le codeOffset;
quint32_le line;
};
static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Block
{
quint32_le nLocals;
quint32_le localsOffset;
quint16_le sizeOfLocalTemporalDeadZone;
quint16_le padding;
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
static int calculateSize(int nLocals) {
int trailingData = nLocals*sizeof (quint32);
size_t size = align(align(sizeof(Block)) + size_t(trailingData));
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
enum class BuiltinType : unsigned int {
Var = 0, Variant, Int, Bool, Real, String, Url, Color,
Font, Time, Date, DateTime, Rect, Point, Size,
Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin
};
struct ParameterType
{
union {
quint32 _dummy;
quint32_le_bitfield<0, 1> indexIsBuiltinType;
quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType;
};
};
static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Parameter
{
quint32_le nameIndex;
ParameterType type;
};
static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
IsArrowFunction = 0x2,
IsGenerator = 0x4
};
// Absolute offset into file where the code for this function is located.
quint32_le codeOffset;
quint32_le codeSize;
quint32_le nameIndex;
quint16_le length;
quint16_le nFormals;
quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData.
ParameterType returnType;
quint32_le localsOffset;
quint16_le nLocals;
quint16_le nLineNumbers;
size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
quint32_le nRegisters;
Location location;
quint32_le nLabelInfos;
quint16_le sizeOfLocalTemporalDeadZone;
quint16_le firstTemporalDeadZoneRegister;
quint16_le sizeOfRegisterTemporalDeadZone;
size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
const Parameter *formalsTable() const { return reinterpret_cast<const Parameter *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
// --- QQmlPropertyCacheCreator interface
const Parameter *formalsBegin() const { return formalsTable(); }
const Parameter *formalsEnd() const { return formalsTable() + nFormals; }
// ---
const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); }
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
+ nLines*sizeof(CodeOffsetToLine);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
Regular,
Getter,
Setter
};
quint32_le name;
quint32_le type;
quint32_le function;
};
static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Class
{
quint32_le nameIndex;
quint32_le scopeIndex;
quint32_le constructorFunction;
quint32_le nStaticMethods;
quint32_le nMethods;
quint32_le methodTableOffset;
const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
static int calculateSize(int nStaticMethods, int nMethods) {
int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
size_t size = align(sizeof(Class) + trailingData);
Q_ASSERT(size < INT_MAX);
return int(size);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TemplateObject
{
quint32_le size;
static int calculateSize(int size) {
int trailingData = 2 * size * sizeof(quint32_le);
size_t s = align(sizeof(TemplateObject) + trailingData);
Q_ASSERT(s < INT_MAX);
return int(s);
}
static size_t align(size_t a) {
return (a + 7) & ~size_t(7);
}
const quint32_le *stringTable() const {
return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1));
}
uint stringIndexAt(uint i) const {
return stringTable()[i];
}
uint rawStringIndexAt(uint i) const {
return stringTable()[size + i];
}
};
static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct ExportEntry
{
quint32_le exportName;
quint32_le moduleRequest;
quint32_le importName;
quint32_le localName;
Location location;
};
static_assert(sizeof(ExportEntry) == 20, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct ImportEntry
{
quint32_le moduleRequest;
quint32_le importName;
quint32_le localName;
Location location;
};
static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Qml data structures
struct TranslationData
{
quint32_le stringIndex;
quint32_le commentIndex;
qint32_le number;
quint32_le padding;
};
static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Binding
{
quint32_le propertyNameIndex;
enum ValueType : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
Type_String,
Type_Null,
Type_Translation,
Type_TranslationById,
Type_Script,
Type_Object,
Type_AttachedProperty,
Type_GroupProperty
};
enum Flags : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
InitializerForReadOnlyDeclaration = 0x8,
IsResolvedEnum = 0x10,
IsListItem = 0x20,
IsBindingToAlias = 0x40,
IsDeferredBinding = 0x80,
IsCustomParserBinding = 0x100,
IsFunctionExpression = 0x200
};
union {
quint32_le_bitfield<0, 16> flags;
quint32_le_bitfield<16, 16> type;
};
union {
bool b;
quint32_le constantValueIndex;
quint32_le compiledScriptIndex; // used when Type_Script
quint32_le objectIndex;
quint32_le translationDataIndex; // used when Type_Translation
quint32 nullMarker;
} value;
quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
bool isValueBinding() const
{
if (type == Type_AttachedProperty
|| type == Type_GroupProperty)
return false;
if (flags & IsSignalHandlerExpression
|| flags & IsSignalHandlerObject)
return false;
return true;
}
bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); }
bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); }
bool isSignalHandler() const
{
if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isAttachedProperty());
Q_ASSERT(!isGroupProperty());
return true;
}
return false;
}
bool isAttachedProperty() const
{
if (type == Type_AttachedProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isGroupProperty());
return true;
}
return false;
}
bool isGroupProperty() const
{
if (type == Type_GroupProperty) {
Q_ASSERT(!isValueBinding());
Q_ASSERT(!isSignalHandler());
Q_ASSERT(!isAttachedProperty());
return true;
}
return false;
}
bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
//reverse of Lexer::singleEscape()
static QString escapedString(const QString &string)
{
QString tmp = QLatin1String("\"");
for (int i = 0; i < string.length(); ++i) {
const QChar &c = string.at(i);
switch (c.unicode()) {
case 0x08:
tmp += QLatin1String("\\b");
break;
case 0x09:
tmp += QLatin1String("\\t");
break;
case 0x0A:
tmp += QLatin1String("\\n");
break;
case 0x0B:
tmp += QLatin1String("\\v");
break;
case 0x0C:
tmp += QLatin1String("\\f");
break;
case 0x0D:
tmp += QLatin1String("\\r");
break;
case 0x22:
tmp += QLatin1String("\\\"");
break;
case 0x27:
tmp += QLatin1String("\\\'");
break;
case 0x5C:
tmp += QLatin1String("\\\\");
break;
default:
tmp += c;
break;
}
}
tmp += QLatin1Char('\"');
return tmp;
}
bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
bool valueAsBoolean() const
{
if (type == Type_Boolean)
return value.b;
return false;
}
};
static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct EnumValue
{
quint32_le nameIndex;
qint32_le value;
Location location;
};
static_assert(sizeof(EnumValue) == 12, "EnumValue structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Enum
{
quint32_le nameIndex;
quint32_le nEnumValues;
Location location;
const EnumValue *enumValueAt(int idx) const {
return reinterpret_cast<const EnumValue*>(this + 1) + idx;
}
static int calculateSize(int nEnumValues) {
return (sizeof(Enum)
+ nEnumValues * sizeof(EnumValue)
+ 7) & ~0x7;
}
// --- QQmlPropertyCacheCreatorInterface
const EnumValue *enumValuesBegin() const { return enumValueAt(0); }
const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); }
int enumValueCount() const { return nEnumValues; }
// ---
};
static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Signal
{
quint32_le nameIndex;
quint32_le nParameters;
Location location;
// Parameter parameters[1];
const Parameter *parameterAt(int idx) const {
return reinterpret_cast<const Parameter*>(this + 1) + idx;
}
static int calculateSize(int nParameters) {
return (sizeof(Signal)
+ nParameters * sizeof(Parameter)
+ 7) & ~0x7;
}
// --- QQmlPropertyCacheCceatorInterface
const Parameter *parametersBegin() const { return parameterAt(0); }
const Parameter *parametersEnd() const { return parameterAt(nParameters); }
int parameterCount() const { return nParameters; }
// ---
};
static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Property
{
quint32_le nameIndex;
union {
quint32_le_bitfield<0, 29> builtinTypeOrTypeNameIndex;
quint32_le_bitfield<29, 1> isBuiltinType;
quint32_le_bitfield<30, 1> isList;
quint32_le_bitfield<31, 1> isReadOnly;
};
Location location;
void setBuiltinType(BuiltinType t)
{
builtinTypeOrTypeNameIndex = static_cast<quint32>(t);
isBuiltinType = true;
}
BuiltinType builtinType() const {
if (isBuiltinType)
return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex));
return BuiltinType::InvalidBuiltin;
}
void setCustomType(int nameIndex)
{
builtinTypeOrTypeNameIndex = nameIndex;
isBuiltinType = false;
}
};
static_assert(sizeof(Property) == 12, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias {
enum Flags : unsigned int {
IsReadOnly = 0x1,
Resolved = 0x2,
AliasPointsToPointerObject = 0x4
};
union {
quint32_le_bitfield<0, 29> nameIndex;
quint32_le_bitfield<29, 3> flags;
};
union {
quint32_le idIndex; // string index
quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
quint32_le_bitfield<31, 1> aliasToLocalAlias;
};
union {
quint32_le propertyNameIndex; // string index
qint32_le encodedMetaPropertyIndex;
quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
};
Location location;
Location referenceLocation;
bool isObjectAlias() const {
Q_ASSERT(flags & Resolved);
return encodedMetaPropertyIndex == -1;
}
};
static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Object
{
enum Flags : unsigned int {
NoFlag = 0x0,
IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
HasDeferredBindings = 0x2, // any of the bindings are deferred
HasCustomParserBindings = 0x4
};
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
quint32_le inheritedTypeNameIndex;
quint32_le idNameIndex;
union {
quint32_le_bitfield<0, 15> flags;
quint32_le_bitfield<15, 1> defaultPropertyIsAlias;
qint32_le_bitfield<16, 16> id;
};
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
quint16_le nFunctions;
quint16_le nProperties;
quint32_le offsetToFunctions;
quint32_le offsetToProperties;
quint32_le offsetToAliases;
quint16_le nAliases;
quint16_le nEnums;
quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects
quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
quint16_le nSignals;
quint16_le nBindings;
quint32_le offsetToBindings;
quint32_le nNamedObjectsInComponent;
quint32_le offsetToNamedObjectsInComponent;
Location location;
Location locationOfIdProperty;
// Function[]
// Property[]
// Signal[]
// Binding[]
static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
+ nProperties * sizeof(Property)
+ nAliases * sizeof(Alias)
+ nEnums * sizeof(quint32)
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ nNamedObjectsInComponent * sizeof(int)
+ 0x7
) & ~0x7;
}
const quint32_le *functionOffsetTable() const
{
return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
}
const Property *propertyTable() const
{
return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties);
}
const Alias *aliasTable() const
{
return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases);
}
const Binding *bindingTable() const
{
return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings);
}
const Enum *enumAt(int idx) const
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset);
}
const Signal *signalAt(int idx) const
{
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
const quint32_le *namedObjectsInComponentTable() const
{
return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
}
// --- QQmlPropertyCacheCreator interface
int propertyCount() const { return nProperties; }
int aliasCount() const { return nAliases; }
int enumCount() const { return nEnums; }
int signalCount() const { return nSignals; }
int functionCount() const { return nFunctions; }
const Binding *bindingsBegin() const { return bindingTable(); }
const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
const Property *propertiesBegin() const { return propertyTable(); }
const Property *propertiesEnd() const { return propertyTable() + nProperties; }
const Alias *aliasesBegin() const { return aliasTable(); }
const Alias *aliasesEnd() const { return aliasTable() + nAliases; }
typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator;
EnumIterator enumsBegin() const { return EnumIterator(this, 0); }
EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); }
typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator;
SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
enum ImportType : unsigned int {
ImportLibrary = 0x1,
ImportFile = 0x2,
ImportScript = 0x3
};
quint32_le type;
quint32_le uriIndex;
quint32_le qualifierIndex;
qint32_le majorVersion;
qint32_le minorVersion;
Location location;
Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; }
};
static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct QmlUnit
{
quint32_le nImports;
quint32_le offsetToImports;
quint32_le nObjects;
quint32_le offsetToObjects;
const Import *importAt(int idx) const {
return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
}
const Object *objectAt(int idx) const {
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
}
};
static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
enum { QmlCompileHashSpace = 48 };
static const char magic_str[] = "qv4cdata";
struct Unit
{
// DO NOT CHANGE THESE FIELDS EVER
char magic[8];
quint32_le version;
quint32_le qtVersion;
qint64_le sourceTimeStamp;
quint32_le unitSize; // Size of the Unit and any depending data.
// END DO NOT CHANGE THESE FIELDS EVER
char libraryVersionHash[QmlCompileHashSpace];
char md5Checksum[16]; // checksum of all bytes following this field.
char dependencyMD5Checksum[16];
enum : unsigned int {
IsJavascript = 0x1,
StaticData = 0x2, // Unit data persistent in memory?
IsSingleton = 0x4,
IsSharedLibrary = 0x8, // .pragma shared?
IsESModule = 0x10,
PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
};
quint32_le flags;
quint32_le stringTableSize;
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
quint32_le classTableSize;
quint32_le offsetToClassTable;
quint32_le templateObjectTableSize;
quint32_le offsetToTemplateObjectTable;
quint32_le blockTableSize;
quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
quint32_le offsetToRegexpTable;
quint32_le constantTableSize;
quint32_le offsetToConstantTable;
quint32_le jsClassTableSize;
quint32_le offsetToJSClassTable;
quint32_le translationTableSize;
quint32_le offsetToTranslationTable;
quint32_le localExportEntryTableSize;
quint32_le offsetToLocalExportEntryTable;
quint32_le indirectExportEntryTableSize;
quint32_le offsetToIndirectExportEntryTable;
quint32_le starExportEntryTableSize;
quint32_le offsetToStarExportEntryTable;
quint32_le importEntryTableSize;
quint32_le offsetToImportEntryTable;
quint32_le moduleRequestTableSize;
quint32_le offsetToModuleRequestTable;
qint32_le indexOfRootFunction;
quint32_le sourceFileIndex;
quint32_le finalUrlIndex;
quint32_le offsetToQmlUnit;
/* QML specific fields */
const QmlUnit *qmlUnit() const {
return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit);
}
QmlUnit *qmlUnit() {
return reinterpret_cast<QmlUnit *>(reinterpret_cast<char *>(this) + offsetToQmlUnit);
}
bool isSingleton() const {
return flags & Unit::IsSingleton;
}
/* end QML specific fields*/
QString stringAtInternal(int idx) const {
Q_ASSERT(idx < int(stringTableSize));
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
if (flags & StaticData) {
const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
return QString(holder);
}
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
QString qstr(str->size, Qt::Uninitialized);
QChar *ch = qstr.data();
for (int i = 0; i < str->size; ++i)
ch[i] = QChar(characters[i]);
return qstr;
#endif
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); }
const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
const Class *classAt(int idx) const {
const quint32_le *offsetTable = classOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
}
const TemplateObject *templateObjectAt(int idx) const {
const quint32_le *offsetTable = templateObjectOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset);
}
const Block *blockAt(int idx) const {
const quint32_le *offsetTable = blockOffsetTable();
const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
}
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
const quint64_le *constants() const {
return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
const quint32_le offset = offsetTable[idx];
const char *ptr = reinterpret_cast<const char *>(this) + offset;
const JSClass *klass = reinterpret_cast<const JSClass *>(ptr);
*nMembers = klass->nMembers;
return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
}
const TranslationData *translations() const {
return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
}
const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
};
static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
TypeReference(const Location &loc)
: location(loc)
, needsCreation(false)
, errorWhenNotFound(false)
{}
Location location; // first use
bool needsCreation : 1; // whether the type needs to be creatable or not
bool errorWhenNotFound: 1;
};
// Map from name index to location of first use.
struct TypeReferenceMap : QHash<int, TypeReference>
{
TypeReference &add(int nameIndex, const Location &loc) {
Iterator it = find(nameIndex);
if (it != end())
return *it;
return *insert(nameIndex, loc);
}
template <typename CompiledObject>
void collectFromObject(const CompiledObject *obj)
{
if (obj->inheritedTypeNameIndex != 0) {
TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location);
r.needsCreation = true;
r.errorWhenNotFound = true;
}
auto prop = obj->propertiesBegin();
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
if (!prop->isBuiltinType) {
TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
auto binding = obj->bindingsBegin();
auto bindingEnd = obj->bindingsEnd();
for ( ; binding != bindingEnd; ++binding) {
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
this->add(binding->propertyNameIndex, binding->location);
}
}
template <typename Iterator>
void collectFromObjects(Iterator it, Iterator end)
{
for (; it != end; ++it)
collectFromObject(*it);
}
};
using DependentTypesHasher = std::function<QByteArray()>;
// This is how this hooks into the existing structures:
struct CompilationUnitBase
{
Q_DISABLE_COPY(CompilationUnitBase)
CompilationUnitBase() = default;
~CompilationUnitBase() = default;
CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); }
CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept
{
if (this != &other) {
runtimeStrings = other.runtimeStrings;
other.runtimeStrings = nullptr;
constants = other.constants;
other.constants = nullptr;
runtimeRegularExpressions = other.runtimeRegularExpressions;
other.runtimeRegularExpressions = nullptr;
runtimeClasses = other.runtimeClasses;
other.runtimeClasses = nullptr;
imports = other.imports;
other.imports = nullptr;
}
return *this;
}
// pointers either to data->constants() or little-endian memory copy.
Heap::String **runtimeStrings = nullptr; // Array
const StaticValue* constants = nullptr;
QV4::StaticValue *runtimeRegularExpressions = nullptr;
Heap::InternalClass **runtimeClasses = nullptr;
const StaticValue** imports = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *));
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *));
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *));
struct CompilationUnit : public CompilationUnitBase
{
Q_DISABLE_COPY(CompilationUnit)
const Unit *data = nullptr;
const QmlUnit *qmlData = nullptr;
QStringList dynamicStrings;
public:
using CompiledObject = CompiledData::Object;
CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(),
const QString &finalUrlString = QString())
{
setUnitData(unitData, nullptr, fileName, finalUrlString);
}
~CompilationUnit()
{
if (data) {
if (data->qmlUnit() != qmlData)
free(const_cast<QmlUnit *>(qmlData));
qmlData = nullptr;
if (!(data->flags & QV4::CompiledData::Unit::StaticData))
free(const_cast<Unit *>(data));
}
data = nullptr;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
delete [] constants;
constants = nullptr;
#endif
delete [] imports;
imports = nullptr;
}
CompilationUnit(CompilationUnit &&other) noexcept
{
*this = std::move(other);
}
CompilationUnit &operator=(CompilationUnit &&other) noexcept
{
if (this != &other) {
data = other.data;
other.data = nullptr;
qmlData = other.qmlData;
other.qmlData = nullptr;
dynamicStrings = std::move(other.dynamicStrings);
other.dynamicStrings.clear();
m_fileName = std::move(other.m_fileName);
other.m_fileName.clear();
m_finalUrlString = std::move(other.m_finalUrlString);
other.m_finalUrlString.clear();
m_module = other.m_module;
other.m_module = nullptr;
CompilationUnitBase::operator=(std::move(other));
}
return *this;
}
const Unit *unitData() const { return data; }
void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
const QString &fileName = QString(), const QString &finalUrlString = QString())
{
data = unitData;
qmlData = nullptr;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
delete [] constants;
#endif
constants = nullptr;
m_fileName.clear();
m_finalUrlString.clear();
if (!data)
return;
qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize];
const quint64_le *littleEndianConstants = data->constants();
for (uint i = 0; i < data->constantTableSize; ++i)
bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]);
constants = bigEndianConstants;
#else
constants = reinterpret_cast<const StaticValue*>(data->constants());
#endif
m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
QString stringAt(int index) const
{
if (uint(index) >= data->stringTableSize)
return dynamicStrings.at(index - data->stringTableSize);
return data->stringAtInternal(index);
}
QString fileName() const { return m_fileName; }
QString finalUrlString() const { return m_finalUrlString; }
Heap::Module *module() const { return m_module; }
void setModule(Heap::Module *module) { m_module = module; }
private:
QString m_fileName; // initialized from data->sourceFileIndex
QString m_finalUrlString; // initialized from data->finalUrlIndex
Heap::Module *m_module = nullptr;
};
class SaveableUnitPointer
{
Q_DISABLE_COPY_MOVE(SaveableUnitPointer)
public:
SaveableUnitPointer(const Unit *unit, quint32 temporaryFlags = Unit::StaticData) :
unit(unit),
temporaryFlags(temporaryFlags)
{
}
~SaveableUnitPointer() = default;
template<typename Char>
bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const
{
auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; });
mutableFlags() |= temporaryFlags;
return writer(data<Char>(), size());
}
static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size,
QString *errorString)
{
#if QT_CONFIG(temporaryfile)
QSaveFile cacheFile(outputFileName);
if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)
|| cacheFile.write(data, size) != size
|| !cacheFile.commit()) {
*errorString = cacheFile.errorString();
return false;
}
errorString->clear();
return true;
#else
Q_UNUSED(outputFileName)
*errorString = QStringLiteral("features.temporaryfile is disabled.");
return false;
#endif
}
private:
const Unit *unit;
quint32 temporaryFlags;
quint32_le &mutableFlags() const
{
return const_cast<Unit *>(unit)->flags;
}
template<typename Char>
const Char *data() const
{
Q_STATIC_ASSERT(sizeof(Char) == 1);
const Char *dataPtr;
memcpy(&dataPtr, &unit, sizeof(dataPtr));
return dataPtr;
}
quint32 size() const
{
return unit->unitSize;
}
};
} // CompiledData namespace
} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
#endif