1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | #ifndef QV4COMPILEDDATA_P_H |
40 | #define QV4COMPILEDDATA_P_H |
41 | |
42 | // |
43 | // W A R N I N G |
44 | // ------------- |
45 | // |
46 | // This file is not part of the Qt API. It exists purely as an |
47 | // implementation detail. This header file may change from version to |
48 | // version without notice, or even be removed. |
49 | // |
50 | // We mean it. |
51 | // |
52 | |
53 | #include <functional> |
54 | |
55 | #include <QtCore/qstring.h> |
56 | #include <QtCore/qscopeguard.h> |
57 | #include <QtCore/qvector.h> |
58 | #include <QtCore/qstringlist.h> |
59 | #include <QtCore/qhash.h> |
60 | |
61 | #if QT_CONFIG(temporaryfile) |
62 | #include <QtCore/qsavefile.h> |
63 | #endif |
64 | |
65 | #include <private/qendian_p.h> |
66 | #include <private/qv4staticvalue_p.h> |
67 | #include <functional> |
68 | |
69 | QT_BEGIN_NAMESPACE |
70 | |
71 | // Bump this whenever the compiler data structures change in an incompatible way. |
72 | // |
73 | // IMPORTANT: |
74 | // |
75 | // Also change the comment behind the number to describe the latest change. This has the added |
76 | // benefit that if another patch changes the version too, it will result in a merge conflict, and |
77 | // not get removed silently. |
78 | #define QV4_DATA_STRUCTURE_VERSION 0x24 // Collect function parameter types |
79 | |
80 | class QIODevice; |
81 | class QQmlTypeNameCache; |
82 | class QQmlType; |
83 | class QQmlEngine; |
84 | |
85 | namespace QmlIR { |
86 | struct Document; |
87 | } |
88 | |
89 | namespace QV4 { |
90 | namespace Heap { |
91 | struct Module; |
92 | struct String; |
93 | struct InternalClass; |
94 | }; |
95 | |
96 | struct Function; |
97 | class EvalISelFactory; |
98 | |
99 | namespace CompiledData { |
100 | |
101 | struct String; |
102 | struct Function; |
103 | struct Lookup; |
104 | struct RegExp; |
105 | struct Unit; |
106 | |
107 | template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const> |
108 | struct TableIterator |
109 | { |
110 | TableIterator(const Container *container, int index) : container(container), index(index) {} |
111 | const Container *container; |
112 | int index; |
113 | |
114 | const ItemType *operator->() { return (container->*IndexedGetter)(index); } |
115 | void operator++() { ++index; } |
116 | bool operator==(const TableIterator &rhs) const { return index == rhs.index; } |
117 | bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } |
118 | }; |
119 | |
120 | struct Location |
121 | { |
122 | union { |
123 | quint32 _dummy; |
124 | quint32_le_bitfield<0, 20> line; |
125 | quint32_le_bitfield<20, 12> column; |
126 | }; |
127 | |
128 | Location() : _dummy(0) { } |
129 | |
130 | inline bool operator<(const Location &other) const { |
131 | return line < other.line || |
132 | (line == other.line && column < other.column); |
133 | } |
134 | }; |
135 | 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" ); |
136 | |
137 | struct RegExp |
138 | { |
139 | enum Flags : unsigned int { |
140 | RegExp_NoFlags = 0x0, |
141 | RegExp_Global = 0x01, |
142 | RegExp_IgnoreCase = 0x02, |
143 | RegExp_Multiline = 0x04, |
144 | RegExp_Unicode = 0x08, |
145 | RegExp_Sticky = 0x10 |
146 | }; |
147 | union { |
148 | quint32 _dummy; |
149 | quint32_le_bitfield<0, 5> flags; |
150 | quint32_le_bitfield<5, 27> stringIndex; |
151 | }; |
152 | |
153 | RegExp() : _dummy(0) { } |
154 | }; |
155 | 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" ); |
156 | |
157 | struct Lookup |
158 | { |
159 | enum Type : unsigned int { |
160 | Type_Getter = 0, |
161 | Type_Setter = 1, |
162 | Type_GlobalGetter = 2, |
163 | Type_QmlContextPropertyGetter = 3 |
164 | }; |
165 | |
166 | union { |
167 | quint32 _dummy; |
168 | quint32_le_bitfield<0, 4> type_and_flags; |
169 | quint32_le_bitfield<4, 28> nameIndex; |
170 | }; |
171 | |
172 | Lookup() : _dummy(0) { } |
173 | }; |
174 | 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" ); |
175 | |
176 | struct JSClassMember |
177 | { |
178 | union { |
179 | quint32 _dummy; |
180 | quint32_le_bitfield<0, 31> nameOffset; |
181 | quint32_le_bitfield<31, 1> isAccessor; |
182 | }; |
183 | |
184 | JSClassMember() : _dummy(0) { } |
185 | }; |
186 | 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" ); |
187 | |
188 | struct JSClass |
189 | { |
190 | quint32_le nMembers; |
191 | // JSClassMember[nMembers] |
192 | |
193 | static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } |
194 | }; |
195 | 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" ); |
196 | |
197 | // This data structure is intended to be binary compatible with QStringData/QStaticStringData on |
198 | // 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped |
199 | // from a file must be castable to a QStringData regardless of the pointer size. With the first |
200 | // few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a |
201 | // ptrdiff_t and thus variable in size. |
202 | // On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while |
203 | // on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain |
204 | // the same value. |
205 | struct String |
206 | { |
207 | qint32_le refcount; // -1 |
208 | qint32_le size; |
209 | quint32_le allocAndCapacityReservedFlag; // 0 |
210 | quint32_le offsetOn32Bit; |
211 | quint64_le offsetOn64Bit; |
212 | // uint16 strdata[] |
213 | |
214 | static int calculateSize(const QString &str) { |
215 | return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7; |
216 | } |
217 | }; |
218 | 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" ); |
219 | |
220 | // Ensure compatibility with QString |
221 | static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location" ); |
222 | static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location" ); |
223 | static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location" ); |
224 | static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location" ); |
225 | #if QT_POINTER_SIZE == 8 |
226 | static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location" ); |
227 | #else |
228 | static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location" ); |
229 | #endif |
230 | |
231 | struct CodeOffsetToLine { |
232 | quint32_le codeOffset; |
233 | quint32_le line; |
234 | }; |
235 | 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" ); |
236 | |
237 | struct Block |
238 | { |
239 | quint32_le nLocals; |
240 | quint32_le localsOffset; |
241 | quint16_le sizeOfLocalTemporalDeadZone; |
242 | quint16_le padding; |
243 | |
244 | const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } |
245 | |
246 | static int calculateSize(int nLocals) { |
247 | int trailingData = nLocals*sizeof (quint32); |
248 | size_t size = align(align(sizeof(Block)) + size_t(trailingData)); |
249 | Q_ASSERT(size < INT_MAX); |
250 | return int(size); |
251 | } |
252 | |
253 | static size_t align(size_t a) { |
254 | return (a + 7) & ~size_t(7); |
255 | } |
256 | }; |
257 | 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" ); |
258 | |
259 | enum class BuiltinType : unsigned int { |
260 | Var = 0, Variant, Int, Bool, Real, String, Url, Color, |
261 | Font, Time, Date, DateTime, Rect, Point, Size, |
262 | Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin |
263 | }; |
264 | |
265 | struct ParameterType |
266 | { |
267 | union { |
268 | quint32 _dummy; |
269 | quint32_le_bitfield<0, 1> indexIsBuiltinType; |
270 | quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; |
271 | }; |
272 | }; |
273 | 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" ); |
274 | |
275 | struct Parameter |
276 | { |
277 | quint32_le nameIndex; |
278 | ParameterType type; |
279 | }; |
280 | 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" ); |
281 | |
282 | // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties |
283 | // for unaligned access. The ordering of the fields is also from largest to smallest. |
284 | struct Function |
285 | { |
286 | enum Flags : unsigned int { |
287 | IsStrict = 0x1, |
288 | IsArrowFunction = 0x2, |
289 | IsGenerator = 0x4 |
290 | }; |
291 | |
292 | // Absolute offset into file where the code for this function is located. |
293 | quint32_le codeOffset; |
294 | quint32_le codeSize; |
295 | |
296 | quint32_le nameIndex; |
297 | quint16_le length; |
298 | quint16_le nFormals; |
299 | quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData. |
300 | ParameterType returnType; |
301 | quint32_le localsOffset; |
302 | quint16_le nLocals; |
303 | quint16_le nLineNumbers; |
304 | size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } |
305 | quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers |
306 | quint16_le sizeOfLocalTemporalDeadZone; |
307 | quint16_le firstTemporalDeadZoneRegister; |
308 | quint16_le sizeOfRegisterTemporalDeadZone; |
309 | quint16_le nRegisters; |
310 | Location location; |
311 | |
312 | quint32_le nLabelInfos; |
313 | size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } |
314 | |
315 | // Keep all unaligned data at the end |
316 | quint8 flags; |
317 | quint8 padding1; |
318 | |
319 | // quint32 formalsIndex[nFormals] |
320 | // quint32 localsIndex[nLocals] |
321 | |
322 | const Parameter *formalsTable() const { return reinterpret_cast<const Parameter *>(reinterpret_cast<const char *>(this) + formalsOffset); } |
323 | const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } |
324 | const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); } |
325 | |
326 | // --- QQmlPropertyCacheCreator interface |
327 | const Parameter *formalsBegin() const { return formalsTable(); } |
328 | const Parameter *formalsEnd() const { return formalsTable() + nFormals; } |
329 | // --- |
330 | |
331 | const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); } |
332 | |
333 | const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; } |
334 | |
335 | static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { |
336 | int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) |
337 | + nLines*sizeof(CodeOffsetToLine); |
338 | size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); |
339 | Q_ASSERT(size < INT_MAX); |
340 | return int(size); |
341 | } |
342 | |
343 | static size_t align(size_t a) { |
344 | return (a + 7) & ~size_t(7); |
345 | } |
346 | }; |
347 | 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" ); |
348 | |
349 | struct Method { |
350 | enum Type { |
351 | Regular, |
352 | Getter, |
353 | Setter |
354 | }; |
355 | |
356 | quint32_le name; |
357 | quint32_le type; |
358 | quint32_le function; |
359 | }; |
360 | 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" ); |
361 | |
362 | struct Class |
363 | { |
364 | quint32_le nameIndex; |
365 | quint32_le scopeIndex; |
366 | quint32_le constructorFunction; |
367 | quint32_le nStaticMethods; |
368 | quint32_le nMethods; |
369 | quint32_le methodTableOffset; |
370 | |
371 | const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); } |
372 | |
373 | static int calculateSize(int nStaticMethods, int nMethods) { |
374 | int trailingData = (nStaticMethods + nMethods) * sizeof(Method); |
375 | size_t size = align(sizeof(Class) + trailingData); |
376 | Q_ASSERT(size < INT_MAX); |
377 | return int(size); |
378 | } |
379 | |
380 | static size_t align(size_t a) { |
381 | return (a + 7) & ~size_t(7); |
382 | } |
383 | }; |
384 | 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" ); |
385 | |
386 | struct TemplateObject |
387 | { |
388 | quint32_le size; |
389 | |
390 | static int calculateSize(int size) { |
391 | int trailingData = 2 * size * sizeof(quint32_le); |
392 | size_t s = align(sizeof(TemplateObject) + trailingData); |
393 | Q_ASSERT(s < INT_MAX); |
394 | return int(s); |
395 | } |
396 | |
397 | static size_t align(size_t a) { |
398 | return (a + 7) & ~size_t(7); |
399 | } |
400 | |
401 | const quint32_le *stringTable() const { |
402 | return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1)); |
403 | } |
404 | |
405 | uint stringIndexAt(uint i) const { |
406 | return stringTable()[i]; |
407 | } |
408 | uint rawStringIndexAt(uint i) const { |
409 | return stringTable()[size + i]; |
410 | } |
411 | }; |
412 | 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" ); |
413 | |
414 | struct ExportEntry |
415 | { |
416 | quint32_le exportName; |
417 | quint32_le moduleRequest; |
418 | quint32_le importName; |
419 | quint32_le localName; |
420 | Location location; |
421 | }; |
422 | 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" ); |
423 | |
424 | struct ImportEntry |
425 | { |
426 | quint32_le moduleRequest; |
427 | quint32_le importName; |
428 | quint32_le localName; |
429 | Location location; |
430 | }; |
431 | 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" ); |
432 | |
433 | // Qml data structures |
434 | |
435 | struct TranslationData |
436 | { |
437 | quint32_le stringIndex; |
438 | quint32_le ; |
439 | qint32_le number; |
440 | quint32_le padding; |
441 | }; |
442 | 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" ); |
443 | |
444 | struct Binding |
445 | { |
446 | quint32_le propertyNameIndex; |
447 | |
448 | enum ValueType : unsigned int { |
449 | Type_Invalid, |
450 | Type_Boolean, |
451 | Type_Number, |
452 | Type_String, |
453 | Type_Null, |
454 | Type_Translation, |
455 | Type_TranslationById, |
456 | Type_Script, |
457 | Type_Object, |
458 | Type_AttachedProperty, |
459 | Type_GroupProperty |
460 | }; |
461 | |
462 | enum Flags : unsigned int { |
463 | IsSignalHandlerExpression = 0x1, |
464 | IsSignalHandlerObject = 0x2, |
465 | IsOnAssignment = 0x4, |
466 | InitializerForReadOnlyDeclaration = 0x8, |
467 | IsResolvedEnum = 0x10, |
468 | IsListItem = 0x20, |
469 | IsBindingToAlias = 0x40, |
470 | IsDeferredBinding = 0x80, |
471 | IsCustomParserBinding = 0x100, |
472 | IsFunctionExpression = 0x200 |
473 | }; |
474 | |
475 | union { |
476 | quint32_le_bitfield<0, 16> flags; |
477 | quint32_le_bitfield<16, 16> type; |
478 | }; |
479 | union { |
480 | bool b; |
481 | quint32_le constantValueIndex; |
482 | quint32_le compiledScriptIndex; // used when Type_Script |
483 | quint32_le objectIndex; |
484 | quint32_le translationDataIndex; // used when Type_Translation |
485 | quint32 nullMarker; |
486 | } value; |
487 | quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) |
488 | |
489 | Location location; |
490 | Location valueLocation; |
491 | |
492 | bool isValueBinding() const |
493 | { |
494 | if (type == Type_AttachedProperty |
495 | || type == Type_GroupProperty) |
496 | return false; |
497 | if (flags & IsSignalHandlerExpression |
498 | || flags & IsSignalHandlerObject) |
499 | return false; |
500 | return true; |
501 | } |
502 | |
503 | bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); } |
504 | bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); } |
505 | |
506 | bool isSignalHandler() const |
507 | { |
508 | if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) { |
509 | Q_ASSERT(!isValueBinding()); |
510 | Q_ASSERT(!isAttachedProperty()); |
511 | Q_ASSERT(!isGroupProperty()); |
512 | return true; |
513 | } |
514 | return false; |
515 | } |
516 | |
517 | bool isAttachedProperty() const |
518 | { |
519 | if (type == Type_AttachedProperty) { |
520 | Q_ASSERT(!isValueBinding()); |
521 | Q_ASSERT(!isSignalHandler()); |
522 | Q_ASSERT(!isGroupProperty()); |
523 | return true; |
524 | } |
525 | return false; |
526 | } |
527 | |
528 | bool isGroupProperty() const |
529 | { |
530 | if (type == Type_GroupProperty) { |
531 | Q_ASSERT(!isValueBinding()); |
532 | Q_ASSERT(!isSignalHandler()); |
533 | Q_ASSERT(!isAttachedProperty()); |
534 | return true; |
535 | } |
536 | return false; |
537 | } |
538 | |
539 | bool isFunctionExpression() const { return (flags & IsFunctionExpression); } |
540 | |
541 | //reverse of Lexer::singleEscape() |
542 | static QString escapedString(const QString &string) |
543 | { |
544 | QString tmp = QLatin1String("\"" ); |
545 | for (int i = 0; i < string.length(); ++i) { |
546 | const QChar &c = string.at(i); |
547 | switch (c.unicode()) { |
548 | case 0x08: |
549 | tmp += QLatin1String("\\b" ); |
550 | break; |
551 | case 0x09: |
552 | tmp += QLatin1String("\\t" ); |
553 | break; |
554 | case 0x0A: |
555 | tmp += QLatin1String("\\n" ); |
556 | break; |
557 | case 0x0B: |
558 | tmp += QLatin1String("\\v" ); |
559 | break; |
560 | case 0x0C: |
561 | tmp += QLatin1String("\\f" ); |
562 | break; |
563 | case 0x0D: |
564 | tmp += QLatin1String("\\r" ); |
565 | break; |
566 | case 0x22: |
567 | tmp += QLatin1String("\\\"" ); |
568 | break; |
569 | case 0x27: |
570 | tmp += QLatin1String("\\\'" ); |
571 | break; |
572 | case 0x5C: |
573 | tmp += QLatin1String("\\\\" ); |
574 | break; |
575 | default: |
576 | tmp += c; |
577 | break; |
578 | } |
579 | } |
580 | tmp += QLatin1Char('\"'); |
581 | return tmp; |
582 | } |
583 | |
584 | bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; } |
585 | bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); } |
586 | |
587 | bool valueAsBoolean() const |
588 | { |
589 | if (type == Type_Boolean) |
590 | return value.b; |
591 | return false; |
592 | } |
593 | |
594 | }; |
595 | |
596 | 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" ); |
597 | |
598 | struct EnumValue |
599 | { |
600 | quint32_le nameIndex; |
601 | qint32_le value; |
602 | Location location; |
603 | }; |
604 | 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" ); |
605 | |
606 | struct Enum |
607 | { |
608 | quint32_le nameIndex; |
609 | quint32_le nEnumValues; |
610 | Location location; |
611 | |
612 | const EnumValue *enumValueAt(int idx) const { |
613 | return reinterpret_cast<const EnumValue*>(this + 1) + idx; |
614 | } |
615 | |
616 | static int calculateSize(int nEnumValues) { |
617 | return (sizeof(Enum) |
618 | + nEnumValues * sizeof(EnumValue) |
619 | + 7) & ~0x7; |
620 | } |
621 | |
622 | // --- QQmlPropertyCacheCreatorInterface |
623 | const EnumValue *enumValuesBegin() const { return enumValueAt(0); } |
624 | const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); } |
625 | int enumValueCount() const { return nEnumValues; } |
626 | // --- |
627 | }; |
628 | 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" ); |
629 | |
630 | struct Signal |
631 | { |
632 | quint32_le nameIndex; |
633 | quint32_le nParameters; |
634 | Location location; |
635 | // Parameter parameters[1]; |
636 | |
637 | const Parameter *parameterAt(int idx) const { |
638 | return reinterpret_cast<const Parameter*>(this + 1) + idx; |
639 | } |
640 | |
641 | static int calculateSize(int nParameters) { |
642 | return (sizeof(Signal) |
643 | + nParameters * sizeof(Parameter) |
644 | + 7) & ~0x7; |
645 | } |
646 | |
647 | // --- QQmlPropertyCacheCceatorInterface |
648 | const Parameter *parametersBegin() const { return parameterAt(0); } |
649 | const Parameter *parametersEnd() const { return parameterAt(nParameters); } |
650 | int parameterCount() const { return nParameters; } |
651 | // --- |
652 | }; |
653 | 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" ); |
654 | |
655 | struct Property |
656 | { |
657 | quint32_le nameIndex; |
658 | union { |
659 | quint32_le_bitfield<0, 29> builtinTypeOrTypeNameIndex; |
660 | quint32_le_bitfield<29, 1> isBuiltinType; |
661 | quint32_le_bitfield<30, 1> isList; |
662 | quint32_le_bitfield<31, 1> isReadOnly; |
663 | }; |
664 | |
665 | Location location; |
666 | |
667 | void setBuiltinType(BuiltinType t) |
668 | { |
669 | builtinTypeOrTypeNameIndex = static_cast<quint32>(t); |
670 | isBuiltinType = true; |
671 | } |
672 | BuiltinType builtinType() const { |
673 | if (isBuiltinType) |
674 | return static_cast<BuiltinType>(quint32(builtinTypeOrTypeNameIndex)); |
675 | return BuiltinType::InvalidBuiltin; |
676 | } |
677 | void setCustomType(int nameIndex) |
678 | { |
679 | builtinTypeOrTypeNameIndex = nameIndex; |
680 | isBuiltinType = false; |
681 | } |
682 | }; |
683 | 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" ); |
684 | |
685 | struct Alias { |
686 | enum Flags : unsigned int { |
687 | IsReadOnly = 0x1, |
688 | Resolved = 0x2, |
689 | AliasPointsToPointerObject = 0x4 |
690 | }; |
691 | union { |
692 | quint32_le_bitfield<0, 29> nameIndex; |
693 | quint32_le_bitfield<29, 3> flags; |
694 | }; |
695 | union { |
696 | quint32_le idIndex; // string index |
697 | quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) |
698 | quint32_le_bitfield<31, 1> aliasToLocalAlias; |
699 | }; |
700 | union { |
701 | quint32_le propertyNameIndex; // string index |
702 | qint32_le encodedMetaPropertyIndex; |
703 | quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) |
704 | }; |
705 | Location location; |
706 | Location referenceLocation; |
707 | |
708 | bool isObjectAlias() const { |
709 | Q_ASSERT(flags & Resolved); |
710 | return encodedMetaPropertyIndex == -1; |
711 | } |
712 | }; |
713 | 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" ); |
714 | |
715 | struct Object |
716 | { |
717 | enum Flags : unsigned int { |
718 | NoFlag = 0x0, |
719 | IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary |
720 | HasDeferredBindings = 0x2, // any of the bindings are deferred |
721 | HasCustomParserBindings = 0x4 |
722 | }; |
723 | |
724 | // Depending on the use, this may be the type name to instantiate before instantiating this |
725 | // object. For grouped properties the type name will be empty and for attached properties |
726 | // it will be the name of the attached type. |
727 | quint32_le inheritedTypeNameIndex; |
728 | quint32_le idNameIndex; |
729 | union { |
730 | quint32_le_bitfield<0, 15> flags; |
731 | quint32_le_bitfield<15, 1> defaultPropertyIsAlias; |
732 | qint32_le_bitfield<16, 16> id; |
733 | }; |
734 | qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object |
735 | quint16_le nFunctions; |
736 | quint16_le nProperties; |
737 | quint32_le offsetToFunctions; |
738 | quint32_le offsetToProperties; |
739 | quint32_le offsetToAliases; |
740 | quint16_le nAliases; |
741 | quint16_le nEnums; |
742 | quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects |
743 | quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects |
744 | quint16_le nSignals; |
745 | quint16_le nBindings; |
746 | quint32_le offsetToBindings; |
747 | quint32_le nNamedObjectsInComponent; |
748 | quint32_le offsetToNamedObjectsInComponent; |
749 | Location location; |
750 | Location locationOfIdProperty; |
751 | // Function[] |
752 | // Property[] |
753 | // Signal[] |
754 | // Binding[] |
755 | |
756 | static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent) |
757 | { |
758 | return ( sizeof(Object) |
759 | + nFunctions * sizeof(quint32) |
760 | + nProperties * sizeof(Property) |
761 | + nAliases * sizeof(Alias) |
762 | + nEnums * sizeof(quint32) |
763 | + nSignals * sizeof(quint32) |
764 | + nBindings * sizeof(Binding) |
765 | + nNamedObjectsInComponent * sizeof(int) |
766 | + 0x7 |
767 | ) & ~0x7; |
768 | } |
769 | |
770 | const quint32_le *functionOffsetTable() const |
771 | { |
772 | return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions); |
773 | } |
774 | |
775 | const Property *propertyTable() const |
776 | { |
777 | return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties); |
778 | } |
779 | |
780 | const Alias *aliasTable() const |
781 | { |
782 | return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases); |
783 | } |
784 | |
785 | const Binding *bindingTable() const |
786 | { |
787 | return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings); |
788 | } |
789 | |
790 | const Enum *enumAt(int idx) const |
791 | { |
792 | const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums); |
793 | const quint32_le offset = offsetTable[idx]; |
794 | return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset); |
795 | } |
796 | |
797 | const Signal *signalAt(int idx) const |
798 | { |
799 | const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals); |
800 | const quint32_le offset = offsetTable[idx]; |
801 | return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); |
802 | } |
803 | |
804 | const quint32_le *namedObjectsInComponentTable() const |
805 | { |
806 | return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); |
807 | } |
808 | |
809 | // --- QQmlPropertyCacheCreator interface |
810 | int propertyCount() const { return nProperties; } |
811 | int aliasCount() const { return nAliases; } |
812 | int enumCount() const { return nEnums; } |
813 | int signalCount() const { return nSignals; } |
814 | int functionCount() const { return nFunctions; } |
815 | |
816 | const Binding *bindingsBegin() const { return bindingTable(); } |
817 | const Binding *bindingsEnd() const { return bindingTable() + nBindings; } |
818 | |
819 | const Property *propertiesBegin() const { return propertyTable(); } |
820 | const Property *propertiesEnd() const { return propertyTable() + nProperties; } |
821 | |
822 | const Alias *aliasesBegin() const { return aliasTable(); } |
823 | const Alias *aliasesEnd() const { return aliasTable() + nAliases; } |
824 | |
825 | typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator; |
826 | EnumIterator enumsBegin() const { return EnumIterator(this, 0); } |
827 | EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); } |
828 | |
829 | typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator; |
830 | SignalIterator signalsBegin() const { return SignalIterator(this, 0); } |
831 | SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } |
832 | |
833 | int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } |
834 | // --- |
835 | }; |
836 | 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" ); |
837 | |
838 | struct Import |
839 | { |
840 | enum ImportType : unsigned int { |
841 | ImportLibrary = 0x1, |
842 | ImportFile = 0x2, |
843 | ImportScript = 0x3 |
844 | }; |
845 | quint32_le type; |
846 | |
847 | quint32_le uriIndex; |
848 | quint32_le qualifierIndex; |
849 | |
850 | qint32_le majorVersion; |
851 | qint32_le minorVersion; |
852 | |
853 | Location location; |
854 | |
855 | Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } |
856 | }; |
857 | 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" ); |
858 | |
859 | struct QmlUnit |
860 | { |
861 | quint32_le nImports; |
862 | quint32_le offsetToImports; |
863 | quint32_le nObjects; |
864 | quint32_le offsetToObjects; |
865 | |
866 | const Import *importAt(int idx) const { |
867 | return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); |
868 | } |
869 | |
870 | const Object *objectAt(int idx) const { |
871 | const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects); |
872 | const quint32_le offset = offsetTable[idx]; |
873 | return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset); |
874 | } |
875 | }; |
876 | 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" ); |
877 | |
878 | enum { QmlCompileHashSpace = 48 }; |
879 | static const char magic_str[] = "qv4cdata" ; |
880 | |
881 | struct Unit |
882 | { |
883 | // DO NOT CHANGE THESE FIELDS EVER |
884 | char magic[8]; |
885 | quint32_le version; |
886 | quint32_le qtVersion; |
887 | qint64_le sourceTimeStamp; |
888 | quint32_le unitSize; // Size of the Unit and any depending data. |
889 | // END DO NOT CHANGE THESE FIELDS EVER |
890 | |
891 | char libraryVersionHash[QmlCompileHashSpace]; |
892 | |
893 | char md5Checksum[16]; // checksum of all bytes following this field. |
894 | char dependencyMD5Checksum[16]; |
895 | |
896 | enum : unsigned int { |
897 | IsJavascript = 0x1, |
898 | StaticData = 0x2, // Unit data persistent in memory? |
899 | IsSingleton = 0x4, |
900 | IsSharedLibrary = 0x8, // .pragma shared? |
901 | IsESModule = 0x10, |
902 | PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation |
903 | }; |
904 | quint32_le flags; |
905 | quint32_le stringTableSize; |
906 | quint32_le offsetToStringTable; |
907 | quint32_le functionTableSize; |
908 | quint32_le offsetToFunctionTable; |
909 | quint32_le classTableSize; |
910 | quint32_le offsetToClassTable; |
911 | quint32_le templateObjectTableSize; |
912 | quint32_le offsetToTemplateObjectTable; |
913 | quint32_le blockTableSize; |
914 | quint32_le offsetToBlockTable; |
915 | quint32_le lookupTableSize; |
916 | quint32_le offsetToLookupTable; |
917 | quint32_le regexpTableSize; |
918 | quint32_le offsetToRegexpTable; |
919 | quint32_le constantTableSize; |
920 | quint32_le offsetToConstantTable; |
921 | quint32_le jsClassTableSize; |
922 | quint32_le offsetToJSClassTable; |
923 | quint32_le translationTableSize; |
924 | quint32_le offsetToTranslationTable; |
925 | quint32_le localExportEntryTableSize; |
926 | quint32_le offsetToLocalExportEntryTable; |
927 | quint32_le indirectExportEntryTableSize; |
928 | quint32_le offsetToIndirectExportEntryTable; |
929 | quint32_le starExportEntryTableSize; |
930 | quint32_le offsetToStarExportEntryTable; |
931 | quint32_le importEntryTableSize; |
932 | quint32_le offsetToImportEntryTable; |
933 | quint32_le moduleRequestTableSize; |
934 | quint32_le offsetToModuleRequestTable; |
935 | qint32_le indexOfRootFunction; |
936 | quint32_le sourceFileIndex; |
937 | quint32_le finalUrlIndex; |
938 | |
939 | quint32_le offsetToQmlUnit; |
940 | |
941 | /* QML specific fields */ |
942 | |
943 | const QmlUnit *qmlUnit() const { |
944 | return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit); |
945 | } |
946 | |
947 | QmlUnit *qmlUnit() { |
948 | return reinterpret_cast<QmlUnit *>(reinterpret_cast<char *>(this) + offsetToQmlUnit); |
949 | } |
950 | |
951 | bool isSingleton() const { |
952 | return flags & Unit::IsSingleton; |
953 | } |
954 | /* end QML specific fields*/ |
955 | |
956 | QString stringAtInternal(int idx) const { |
957 | Q_ASSERT(idx < int(stringTableSize)); |
958 | const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable); |
959 | const quint32_le offset = offsetTable[idx]; |
960 | const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset); |
961 | if (str->size == 0) |
962 | return QString(); |
963 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
964 | if (flags & StaticData) { |
965 | const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) }; |
966 | return QString(holder); |
967 | } |
968 | const QChar *characters = reinterpret_cast<const QChar *>(str + 1); |
969 | return QString(characters, str->size); |
970 | #else |
971 | const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1); |
972 | QString qstr(str->size, Qt::Uninitialized); |
973 | QChar *ch = qstr.data(); |
974 | for (int i = 0; i < str->size; ++i) |
975 | ch[i] = QChar(characters[i]); |
976 | return qstr; |
977 | #endif |
978 | } |
979 | |
980 | const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } |
981 | const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } |
982 | const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); } |
983 | const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } |
984 | |
985 | const Function *functionAt(int idx) const { |
986 | const quint32_le *offsetTable = functionOffsetTable(); |
987 | const quint32_le offset = offsetTable[idx]; |
988 | return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset); |
989 | } |
990 | |
991 | const Class *classAt(int idx) const { |
992 | const quint32_le *offsetTable = classOffsetTable(); |
993 | const quint32_le offset = offsetTable[idx]; |
994 | return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); |
995 | } |
996 | |
997 | const TemplateObject *templateObjectAt(int idx) const { |
998 | const quint32_le *offsetTable = templateObjectOffsetTable(); |
999 | const quint32_le offset = offsetTable[idx]; |
1000 | return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset); |
1001 | } |
1002 | |
1003 | const Block *blockAt(int idx) const { |
1004 | const quint32_le *offsetTable = blockOffsetTable(); |
1005 | const quint32_le offset = offsetTable[idx]; |
1006 | return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset); |
1007 | } |
1008 | |
1009 | const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); } |
1010 | const RegExp *regexpAt(int index) const { |
1011 | return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp)); |
1012 | } |
1013 | const quint64_le *constants() const { |
1014 | return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable); |
1015 | } |
1016 | |
1017 | const JSClassMember *jsClassAt(int idx, int *nMembers) const { |
1018 | const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable); |
1019 | const quint32_le offset = offsetTable[idx]; |
1020 | const char *ptr = reinterpret_cast<const char *>(this) + offset; |
1021 | const JSClass *klass = reinterpret_cast<const JSClass *>(ptr); |
1022 | *nMembers = klass->nMembers; |
1023 | return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass)); |
1024 | } |
1025 | |
1026 | const TranslationData *translations() const { |
1027 | return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable); |
1028 | } |
1029 | |
1030 | const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); } |
1031 | const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); } |
1032 | const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); } |
1033 | const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); } |
1034 | |
1035 | const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); } |
1036 | }; |
1037 | |
1038 | 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" ); |
1039 | |
1040 | struct TypeReference |
1041 | { |
1042 | TypeReference(const Location &loc) |
1043 | : location(loc) |
1044 | , needsCreation(false) |
1045 | , errorWhenNotFound(false) |
1046 | {} |
1047 | Location location; // first use |
1048 | bool needsCreation : 1; // whether the type needs to be creatable or not |
1049 | bool errorWhenNotFound: 1; |
1050 | }; |
1051 | |
1052 | // Map from name index to location of first use. |
1053 | struct TypeReferenceMap : QHash<int, TypeReference> |
1054 | { |
1055 | TypeReference &add(int nameIndex, const Location &loc) { |
1056 | Iterator it = find(nameIndex); |
1057 | if (it != end()) |
1058 | return *it; |
1059 | return *insert(nameIndex, loc); |
1060 | } |
1061 | |
1062 | template <typename CompiledObject> |
1063 | void collectFromObject(const CompiledObject *obj) |
1064 | { |
1065 | if (obj->inheritedTypeNameIndex != 0) { |
1066 | TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location); |
1067 | r.needsCreation = true; |
1068 | r.errorWhenNotFound = true; |
1069 | } |
1070 | |
1071 | auto prop = obj->propertiesBegin(); |
1072 | auto propEnd = obj->propertiesEnd(); |
1073 | for ( ; prop != propEnd; ++prop) { |
1074 | if (!prop->isBuiltinType) { |
1075 | TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location); |
1076 | r.errorWhenNotFound = true; |
1077 | } |
1078 | } |
1079 | |
1080 | auto binding = obj->bindingsBegin(); |
1081 | auto bindingEnd = obj->bindingsEnd(); |
1082 | for ( ; binding != bindingEnd; ++binding) { |
1083 | if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) |
1084 | this->add(binding->propertyNameIndex, binding->location); |
1085 | } |
1086 | } |
1087 | |
1088 | template <typename Iterator> |
1089 | void collectFromObjects(Iterator it, Iterator end) |
1090 | { |
1091 | for (; it != end; ++it) |
1092 | collectFromObject(*it); |
1093 | } |
1094 | }; |
1095 | |
1096 | using DependentTypesHasher = std::function<QByteArray()>; |
1097 | |
1098 | // This is how this hooks into the existing structures: |
1099 | |
1100 | struct CompilationUnitBase |
1101 | { |
1102 | Q_DISABLE_COPY(CompilationUnitBase) |
1103 | |
1104 | CompilationUnitBase() = default; |
1105 | ~CompilationUnitBase() = default; |
1106 | |
1107 | CompilationUnitBase(CompilationUnitBase &&other) noexcept { *this = std::move(other); } |
1108 | |
1109 | CompilationUnitBase &operator=(CompilationUnitBase &&other) noexcept |
1110 | { |
1111 | if (this != &other) { |
1112 | runtimeStrings = other.runtimeStrings; |
1113 | other.runtimeStrings = nullptr; |
1114 | constants = other.constants; |
1115 | other.constants = nullptr; |
1116 | runtimeRegularExpressions = other.runtimeRegularExpressions; |
1117 | other.runtimeRegularExpressions = nullptr; |
1118 | runtimeClasses = other.runtimeClasses; |
1119 | other.runtimeClasses = nullptr; |
1120 | imports = other.imports; |
1121 | other.imports = nullptr; |
1122 | } |
1123 | return *this; |
1124 | } |
1125 | |
1126 | // pointers either to data->constants() or little-endian memory copy. |
1127 | Heap::String **runtimeStrings = nullptr; // Array |
1128 | const StaticValue* constants = nullptr; |
1129 | QV4::StaticValue *runtimeRegularExpressions = nullptr; |
1130 | Heap::InternalClass **runtimeClasses = nullptr; |
1131 | const StaticValue** imports = nullptr; |
1132 | }; |
1133 | |
1134 | Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); |
1135 | Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); |
1136 | Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **)); |
1137 | Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const StaticValue *)); |
1138 | Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const StaticValue *)); |
1139 | Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const StaticValue *)); |
1140 | |
1141 | struct CompilationUnit : public CompilationUnitBase |
1142 | { |
1143 | Q_DISABLE_COPY(CompilationUnit) |
1144 | |
1145 | const Unit *data = nullptr; |
1146 | const QmlUnit *qmlData = nullptr; |
1147 | QStringList dynamicStrings; |
1148 | public: |
1149 | using CompiledObject = CompiledData::Object; |
1150 | |
1151 | CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), |
1152 | const QString &finalUrlString = QString()) |
1153 | { |
1154 | setUnitData(unitData, nullptr, fileName, finalUrlString); |
1155 | } |
1156 | |
1157 | ~CompilationUnit() |
1158 | { |
1159 | if (data) { |
1160 | if (data->qmlUnit() != qmlData) |
1161 | free(const_cast<QmlUnit *>(qmlData)); |
1162 | qmlData = nullptr; |
1163 | |
1164 | if (!(data->flags & QV4::CompiledData::Unit::StaticData)) |
1165 | free(const_cast<Unit *>(data)); |
1166 | } |
1167 | data = nullptr; |
1168 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
1169 | delete [] constants; |
1170 | constants = nullptr; |
1171 | #endif |
1172 | |
1173 | delete [] imports; |
1174 | imports = nullptr; |
1175 | } |
1176 | |
1177 | CompilationUnit(CompilationUnit &&other) noexcept |
1178 | { |
1179 | *this = std::move(other); |
1180 | } |
1181 | |
1182 | CompilationUnit &operator=(CompilationUnit &&other) noexcept |
1183 | { |
1184 | if (this != &other) { |
1185 | data = other.data; |
1186 | other.data = nullptr; |
1187 | qmlData = other.qmlData; |
1188 | other.qmlData = nullptr; |
1189 | dynamicStrings = std::move(other.dynamicStrings); |
1190 | other.dynamicStrings.clear(); |
1191 | m_fileName = std::move(other.m_fileName); |
1192 | other.m_fileName.clear(); |
1193 | m_finalUrlString = std::move(other.m_finalUrlString); |
1194 | other.m_finalUrlString.clear(); |
1195 | m_module = other.m_module; |
1196 | other.m_module = nullptr; |
1197 | CompilationUnitBase::operator=(std::move(other)); |
1198 | } |
1199 | return *this; |
1200 | } |
1201 | |
1202 | const Unit *unitData() const { return data; } |
1203 | |
1204 | void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr, |
1205 | const QString &fileName = QString(), const QString &finalUrlString = QString()) |
1206 | { |
1207 | data = unitData; |
1208 | qmlData = nullptr; |
1209 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
1210 | delete [] constants; |
1211 | #endif |
1212 | constants = nullptr; |
1213 | m_fileName.clear(); |
1214 | m_finalUrlString.clear(); |
1215 | if (!data) |
1216 | return; |
1217 | |
1218 | qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); |
1219 | |
1220 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
1221 | StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize]; |
1222 | const quint64_le *littleEndianConstants = data->constants(); |
1223 | for (uint i = 0; i < data->constantTableSize; ++i) |
1224 | bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]); |
1225 | constants = bigEndianConstants; |
1226 | #else |
1227 | constants = reinterpret_cast<const StaticValue*>(data->constants()); |
1228 | #endif |
1229 | |
1230 | m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); |
1231 | m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex); |
1232 | } |
1233 | |
1234 | QString stringAt(int index) const |
1235 | { |
1236 | if (uint(index) >= data->stringTableSize) |
1237 | return dynamicStrings.at(index - data->stringTableSize); |
1238 | return data->stringAtInternal(index); |
1239 | } |
1240 | |
1241 | QString fileName() const { return m_fileName; } |
1242 | QString finalUrlString() const { return m_finalUrlString; } |
1243 | |
1244 | Heap::Module *module() const { return m_module; } |
1245 | void setModule(Heap::Module *module) { m_module = module; } |
1246 | |
1247 | private: |
1248 | QString m_fileName; // initialized from data->sourceFileIndex |
1249 | QString m_finalUrlString; // initialized from data->finalUrlIndex |
1250 | |
1251 | Heap::Module *m_module = nullptr; |
1252 | }; |
1253 | |
1254 | class SaveableUnitPointer |
1255 | { |
1256 | Q_DISABLE_COPY_MOVE(SaveableUnitPointer) |
1257 | public: |
1258 | SaveableUnitPointer(const Unit *unit, quint32 temporaryFlags = Unit::StaticData) : |
1259 | unit(unit), |
1260 | temporaryFlags(temporaryFlags) |
1261 | { |
1262 | } |
1263 | |
1264 | ~SaveableUnitPointer() = default; |
1265 | |
1266 | template<typename Char> |
1267 | bool saveToDisk(const std::function<bool(const Char *, quint32)> &writer) const |
1268 | { |
1269 | auto cleanup = qScopeGuard([this]() { mutableFlags() ^= temporaryFlags; }); |
1270 | mutableFlags() |= temporaryFlags; |
1271 | return writer(data<Char>(), size()); |
1272 | } |
1273 | |
1274 | static bool writeDataToFile(const QString &outputFileName, const char *data, quint32 size, |
1275 | QString *errorString) |
1276 | { |
1277 | #if QT_CONFIG(temporaryfile) |
1278 | QSaveFile cacheFile(outputFileName); |
1279 | if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate) |
1280 | || cacheFile.write(data, size) != size |
1281 | || !cacheFile.commit()) { |
1282 | *errorString = cacheFile.errorString(); |
1283 | return false; |
1284 | } |
1285 | |
1286 | errorString->clear(); |
1287 | return true; |
1288 | #else |
1289 | Q_UNUSED(outputFileName) |
1290 | *errorString = QStringLiteral("features.temporaryfile is disabled." ); |
1291 | return false; |
1292 | #endif |
1293 | } |
1294 | |
1295 | private: |
1296 | const Unit *unit; |
1297 | quint32 temporaryFlags; |
1298 | |
1299 | quint32_le &mutableFlags() const |
1300 | { |
1301 | return const_cast<Unit *>(unit)->flags; |
1302 | } |
1303 | |
1304 | template<typename Char> |
1305 | const Char *data() const |
1306 | { |
1307 | Q_STATIC_ASSERT(sizeof(Char) == 1); |
1308 | const Char *dataPtr; |
1309 | memcpy(&dataPtr, &unit, sizeof(dataPtr)); |
1310 | return dataPtr; |
1311 | } |
1312 | |
1313 | quint32 size() const |
1314 | { |
1315 | return unit->unitSize; |
1316 | } |
1317 | }; |
1318 | |
1319 | |
1320 | } // CompiledData namespace |
1321 | } // QV4 namespace |
1322 | |
1323 | Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE); |
1324 | |
1325 | QT_END_NAMESPACE |
1326 | |
1327 | #endif |
1328 | |