1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef frontend_BinASTTokenReaderContext_h
8 #define frontend_BinASTTokenReaderContext_h
9
10 #include "mozilla/Array.h" // mozilla::Array
11 #include "mozilla/Assertions.h" // MOZ_ASSERT
12 #include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
13 #include "mozilla/IntegerRange.h" // mozilla::IntegerRange
14 #include "mozilla/Maybe.h" // mozilla::Maybe
15 #include "mozilla/RangedPtr.h" // mozilla::RangedPtr
16 #include "mozilla/Span.h" // mozilla::Span
17 #include "mozilla/Variant.h" // mozilla::Variant
18
19 #include <stddef.h> // size_t
20 #include <stdint.h> // uint8_t, uint32_t
21
22 #include "jstypes.h" // JS_PUBLIC_API
23 #include "frontend/BinASTRuntimeSupport.h" // CharSlice, BinASTSourceMetadata
24 #include "frontend/BinASTToken.h" // BinASTVariant, BinASTKind, BinASTField
25 #include "frontend/BinASTTokenReaderBase.h" // BinASTTokenReaderBase, SkippableSubTree
26 #include "js/AllocPolicy.h" // SystemAllocPolicy
27 #include "js/HashTable.h" // HashMap, DefaultHasher
28 #include "js/Result.h" // JS::Result, Ok, Error
29 #include "js/Vector.h" // js::Vector
30
31 class JS_PUBLIC_API JSAtom;
32 class JS_PUBLIC_API JSTracer;
33 struct JS_PUBLIC_API JSContext;
34
35 namespace js {
36
37 class ScriptSource;
38
39 namespace frontend {
40
41 class ErrorReporter;
42 class FunctionBox;
43
44 // The format treats several distinct models as the same.
45 //
46 // We use `NormalizedInterfaceAndField` as a proxy for `BinASTInterfaceAndField`
47 // to ensure that we always normalize into the canonical model.
48 struct NormalizedInterfaceAndField {
49 const BinASTInterfaceAndField identity_;
NormalizedInterfaceAndFieldNormalizedInterfaceAndField50 explicit NormalizedInterfaceAndField(BinASTInterfaceAndField identity)
51 : identity_(
52 identity == BinASTInterfaceAndField::
53 StaticMemberAssignmentTarget__Property
54 ? BinASTInterfaceAndField::StaticMemberExpression__Property
55 : identity) {}
56 };
57
58 template <typename T>
59 struct Split {
60 T prefix_;
61 T suffix_;
62 };
63
64 // A bunch of bits used to lookup a value in a Huffman table. In most cases,
65 // these are the 32 leading bits of the underlying bit stream.
66 //
67 // In a Huffman table, keys have variable bitlength. Consequently, we only know
68 // the bitlength of the key *after* we have performed the lookup. A
69 // `HuffmanLookup` is a data structure contained at least as many bits as
70 // needed to perform the lookup.
71 //
72 // Whenever a lookup is performed, the consumer MUST look at the `bitLength` of
73 // the returned `HuffmanKey` and consume as many bits from the bit stream.
74 struct HuffmanLookup {
HuffmanLookupHuffmanLookup75 HuffmanLookup(const uint32_t bits, const uint8_t bitLength)
76 // We zero out the highest `32 - bitLength_` bits.
77 : bits_(bitLength == 0
78 ? 0 // >> 32 is UB
79 : (bits & (uint32_t(0xFFFFFFFF) >> (32 - bitLength)))),
80 bitLength_(bitLength) {
81 MOZ_ASSERT(bitLength_ <= 32);
82 MOZ_ASSERT_IF(bitLength_ != 32 /* >> 32 is UB */, bits_ >> bitLength_ == 0);
83 }
84
85 // Return the `bitLength` leading bits of this superset, in the order
86 // expected to compare to a `HuffmanKey`. The order of bits and bytes
87 // is ensured by `BitBuffer`.
88 //
89 // Note: This only makes sense if `bitLength <= bitLength_`.
90 //
91 // So, for instance, if `leadingBits(4)` returns
92 // `0b_0000_0000__0000_0000__0000_0000__0000_0100`, this is
93 // equal to Huffman Key `0100`.
94 uint32_t leadingBits(const uint8_t bitLength) const;
95
96 // Split a HuffmanLookup into a prefix and a suffix.
97 //
98 // If the value holds at least `prefixLength` bits, the
99 // prefix consists in the first `prefixLength` bits and the
100 // suffix in the remaining bits.
101 //
102 // If the value holds fewer bits, the prefix consists in
103 // all the bits, with 0 padding at the end to ensure that
104 // the prefix contains exactly `prefixLength` bits.
105 Split<HuffmanLookup> split(const uint8_t prefixLength) const;
106
107 // The buffer holding the bits. At this stage, bits are stored
108 // in the same order as `HuffmanKey`. See the implementation of
109 // `BitBuffer` methods for more details about how this order
110 // is implemented.
111 //
112 // If `bitLength_ < 32`, the unused highest bits are guaranteed
113 // to be 0.
114 const uint32_t bits_;
115
116 // The actual length of buffer `bits_`.
117 //
118 // MUST be within `[0, 32]`.
119 //
120 // If `bitLength_ < 32`, it means that some of the highest bits are unused.
121 const uint8_t bitLength_;
122
123 // Return an iterable data structure representing all possible
124 // suffixes of this `HuffmanLookup` with `expectedBitLength`
125 // bits.
126 //
127 // If this `HuffmanLookup` is already at least `expectedBitLength`
128 // bits long, we truncate the `HuffmanLookup` to `expectedBitLength`
129 // bits and there is only one such suffix.
130 mozilla::detail::IntegerRange<size_t> suffixes(
131 uint8_t expectedBitLength) const;
132 };
133
134 // A Huffman Key.
135 struct HuffmanKey {
136 // Construct the HuffmanKey.
137 //
138 // `bits` and `bitLength` define a buffer containing the standard Huffman
139 // code for this key.
140 //
141 // For instance, if the Huffman code is `0100`,
142 // - `bits = 0b0000_0000__0000_0000__0000_0000__0000_0100`;
143 // - `bitLength = 4`.
144 HuffmanKey(const uint32_t bits, const uint8_t bitLength);
145
146 // The buffer holding the bits.
147 //
148 // For a Huffman code of `0100`
149 // - `bits_ = 0b0000_0000__0000_0000__0000_0000__0000_0100`;
150 //
151 // If `bitLength_ < 32`, the unused highest bits are guaranteed
152 // to be 0.
153 const uint32_t bits_;
154
155 // The actual length of buffer `bits_`.
156 //
157 // MUST be within `[0, 32]`.
158 //
159 // If `bitLength_ < 32`, it means that some of the highest bits are unused.
160 const uint8_t bitLength_;
161 };
162
163 // Symbol appears in the table.
164 // This class is used to store symbols in `*HuffmanTable` classes without having
165 // multiple implementation or different generated code for each type.
166 //
167 // This class doesn't store any tag to determine which kind of symbol it is.
168 // The consumer MUST use the correct `from*`/`to*` pair.
169 class alignas(8) BinASTSymbol {
170 private:
171 static const size_t NullAtomIndex = size_t(-1);
172
173 uint64_t asBits_;
174
BinASTSymbol(uint64_t asBits)175 explicit BinASTSymbol(uint64_t asBits) : asBits_(asBits) {}
176
fromRawBits(uint64_t asBits)177 static BinASTSymbol fromRawBits(uint64_t asBits) {
178 return BinASTSymbol(asBits);
179 }
180
181 public:
fromUnsignedLong(uint32_t i)182 static BinASTSymbol fromUnsignedLong(uint32_t i) { return fromRawBits(i); }
fromListLength(uint32_t i)183 static BinASTSymbol fromListLength(uint32_t i) { return fromRawBits(i); }
fromSubtableIndex(size_t i)184 static BinASTSymbol fromSubtableIndex(size_t i) { return fromRawBits(i); }
fromBool(bool b)185 static BinASTSymbol fromBool(bool b) { return fromRawBits(b); }
fromDouble(double d)186 static BinASTSymbol fromDouble(double d) {
187 return fromRawBits(mozilla::BitwiseCast<uint64_t>(d));
188 }
fromKind(BinASTKind k)189 static BinASTSymbol fromKind(BinASTKind k) {
190 return fromRawBits(uint64_t(k));
191 }
fromVariant(BinASTVariant v)192 static BinASTSymbol fromVariant(BinASTVariant v) {
193 return fromRawBits(uint64_t(v));
194 }
fromAtomIndex(size_t i)195 static BinASTSymbol fromAtomIndex(size_t i) { return fromRawBits(i); }
nullAtom()196 static BinASTSymbol nullAtom() { return fromRawBits(NullAtomIndex); }
197
toUnsignedLong()198 uint32_t toUnsignedLong() const { return uint32_t(asBits_); }
toListLength()199 uint32_t toListLength() const { return uint32_t(asBits_); }
toSubtableIndex()200 size_t toSubtableIndex() const { return size_t(asBits_); }
toBool()201 bool toBool() const { return bool(asBits_); }
toDouble()202 double toDouble() const { return mozilla::BitwiseCast<double>(asBits_); }
toKind()203 BinASTKind toKind() const { return BinASTKind(asBits_); }
toVariant()204 BinASTVariant toVariant() const { return BinASTVariant(asBits_); }
205
toAtomIndex()206 size_t toAtomIndex() const {
207 MOZ_ASSERT(!isNullAtom());
208 return toAtomIndexNoCheck();
209 }
210
isNullAtom()211 bool isNullAtom() const { return toAtomIndexNoCheck() == NullAtomIndex; }
212
213 private:
toAtomIndexNoCheck()214 size_t toAtomIndexNoCheck() const { return size_t(asBits_); }
215
216 friend class ::js::ScriptSource;
217 };
218
219 // An entry in a Huffman table.
220 class HuffmanEntry {
221 const HuffmanKey key_;
222 const BinASTSymbol value_;
223
224 public:
HuffmanEntry(HuffmanKey key,const BinASTSymbol & value)225 HuffmanEntry(HuffmanKey key, const BinASTSymbol& value)
226 : key_(key), value_(value) {}
227
HuffmanEntry(uint32_t bits,uint8_t bitLength,const BinASTSymbol & value)228 HuffmanEntry(uint32_t bits, uint8_t bitLength, const BinASTSymbol& value)
229 : key_(bits, bitLength), value_(value) {}
230
key()231 const HuffmanKey& key() const { return key_; };
value()232 const BinASTSymbol& value() const { return value_; };
233 };
234
235 // The result of lookup in Huffman table.
236 class HuffmanLookupResult {
237 uint8_t bitLength_;
238 const BinASTSymbol* value_;
239
HuffmanLookupResult(uint8_t bitLength,const BinASTSymbol * value)240 HuffmanLookupResult(uint8_t bitLength, const BinASTSymbol* value)
241 : bitLength_(bitLength), value_(value) {}
242
243 public:
found(uint8_t bitLength,const BinASTSymbol * value)244 static HuffmanLookupResult found(uint8_t bitLength,
245 const BinASTSymbol* value) {
246 MOZ_ASSERT(value);
247 return HuffmanLookupResult(bitLength, value);
248 }
249
notFound()250 static HuffmanLookupResult notFound() {
251 return HuffmanLookupResult(0, nullptr);
252 }
253
isFound()254 bool isFound() const { return !!value_; };
255
bitLength()256 uint8_t bitLength() const {
257 MOZ_ASSERT(isFound());
258 return bitLength_;
259 }
260
value()261 const BinASTSymbol& value() const {
262 MOZ_ASSERT(isFound());
263 return *value_;
264 }
265 };
266
267 // A flag that determines only whether a value is `null`.
268 // Used for optional interface.
269 enum class Nullable {
270 Null,
271 NonNull,
272 };
273
274 class TemporaryStorage;
275
276 // An implementation of Huffman Tables for single-entry table.
277 class SingleEntryHuffmanTable {
278 public:
SingleEntryHuffmanTable(const BinASTSymbol & value)279 explicit SingleEntryHuffmanTable(const BinASTSymbol& value) : value_(value) {}
280 SingleEntryHuffmanTable(SingleEntryHuffmanTable&& other) = default;
281
282 SingleEntryHuffmanTable() = delete;
283 SingleEntryHuffmanTable(SingleEntryHuffmanTable&) = delete;
284
285 // Lookup a value in the table.
286 // The key is 0-bit length and this always suceeds.
287 HuffmanLookupResult lookup(HuffmanLookup key) const;
288
289 // The number of values in the table.
length()290 size_t length() const { return 1; }
291
292 // Iterating in the order of insertion.
293 struct Iterator {
294 explicit Iterator(const BinASTSymbol* position);
295 void operator++();
296 const BinASTSymbol* operator*() const;
297 const BinASTSymbol* operator->() const;
298 bool operator==(const Iterator& other) const;
299 bool operator!=(const Iterator& other) const;
300
301 private:
302 const BinASTSymbol* position_;
303 };
begin()304 Iterator begin() const { return Iterator(&value_); }
end()305 Iterator end() const { return Iterator(nullptr); }
306
307 private:
308 BinASTSymbol value_;
309
310 friend class HuffmanPreludeReader;
311 friend class ::js::ScriptSource;
312 };
313
314 // An implementation of Huffman Tables for two-entry table.
315 class TwoEntriesHuffmanTable {
316 public:
317 TwoEntriesHuffmanTable() = default;
318 TwoEntriesHuffmanTable(TwoEntriesHuffmanTable&& other) noexcept = default;
319
320 // Initialize a Huffman table containing `numberOfSymbols`.
321 // Symbols must be added with `addSymbol`.
322 // If you initialize with `initStart`, you MUST call `initComplete()`
323 // at the end of initialization.
324 JS::Result<Ok> initStart(JSContext* cx, TemporaryStorage* tempStorage,
325 size_t numberOfSymbols, uint8_t maxBitLength);
326
327 JS::Result<Ok> initComplete(JSContext* cx, TemporaryStorage* tempStorage);
328
329 // Add a symbol to a value.
330 // The symbol is the `index`-th item in this table.
331 JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
332 const BinASTSymbol& value);
333
334 TwoEntriesHuffmanTable(TwoEntriesHuffmanTable&) = delete;
335
336 // Lookup a value in the table.
337 //
338 // The return of this method contains:
339 //
340 // - the resulting value (`nullptr` if the value is not in the table);
341 // - the number of bits in the entry associated to this value.
342 //
343 // Note that entries inside a single table are typically associated to
344 // distinct bit lengths. The caller is responsible for checking
345 // the result of this method and advancing the bitstream by
346 // `result.key().bitLength_` bits.
347 HuffmanLookupResult lookup(HuffmanLookup key) const;
348
349 struct Iterator {
350 explicit Iterator(const BinASTSymbol* position);
351 void operator++();
352 const BinASTSymbol* operator*() const;
353 const BinASTSymbol* operator->() const;
354 bool operator==(const Iterator& other) const;
355 bool operator!=(const Iterator& other) const;
356
357 private:
358 const BinASTSymbol* position_;
359 };
begin()360 Iterator begin() const { return Iterator(std::begin(values_)); }
end()361 Iterator end() const { return Iterator(std::end(values_)); }
362
363 // The number of values in the table.
length()364 size_t length() const { return 2; }
365
366 private:
367 // A buffer for the values added to this table.
368 BinASTSymbol values_[2] = {BinASTSymbol::fromBool(false),
369 BinASTSymbol::fromBool(false)};
370
371 friend class HuffmanPreludeReader;
372 friend class ::js::ScriptSource;
373 };
374
375 // An implementation of Huffman Tables as a vector designed to allow
376 // constant-time lookups at the expense of high space complexity.
377 //
378 // # Time complexity
379 //
380 // Lookups take constant time, which essentially consists in two
381 // simple vector lookups.
382 //
383 // # Space complexity
384 //
385 // After initialization, a `SingleLookupHuffmanTable`
386 // requires O(2 ^ max bit length in the table) space:
387 //
388 // - A vector `values_` containing one entry per symbol.
389 // - A vector `saturated_` containing exactly 2 ^ (max bit length in the
390 // table) entries, which we use to map any combination of `largestBitLength_`
391 // bits onto the only `HuffmanEntry` that may be reached by a prefix
392 // of these `largestBitLength_` bits. See below for more details.
393 //
394 // # Algorithm
395 //
396 // Consider the following Huffman table
397 //
398 // Symbol | Binary Code | Int value of Code | Bit Length
399 // ------ | ------------ | ----------------- | ----------
400 // A | 11000 | 24 | 5
401 // B | 11001 | 25 | 5
402 // C | 1101 | 13 | 4
403 // D | 100 | 4 | 3
404 // E | 101 | 5 | 3
405 // F | 111 | 7 | 3
406 // G | 00 | 0 | 2
407 // H | 01 | 1 | 2
408 //
409 // By definition of a Huffman Table, the Binary Codes represent
410 // paths in a Huffman Tree. Consequently, padding these codes
411 // to the end would not change the result.
412 //
413 // Symbol | Binary Code | Int value of Code | Bit Length
414 // ------ | ------------ | ----------------- | ----------
415 // A | 11000 | 24 | 5
416 // B | 11001 | 25 | 5
417 // C | 1101? | [26...27] | 4
418 // D | 100?? | [16...19] | 3
419 // E | 101?? | [20..23] | 3
420 // F | 111?? | [28..31] | 3
421 // G | 00??? | [0...7] | 2
422 // H | 01??? | [8...15] | 2
423 //
424 // Row "Int value of Code" now contains all possible values
425 // that may be expressed in 5 bits. By using these values
426 // as array indices, we may therefore represent the
427 // Huffman table as an array:
428 //
429 // Index | Symbol | Bit Length
430 // --------- | ---------- | -------------
431 // [0...7] | G | 2
432 // [8...15] | H | 2
433 // [16...19] | D | 3
434 // [20...23] | E | 3
435 // 24 | A | 5
436 // 25 | B | 5
437 // [26...27] | C | 4
438 // [28...31] | F | 3
439 //
440 // By using the next 5 bits in the bit buffer, we may, in
441 // a single lookup, determine the symbol and the bit length.
442 //
443 // In the current implementation, to save some space, we have
444 // two distinct arrays, one (`values_`) with a single instance of each
445 // symbols bit length, and one (`saturated_`) with indices into that
446 // array.
447 class SingleLookupHuffmanTable {
448 public:
449 // An index into table `values_`.
450 // We use `uint8_t` instead of `size_t` to limit the space
451 // used by the table.
452 using InternalIndex = uint8_t;
453
454 // An enum used to represent how this table is used.
455 // Used to perform additional DEBUG assertions.
456 enum Use {
457 // Used as a `Subtable` argument of a `MultiLookupHuffmanTable`.
458 LeafOfMultiLookupHuffmanTable,
459 // Used as its own table.
460 ToplevelTable,
461 // Used as a `shortKeys_` in a `MultiLookupHuffmanTable`.
462 ShortKeys,
463 };
464
465 // The largest bit length that may be represented by this table.
466 static const uint8_t MAX_BIT_LENGTH = sizeof(InternalIndex) * 8;
467
468 explicit SingleLookupHuffmanTable(
469 Use use = Use::LeafOfMultiLookupHuffmanTable)
470 : largestBitLength_(-1)
471 #ifdef DEBUG
472 ,
473 use_(use)
474 #endif // DEBUG
475 {
476 }
477 SingleLookupHuffmanTable(SingleLookupHuffmanTable&& other) = default;
478
479 // Initialize a Huffman table containing `numberOfSymbols`.
480 // Symbols must be added with `addSymbol`.
481 // If you initialize with `initStart`, you MUST call `initComplete()`
482 // at the end of initialization.
483 JS::Result<Ok> initStart(JSContext* cx, TemporaryStorage* tempStorage,
484 size_t numberOfSymbols, uint8_t maxBitLength);
485
486 JS::Result<Ok> initComplete(JSContext* cx, TemporaryStorage* tempStorage);
487
488 // Add a `(bit, bitLength) => value` mapping.
489 // The symbol is the `index`-th item in this table.
490 JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
491 const BinASTSymbol& value);
492
493 SingleLookupHuffmanTable(SingleLookupHuffmanTable&) = delete;
494
495 // Lookup a value in the table.
496 //
497 // The return of this method contains:
498 //
499 // - the resulting value (`nullptr` if the value is not in the table);
500 // - the number of bits in the entry associated to this value.
501 //
502 // Note that entries inside a single table are typically associated to
503 // distinct bit lengths. The caller is responsible for checking
504 // the result of this method and advancing the bitstream by
505 // `result.key().bitLength_` bits.
506 HuffmanLookupResult lookup(HuffmanLookup key) const;
507
508 // The number of values in the table.
length()509 size_t length() const { return values_.size(); }
510
511 // Iterating in the order of insertion.
512 struct Iterator {
513 explicit Iterator(const HuffmanEntry* position);
514 void operator++();
515 const BinASTSymbol* operator*() const;
516 const BinASTSymbol* operator->() const;
517 bool operator==(const Iterator& other) const;
518 bool operator!=(const Iterator& other) const;
519
520 private:
521 const HuffmanEntry* position_;
522 };
begin()523 Iterator begin() const { return Iterator(&values_[0]); }
end()524 Iterator end() const { return Iterator(&values_[0] + values_.size()); }
525
526 private:
527 // The entries in this Huffman Table, sorted in the order of insertion.
528 //
529 // Invariant (once `init*` has been called):
530 // - Length is the number of values inserted in the table.
531 // - for all i, `values_[i].bitLength_ <= largestBitLength_`.
532 mozilla::Span<HuffmanEntry> values_;
533
534 // The entries in this Huffman table, prepared for lookup.
535 //
536 // Invariant (once `init*` has been called):
537 // - Length is `1 << largestBitLength_`.
538 // - for all i, `saturated_[i] < values_.size()`
539 mozilla::Span<InternalIndex> saturated_;
540
541 friend class HuffmanDictionaryForMetadata;
542
543 // The maximal bitlength of a value in this table.
544 //
545 // Invariant (once `init*` has been called):
546 // - `largestBitLength_ <= MAX_CODE_BIT_LENGTH`
547 uint8_t largestBitLength_;
548
549 #ifdef DEBUG
550 Use use_;
551 #endif // DEBUG
552
553 friend class HuffmanPreludeReader;
554 friend class ::js::ScriptSource;
555 };
556
557 /// A table designed to support fast lookup in large sets of data.
558 /// In most cases, lookup will be slower than a `SingleLookupHuffmanTable`
559 /// but, particularly in heavily unbalanced trees, the table will
560 /// take ~2^prefix_len fewer internal entries than a `SingleLookupHuffmanTable`.
561 ///
562 /// Typically, use this table whenever codes range between 10 and 20 bits.
563 ///
564 /// # Time complexity
565 ///
566 /// A lookup in `MultiLookupHuffmanTable` will also take constant time:
567
568 /// - a constant-time lookup in a `SingleLookupHuffmanTable`, in case we only
569 /// need to look for a small key;
570 /// - if the above lookup fails:
571 /// - a constant-time lookup to determine into which suffix table to perform
572 /// the lookup;
573 /// - a constant-time lookup into the suffix table;
574 /// - a constant-time lookup into the array of values.
575 ///
576 ///
577 /// # Space complexity
578 ///
579 /// TBD. Highly dependent on the shape of the Huffman Tree.
580 ///
581 ///
582 /// # Algorithm
583 ///
584 /// Consider the following Huffman table
585 ///
586 /// Symbol | Binary Code | Bit Length
587 /// ------ | ------------ | ----------
588 /// A | 11000 | 5
589 /// B | 11001 | 5
590 /// C | 1101 | 4
591 /// D | 100 | 3
592 /// E | 101 | 3
593 /// F | 111 | 3
594 /// G | 00 | 2
595 /// H | 01 | 2
596 ///
597 /// Let us assume that we have somehow determined that:
598 ///
599 /// - we wish to store all values with a bit length of 2
600 /// or less in a fast access table.
601 /// - we wish to use a prefix length of 4.
602 ///
603 /// Note: These numbers of 2 and 4 are picked arbitrarily
604 /// for the example. Actual numbers used may vary.
605 ///
606 /// We first extract all values with a bit length of <= 2:
607 ///
608 /// Symbol | Binary Code | Bit Length
609 /// ------ | ------------ | ----------
610 /// G | 00 | 2
611 /// H | 01 | 2
612 ///
613 /// We store these values in a `SingleLookupHuffmanTable` for fast access.
614 /// We are now done with these values. Let's deal with the remaining values.
615 ///
616 /// Now, as our prefix length is 4, we precompute all possible 3-bit
617 /// prefixes and split the table across such prefixes.
618 ///
619 /// Prefix | Int Value of Prefix | Symbols | Max bit length
620 /// ------- | ------------------- | --------- | --------------
621 /// 0000 | 0 | | 0
622 /// 0001 | 1 | | 0
623 /// 0010 | 2 | | 0
624 /// 0011 | 3 | | 0
625 /// 0100 | 4 | | 0
626 /// 0101 | 5 | | 0
627 /// 0110 | 6 | | 0
628 /// 0111 | 7 | | 0
629 /// 1000 | 8 | D | 0
630 /// 1001 | 9 | D | 0
631 /// 1010 | 10 | E | 0
632 /// 1011 | 11 | E | 0
633 /// 1100 | 12 | A, B | 1
634 /// 1101 | 13 | C | 0
635 /// 1110 | 14 | F | 0
636 /// 1111 | 15 | F | 0
637 ///
638 /// For each prefix, we build the table containing the Symbols,
639 /// stripping prefix from the Binary Code.
640 /// - Prefixes 0000-01111
641 ///
642 /// Empty tables.
643 ///
644 /// - Prefixes 1000, 1001
645 ///
646 /// Symbol | Binary Code | Bit Length | Total Bit Length
647 /// ------ | ----------- | ---------- | --------------
648 /// D | (none) | 0 | 3
649 ///
650 /// - Prefixes 1010, 1011
651 ///
652 /// Symbol | Binary Code | Bit Length | Total Bit Length
653 /// ------ | ----------- | ---------- | --------------
654 /// E | (none) | 0 | 3
655 ///
656 /// - Prefix 1100
657 ///
658 /// Symbol | Binary Code | Bit Length | Total Bit Length
659 /// ------ | ----------- | ---------- | ----------------
660 /// A | 0 | 1 | 5
661 /// B | 1 | 1 | 5
662 ///
663 /// - Prefix 1101
664 ///
665 /// Symbol | Binary Code | Bit Length | Total Bit Length
666 /// ------ | ----------- | ---------- | ----------------
667 /// C | (none) | 0 | 4
668 ///
669 /// - Prefixes 1110, 1111
670 ///
671 /// Symbol | Binary Code | Bit Length | Total Bit Length
672 /// ------ | ----------- | ---------- | ----------------
673 /// F | (none) | 0 | 4
674 ///
675 ///
676 /// With this transformation, we have represented one table
677 /// with an initial max bit length of 5 as:
678 ///
679 /// - 1 SingleLookupValue table with a max bit length of 3;
680 /// - 8 empty tables;
681 /// - 7 tables with a max bit length of 0;
682 /// - 1 table with a max bit length of 1;
683 ///
684 /// Consequently, instead of storing 2^5 = 32 internal references,
685 /// as we would have done with a SingleLookupHuffmanTable, we only
686 /// need to store:
687 ///
688 /// - 1 subtable with 2^3 = 8 references;
689 /// - 7 subtables with 1 reference each;
690 /// - 1 subtable with 2^1 = 2 references.
691 template <typename Subtable, uint8_t PrefixBitLength>
692 class MultiLookupHuffmanTable {
693 public:
694 // The largest bit length that may be represented by this table.
695 static const uint8_t MAX_BIT_LENGTH =
696 PrefixBitLength + Subtable::MAX_BIT_LENGTH;
697
MultiLookupHuffmanTable()698 MultiLookupHuffmanTable()
699 : shortKeys_(SingleLookupHuffmanTable::Use::ShortKeys),
700 largestBitLength_(-1) {}
701 MultiLookupHuffmanTable(MultiLookupHuffmanTable&& other) = default;
702
703 // Initialize a Huffman table containing `numberOfSymbols`.
704 // Symbols must be added with `addSymbol`.
705 // If you initialize with `initStart`, you MUST call `initComplete()`
706 // at the end of initialization.
707 JS::Result<Ok> initStart(JSContext* cx, TemporaryStorage* tempStorage,
708 size_t numberOfSymbols, uint8_t largestBitLength);
709
710 JS::Result<Ok> initComplete(JSContext* cx, TemporaryStorage* tempStorage);
711
712 // Add a `(bit, bitLength) => value` mapping.
713 // The symbol is the `index`-th item in this table.
714 JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
715 const BinASTSymbol& value);
716
717 MultiLookupHuffmanTable(MultiLookupHuffmanTable&) = delete;
718
719 // Lookup a value in the table.
720 //
721 // The return of this method contains:
722 //
723 // - the resulting value (`nullptr` if the value is not in the table);
724 // - the number of bits in the entry associated to this value.
725 //
726 // Note that entries inside a single table are typically associated to
727 // distinct bit lengths. The caller is responsible for checking
728 // the result of this method and advancing the bitstream by
729 // `result.key().bitLength_` bits.
730 HuffmanLookupResult lookup(HuffmanLookup key) const;
731
732 // The number of values in the table.
length()733 size_t length() const { return values_.size(); }
734
735 // Iterating in the order of insertion.
736 struct Iterator {
737 explicit Iterator(const HuffmanEntry* position);
738 void operator++();
739 const BinASTSymbol* operator*() const;
740 const BinASTSymbol* operator->() const;
741 bool operator==(const Iterator& other) const;
742 bool operator!=(const Iterator& other) const;
743
744 private:
745 const HuffmanEntry* position_;
746 };
begin()747 Iterator begin() const { return Iterator(&values_[0]); }
end()748 Iterator end() const { return Iterator(&values_[0] + values_.size()); }
749
750 public:
751 // An index into table `values_`.
752 // We use `uint8_t` instead of `size_t` to limit the space
753 // used by the table.
754 using InternalIndex = uint8_t;
755
756 private:
757 // Fast lookup for values whose keys fit within 8 bits.
758 // Such values are not added to `suffixTables`.
759 SingleLookupHuffmanTable shortKeys_;
760
761 // The entries in this Huffman Table, sorted in the order of insertion.
762 //
763 // Invariant (once `init*` has been called):
764 // - Length is the number of values inserted in the table.
765 // - for all i, `values_[i].bitLength_ <= largestBitLength_`.
766 //
767 // FIXME: In a ThreeLookupsHuffmanTable, we currently store each value
768 // three times. We could at least get down to twice.
769 mozilla::Span<HuffmanEntry> values_;
770
771 // A mapping from 0..2^prefixBitLen such that index `i`
772 // maps to a subtable that holds all values associated
773 // with a key that starts with `HuffmanKey(i, prefixBitLen)`.
774 //
775 // Note that, to allow the use of smaller tables, keys
776 // inside the subtables have been stripped
777 // from the prefix `HuffmanKey(i, prefixBitLen)`.
778 mozilla::Span<Subtable> suffixTables_;
779
780 friend class HuffmanDictionaryForMetadata;
781
782 // The maximal bitlength of a value in this table.
783 //
784 // Invariant (once `init*` has been called):
785 // - `largestBitLength_ <= MAX_CODE_BIT_LENGTH`
786 uint8_t largestBitLength_;
787
788 friend class HuffmanPreludeReader;
789 friend class ::js::ScriptSource;
790 };
791
792 /// A Huffman table suitable for max bit lengths in [8, 14]
793 using TwoLookupsHuffmanTable =
794 MultiLookupHuffmanTable<SingleLookupHuffmanTable, 6>;
795
796 /// A Huffman table suitable for max bit lengths in [15, 20]
797 using ThreeLookupsHuffmanTable =
798 MultiLookupHuffmanTable<TwoLookupsHuffmanTable, 6>;
799
800 // The initial value of GenericHuffmanTable.implementation_, that indicates
801 // the table isn't yet initialized.
802 struct TableImplementationUninitialized {};
803
804 // Generic implementation of Huffman tables.
805 //
806 //
807 struct GenericHuffmanTable {
808 GenericHuffmanTable();
809
810 // Initialize a Huffman table containing a single value.
811 JS::Result<Ok> initWithSingleValue(JSContext* cx, const BinASTSymbol& value);
812
813 // Initialize a Huffman table containing `numberOfSymbols`.
814 // Symbols must be added with `addSymbol`.
815 // If you initialize with `initStart`, you MUST call `initComplete()`
816 // at the end of initialization.
817 JS::Result<Ok> initStart(JSContext* cx, TemporaryStorage* tempStorage,
818 size_t numberOfSymbols, uint8_t maxBitLength);
819
820 // Add a `(bit, bitLength) => value` mapping.
821 // The symbol is the `index`-th item in this table.
822 JS::Result<Ok> addSymbol(size_t index, uint32_t bits, uint8_t bitLength,
823 const BinASTSymbol& value);
824
825 JS::Result<Ok> initComplete(JSContext* cx, TemporaryStorage* tempStorage);
826
827 // The number of values in the table.
828 size_t length() const;
829
830 struct Iterator {
831 explicit Iterator(typename SingleEntryHuffmanTable::Iterator&&);
832 explicit Iterator(typename TwoEntriesHuffmanTable::Iterator&&);
833 explicit Iterator(typename SingleLookupHuffmanTable::Iterator&&);
834 explicit Iterator(typename TwoLookupsHuffmanTable::Iterator&&);
835 explicit Iterator(typename ThreeLookupsHuffmanTable::Iterator&&);
836 Iterator(Iterator&&) = default;
837 Iterator(const Iterator&) = default;
838 void operator++();
839 const BinASTSymbol* operator*() const;
840 const BinASTSymbol* operator->() const;
841 bool operator==(const Iterator& other) const;
842 bool operator!=(const Iterator& other) const;
843
844 private:
845 mozilla::Variant<typename SingleEntryHuffmanTable::Iterator,
846 typename TwoEntriesHuffmanTable::Iterator,
847 typename SingleLookupHuffmanTable::Iterator,
848 typename TwoLookupsHuffmanTable::Iterator,
849 typename ThreeLookupsHuffmanTable::Iterator>
850 implementation_;
851 };
852
853 // Iterating in the order of insertion.
854 Iterator begin() const;
855 Iterator end() const;
856
857 // Lookup a value in the table.
858 //
859 // The return of this method contains:
860 //
861 // - the resulting value (`nullptr` if the value is not in the table);
862 // - the number of bits in the entry associated to this value.
863 //
864 // Note that entries inside a single table are typically associated to
865 // distinct bit lengths. The caller is responsible for checking
866 // the result of this method and advancing the bitstream by
867 // `result.key().bitLength_` bits.
868 HuffmanLookupResult lookup(HuffmanLookup key) const;
869
870 // `true` if this table only contains values for `null` for maybe-interface
871 // table.
872 // This method MUST be used only for maybe-interface table.
isMaybeInterfaceAlwaysNullGenericHuffmanTable873 bool isMaybeInterfaceAlwaysNull() const {
874 MOZ_ASSERT(length() == 1 || length() == 2);
875
876 // By definition, we have either 1 or 2 values.
877 // By definition, if we have 2 values, one of them is not null.
878 if (length() == 2) {
879 return false;
880 }
881
882 // Otherwise, check the single value.
883 return begin()->toKind() == BinASTKind::_Null;
884 }
885
886 private:
887 mozilla::Variant<SingleEntryHuffmanTable, TwoEntriesHuffmanTable,
888 SingleLookupHuffmanTable, TwoLookupsHuffmanTable,
889 ThreeLookupsHuffmanTable, TableImplementationUninitialized>
890 implementation_;
891
892 friend class HuffmanDictionaryForMetadata;
893 friend class ::js::ScriptSource;
894 };
895
896 // Temporary space to allocate `T` typed items with less alloc/free calls,
897 // to reduce mutex call inside allocator.
898 //
899 // Items are preallocated in `alloc` call, with at least `Chunk::DefaultSize`
900 // items at once, and freed in the TemporaryStorageItem destructor all at once.
901 //
902 // This class is used inside TemporaryStorage.
903 template <typename T>
904 class TemporaryStorageItem {
905 class Chunk {
906 public:
907 // The number of `T` items per single chunk.
908 static const size_t DefaultSize = 1024;
909
910 // The next (older) chunk in the linked list.
911 Chunk* next_ = nullptr;
912
913 // The number of already-used items in this chunk.
914 size_t used_ = 0;
915
916 // The total number of allocated items in this chunk.
917 // This is usually `DefaultSize`, but becomes larger if the consumer
918 // tries to allocate more than `DefaultSize` items at once.
919 size_t size_ = 0;
920
921 // Start of the entries.
922 // The actual size is defined in TemporaryStorage::alloc.
923 T entries_[1];
924
925 Chunk() = default;
926 };
927
928 // The total number of used items in this storage.
929 size_t total_ = 0;
930
931 // The first (latest) chunk in the linked list.
932 Chunk* head_ = nullptr;
933
934 public:
935 TemporaryStorageItem() = default;
936
~TemporaryStorageItem()937 ~TemporaryStorageItem() {
938 Chunk* chunk = head_;
939 while (chunk) {
940 Chunk* next = chunk->next_;
941 js_free(chunk);
942 chunk = next;
943 }
944 }
945
946 // Allocate `count` number of `T` items and returns the pointer to the
947 // first item.
948 T* alloc(JSContext* cx, size_t count);
949
950 // The total number of used items in this storage.
total()951 size_t total() const { return total_; }
952 };
953
954 // Temporary storage used for dynamic allocations while reading the Huffman
955 // prelude. Once reading is complete, we move them to metadata.
956 //
957 // Each items are allocated with `TemporaryStorageItem`, with less alloc/free
958 // calls (See `TemporaryStorageItem` doc for more details).
959 class TemporaryStorage {
960 using InternalIndex = SingleLookupHuffmanTable::InternalIndex;
961
962 static_assert(sizeof(SingleLookupHuffmanTable::InternalIndex) ==
963 sizeof(TwoLookupsHuffmanTable::InternalIndex),
964 "InternalIndex should be same between tables");
965
966 TemporaryStorageItem<HuffmanEntry> huffmanEntries_;
967 TemporaryStorageItem<InternalIndex> internalIndices_;
968 TemporaryStorageItem<SingleLookupHuffmanTable> singleTables_;
969 TemporaryStorageItem<TwoLookupsHuffmanTable> twoTables_;
970
971 public:
972 TemporaryStorage() = default;
973
974 // Allocate `count` number of `T` items and returns the span to point the
975 // allocated items.
976 template <typename T>
977 JS::Result<mozilla::Span<T>> alloc(JSContext* cx, size_t count);
978
979 // The total number of used items in this storage.
numHuffmanEntries()980 size_t numHuffmanEntries() const { return huffmanEntries_.total(); }
numInternalIndices()981 size_t numInternalIndices() const { return internalIndices_.total(); }
numSingleTables()982 size_t numSingleTables() const { return singleTables_.total(); }
numTwoTables()983 size_t numTwoTables() const { return twoTables_.total(); }
984 };
985
986 // Handles the mapping from NormalizedInterfaceAndField and BinASTList to
987 // the index inside the list of huffman tables.
988 //
989 // The mapping from `(Interface, Field) -> index` and `List -> index` is
990 // extracted statically from the webidl specs.
991 class TableIdentity {
992 size_t index_;
993
994 static const size_t ListIdentityBase = BINAST_INTERFACE_AND_FIELD_LIMIT;
995
996 public:
997 // The maximum number of tables.
998 static const size_t Limit = ListIdentityBase + BINAST_NUMBER_OF_LIST_TYPES;
999
TableIdentity(NormalizedInterfaceAndField index)1000 explicit TableIdentity(NormalizedInterfaceAndField index)
1001 : index_(static_cast<size_t>(index.identity_)) {}
TableIdentity(BinASTList list)1002 explicit TableIdentity(BinASTList list)
1003 : index_(static_cast<size_t>(list) + ListIdentityBase) {}
1004
toIndex()1005 size_t toIndex() const { return index_; }
1006 };
1007
1008 class HuffmanDictionary;
1009
1010 // A Huffman dictionary for the current file, used after reading the dictionary.
1011 //
1012 // A Huffman dictionary consists in a (contiguous) set of Huffman tables
1013 // to predict field values and list lengths, and (contiguous) sets of
1014 // items pointed by each tables.
alignas(uintptr_t)1015 class alignas(uintptr_t) HuffmanDictionaryForMetadata {
1016 static const uint16_t UnreachableIndex = uint16_t(-1);
1017
1018 using InternalIndex = uint8_t;
1019
1020 HuffmanDictionaryForMetadata(size_t numTables, size_t numHuffmanEntries,
1021 size_t numInternalIndices,
1022 size_t numSingleTables, size_t numTwoTables)
1023 : numTables_(numTables),
1024 numHuffmanEntries_(numHuffmanEntries),
1025 numInternalIndices_(numInternalIndices),
1026 numSingleTables_(numSingleTables),
1027 numTwoTables_(numTwoTables) {}
1028
1029 // This class is allocated with extra payload for storing tables and items.
1030 // The full layout is the following:
1031 //
1032 // HuffmanDictionaryForMetadata
1033 // GenericHuffmanTable[numTables_]
1034 // HuffmanEntry[numHuffmanEntries_]
1035 // InternalIndex[numInternalIndices_]
1036 // SingleLookupHuffmanTable[numSingleTables_]
1037 // TwoLookupsHuffmanTable[numTwoTables_]
1038
1039 // Accessors for the above payload.
1040 // Each accessor returns the pointer to the first element of each array.
1041 mozilla::RangedPtr<GenericHuffmanTable> tablesBase() {
1042 auto p = reinterpret_cast<GenericHuffmanTable*>(
1043 reinterpret_cast<uintptr_t>(this + 1));
1044 return mozilla::RangedPtr<GenericHuffmanTable>(p, p, p + numTables_);
1045 }
1046 const mozilla::RangedPtr<GenericHuffmanTable> tablesBase() const {
1047 auto p = reinterpret_cast<GenericHuffmanTable*>(
1048 reinterpret_cast<uintptr_t>(this + 1));
1049 return mozilla::RangedPtr<GenericHuffmanTable>(p, p, p + numTables_);
1050 }
1051
1052 mozilla::RangedPtr<HuffmanEntry> huffmanEntriesBase() {
1053 auto p = reinterpret_cast<HuffmanEntry*>(
1054 reinterpret_cast<uintptr_t>(this + 1) +
1055 sizeof(GenericHuffmanTable) * numTables_);
1056 return mozilla::RangedPtr<HuffmanEntry>(p, p, p + numHuffmanEntries_);
1057 }
1058 const mozilla::RangedPtr<HuffmanEntry> huffmanEntriesBase() const {
1059 auto p = reinterpret_cast<HuffmanEntry*>(
1060 reinterpret_cast<uintptr_t>(this + 1) +
1061 sizeof(GenericHuffmanTable) * numTables_);
1062 return mozilla::RangedPtr<HuffmanEntry>(p, p, p + numHuffmanEntries_);
1063 }
1064
1065 mozilla::RangedPtr<InternalIndex> internalIndicesBase() {
1066 auto p = reinterpret_cast<InternalIndex*>(
1067 reinterpret_cast<uintptr_t>(this + 1) +
1068 sizeof(GenericHuffmanTable) * numTables_ +
1069 sizeof(HuffmanEntry) * numHuffmanEntries_);
1070 return mozilla::RangedPtr<InternalIndex>(p, p, p + numInternalIndices_);
1071 }
1072 const mozilla::RangedPtr<InternalIndex> internalIndicesBase() const {
1073 auto p = reinterpret_cast<InternalIndex*>(
1074 reinterpret_cast<uintptr_t>(this + 1) +
1075 sizeof(GenericHuffmanTable) * numTables_ +
1076 sizeof(HuffmanEntry) * numHuffmanEntries_);
1077 return mozilla::RangedPtr<InternalIndex>(p, p, p + numInternalIndices_);
1078 }
1079
1080 mozilla::RangedPtr<SingleLookupHuffmanTable> singleTablesBase() {
1081 auto p = reinterpret_cast<SingleLookupHuffmanTable*>(
1082 reinterpret_cast<uintptr_t>(this + 1) +
1083 sizeof(GenericHuffmanTable) * numTables_ +
1084 sizeof(HuffmanEntry) * numHuffmanEntries_ +
1085 sizeof(InternalIndex) * numInternalIndices_);
1086 return mozilla::RangedPtr<SingleLookupHuffmanTable>(p, p,
1087 p + numSingleTables_);
1088 }
1089 const mozilla::RangedPtr<SingleLookupHuffmanTable> singleTablesBase() const {
1090 auto p = reinterpret_cast<SingleLookupHuffmanTable*>(
1091 reinterpret_cast<uintptr_t>(this + 1) +
1092 sizeof(GenericHuffmanTable) * numTables_ +
1093 sizeof(HuffmanEntry) * numHuffmanEntries_ +
1094 sizeof(InternalIndex) * numInternalIndices_);
1095 return mozilla::RangedPtr<SingleLookupHuffmanTable>(p, p,
1096 p + numSingleTables_);
1097 }
1098
1099 mozilla::RangedPtr<TwoLookupsHuffmanTable> twoTablesBase() {
1100 auto p = reinterpret_cast<TwoLookupsHuffmanTable*>(
1101 reinterpret_cast<uintptr_t>(this + 1) +
1102 sizeof(GenericHuffmanTable) * numTables_ +
1103 sizeof(HuffmanEntry) * numHuffmanEntries_ +
1104 sizeof(InternalIndex) * numInternalIndices_ +
1105 sizeof(SingleLookupHuffmanTable) * numSingleTables_);
1106 return mozilla::RangedPtr<TwoLookupsHuffmanTable>(p, p, p + numTwoTables_);
1107 }
1108 const mozilla::RangedPtr<TwoLookupsHuffmanTable> twoTablesBase() const {
1109 auto p = reinterpret_cast<TwoLookupsHuffmanTable*>(
1110 reinterpret_cast<uintptr_t>(this + 1) +
1111 sizeof(GenericHuffmanTable) * numTables_ +
1112 sizeof(HuffmanEntry) * numHuffmanEntries_ +
1113 sizeof(InternalIndex) * numInternalIndices_ +
1114 sizeof(SingleLookupHuffmanTable) * numSingleTables_);
1115 return mozilla::RangedPtr<TwoLookupsHuffmanTable>(p, p, p + numTwoTables_);
1116 }
1117
1118 public:
1119 HuffmanDictionaryForMetadata() = delete;
1120 ~HuffmanDictionaryForMetadata();
1121
1122 // Create HuffmanDictionaryForMetadata by moving data from
1123 // HuffmanDictionary and items allocated in TemporaryStorage.
1124 //
1125 // After calling this, consumers shouldn't use `dictionary` and
1126 // `tempStorage`.
1127 static HuffmanDictionaryForMetadata* createFrom(
1128 HuffmanDictionary* dictionary, TemporaryStorage* tempStorage);
1129
1130 static HuffmanDictionaryForMetadata* create(size_t numTables,
1131 size_t numHuffmanEntries,
1132 size_t numInternalIndices,
1133 size_t numSingleTables,
1134 size_t numTwoTables);
1135
1136 void clearFromIncompleteInitialization(size_t numInitializedTables,
1137 size_t numInitializedSingleTables,
1138 size_t numInitializedTwoTables);
1139
1140 friend class ::js::ScriptSource;
1141
1142 private:
1143 // Returns the total required size of HuffmanDictionaryForMetadata with
1144 // extra payload to store items, in bytes.
1145 static size_t totalSize(size_t numTables, size_t numHuffmanEntries,
1146 size_t numInternalIndices, size_t numSingleTables,
1147 size_t numTwoTables);
1148
1149 // After allocating HuffmanDictionaryForMetadata with extra payload,
1150 // move data from HuffmanDictionary and items allocated in TemporaryStorage.
1151 //
1152 // Called by createFrom.
1153 void moveFrom(HuffmanDictionary* dictionary, TemporaryStorage* tempStorage);
1154
1155 public:
1156 bool isUnreachable(TableIdentity i) const {
1157 return tableIndices_[i.toIndex()] == UnreachableIndex;
1158 }
1159
1160 bool isReady(TableIdentity i) const {
1161 return tableIndices_[i.toIndex()] != UnreachableIndex;
1162 }
1163
1164 const GenericHuffmanTable& getTable(TableIdentity i) const {
1165 MOZ_ASSERT(isReady(i));
1166 return table(i);
1167 }
1168
1169 private:
1170 size_t numTables_ = 0;
1171 size_t numHuffmanEntries_ = 0;
1172 size_t numInternalIndices_ = 0;
1173 size_t numSingleTables_ = 0;
1174 size_t numTwoTables_ = 0;
1175
1176 uint16_t tableIndices_[TableIdentity::Limit] = {UnreachableIndex};
1177
1178 GenericHuffmanTable& table(TableIdentity i) {
1179 return tableAtIndex(tableIndices_[i.toIndex()]);
1180 }
1181
1182 const GenericHuffmanTable& table(TableIdentity i) const {
1183 return tableAtIndex(tableIndices_[i.toIndex()]);
1184 }
1185
1186 GenericHuffmanTable& tableAtIndex(size_t i) { return tablesBase()[i]; }
1187
1188 const GenericHuffmanTable& tableAtIndex(size_t i) const {
1189 return tablesBase()[i];
1190 }
1191
1192 public:
1193 size_t numTables() const { return numTables_; }
1194 size_t numHuffmanEntries() const { return numHuffmanEntries_; }
1195 size_t numInternalIndices() const { return numInternalIndices_; }
1196 size_t numSingleTables() const { return numSingleTables_; }
1197 size_t numTwoTables() const { return numTwoTables_; }
1198
1199 size_t huffmanEntryIndexOf(HuffmanEntry* entry) {
1200 MOZ_ASSERT(huffmanEntriesBase().get() <= entry &&
1201 entry < huffmanEntriesBase().get() + numHuffmanEntries());
1202 return entry - huffmanEntriesBase().get();
1203 }
1204
1205 size_t internalIndexIndexOf(InternalIndex* index) {
1206 MOZ_ASSERT(internalIndicesBase().get() <= index &&
1207 index < internalIndicesBase().get() + numInternalIndices());
1208 return index - internalIndicesBase().get();
1209 }
1210
1211 size_t singleTableIndexOf(SingleLookupHuffmanTable* table) {
1212 MOZ_ASSERT(singleTablesBase().get() <= table &&
1213 table < singleTablesBase().get() + numSingleTables());
1214 return table - singleTablesBase().get();
1215 }
1216
1217 size_t twoTableIndexOf(TwoLookupsHuffmanTable* table) {
1218 MOZ_ASSERT(twoTablesBase().get() <= table &&
1219 table < twoTablesBase().get() + numTwoTables());
1220 return table - twoTablesBase().get();
1221 }
1222 };
1223
1224 // When creating HuffmanDictionaryForMetadata with
1225 // HuffmanDictionaryForMetadata::create while decoding XDR,
1226 // it can fail and in that case the newly created HuffmanDictionaryForMetadata
1227 // instance is left with partially initialized payload, and then
1228 // HuffmanDictionaryForMetadata destructor can call destructor for
1229 // uninitialized memory.
1230 //
1231 // This class prevents it by calling destructors on already-initialized tables
1232 // and resetting HuffmanDictionaryForMetadata instances fields so that it
1233 // doesn't call destructors inside its destructor.
1234 //
1235 // Usage:
1236 // UniquePtr<frontend::HuffmanDictionaryForMetadata> newDict;
1237 // AutoClearHuffmanDictionaryForMetadata autoClear;
1238 //
1239 // auto dict = HuffmanDictionaryForMetadata::create(...);
1240 // if (!dict) { ... }
1241 //
1242 // autoClear.set(dict);
1243 // newDict.reset(dict);
1244 //
1245 // // When initializing table, call one of:
1246 // autoClear.addInitializedTable();
1247 // autoClear.addInitializedSingleTable();
1248 // autoClear.addInitializedTwoTable();
1249 //
1250 // // Do any fallible operation, and return on failure.
1251 // ...
1252 //
1253 // // When initialization finished.
1254 // autoClear.reset();
1255 //
1256 // return dict;
1257 class MOZ_STACK_CLASS AutoClearHuffmanDictionaryForMetadata {
1258 frontend::HuffmanDictionaryForMetadata* dictionary_;
1259
1260 size_t numInitializedTables_ = 0;
1261 size_t numInitializedSingleTables_ = 0;
1262 size_t numInitializedTwoTables_ = 0;
1263
1264 public:
AutoClearHuffmanDictionaryForMetadata()1265 AutoClearHuffmanDictionaryForMetadata() : dictionary_(nullptr) {}
1266
~AutoClearHuffmanDictionaryForMetadata()1267 ~AutoClearHuffmanDictionaryForMetadata() {
1268 if (dictionary_) {
1269 dictionary_->clearFromIncompleteInitialization(
1270 numInitializedTables_, numInitializedSingleTables_,
1271 numInitializedTwoTables_);
1272 }
1273 }
1274
set(frontend::HuffmanDictionaryForMetadata * dictionary)1275 void set(frontend::HuffmanDictionaryForMetadata* dictionary) {
1276 dictionary_ = dictionary;
1277 }
1278
reset()1279 void reset() { dictionary_ = nullptr; }
1280
addInitializedTable()1281 void addInitializedTable() { numInitializedTables_++; }
addInitializedSingleTable()1282 void addInitializedSingleTable() { numInitializedSingleTables_++; }
addInitializedTwoTable()1283 void addInitializedTwoTable() { numInitializedTwoTables_++; }
1284 };
1285
1286 // A Huffman dictionary for the current file, used while reading the dictionary.
1287 // When finished reading the dictionary, all data is moved to
1288 // `HuffmanDictionaryForMetadata`.
1289 //
1290 // A Huffman dictionary consists in a (contiguous) set of Huffman tables
1291 // to predict field values and list lengths.
1292 //
1293 // Each table can contain pointers to items allocated inside
1294 // `TemporaryStorageItem`.
1295 class HuffmanDictionary {
1296 // While reading the Huffman prelude, whenever we first encounter a
1297 // table with `Unreachable` status, we set its status with a `Initializing`
1298 // to mark that we should not attempt to read/initialize it again.
1299 // Once the table is initialized, it becomes `Ready`.
1300 enum class TableStatus : uint8_t {
1301 Unreachable,
1302 Initializing,
1303 Ready,
1304 };
1305
1306 public:
1307 HuffmanDictionary() = default;
1308 ~HuffmanDictionary();
1309
isUnreachable(TableIdentity i)1310 bool isUnreachable(TableIdentity i) const {
1311 return status_[i.toIndex()] == TableStatus::Unreachable;
1312 }
1313
isInitializing(TableIdentity i)1314 bool isInitializing(TableIdentity i) const {
1315 return status_[i.toIndex()] == TableStatus::Initializing;
1316 }
1317
isReady(TableIdentity i)1318 bool isReady(TableIdentity i) const {
1319 return status_[i.toIndex()] == TableStatus::Ready;
1320 }
1321
setInitializing(TableIdentity i)1322 void setInitializing(TableIdentity i) {
1323 status_[i.toIndex()] = TableStatus::Initializing;
1324 }
1325
1326 private:
setReady(TableIdentity i)1327 void setReady(TableIdentity i) { status_[i.toIndex()] = TableStatus::Ready; }
1328
1329 public:
createTable(TableIdentity i)1330 GenericHuffmanTable& createTable(TableIdentity i) {
1331 MOZ_ASSERT(isUnreachable(i) || isInitializing(i));
1332
1333 setReady(i);
1334
1335 tableIndices_[i.toIndex()] = nextIndex_++;
1336
1337 auto& t = table(i);
1338 new (mozilla::KnownNotNull, &t) GenericHuffmanTable();
1339
1340 return t;
1341 }
1342
getTable(TableIdentity i)1343 const GenericHuffmanTable& getTable(TableIdentity i) const {
1344 MOZ_ASSERT(isReady(i));
1345 return table(i);
1346 }
1347
numTables()1348 size_t numTables() const { return nextIndex_; }
1349
1350 private:
1351 // For the following purpose, tables are stored as an array of status
1352 // and a uninitialized buffer to store an array of tables.
1353 //
1354 // * In most case a single BinAST file doesn't use all tables
1355 // * GenericHuffmanTable constructor/destructor costs are not negligible,
1356 // and we don't want to call them for unused tables
1357 // * Initializing status for whether the table is used or not takes
1358 // less time if they're stored in contiguous memory, instead of
1359 // placed before each table (using `Variant` or `Maybe`)
1360 //
1361 // Some tables may be left Unreachable if they represent `(Interface, Field)`
1362 // pairs or lists that actually do not show up in the file.
1363 TableStatus status_[TableIdentity::Limit] = {TableStatus::Unreachable};
1364
1365 // Mapping from TableIdentity to the index into tables_.
1366 uint16_t tableIndices_[TableIdentity::Limit] = {0};
1367
1368 // The next uninitialized table's index in tables_.
1369 uint16_t nextIndex_ = 0;
1370
1371 // Huffman tables for either:
1372 // * `(Interface, Field)` pairs, used to decode the value of
1373 // `Interface::Field`.
1374 // * list lengths
1375 //
1376 // Tables in [0..nextIndex_] range are initialized.
1377 //
1378 // Semantically this is `GenericHuffmanTable tables_[TableIdentity::Limit]`,
1379 // but items are constructed lazily.
alignas(GenericHuffmanTable)1380 alignas(GenericHuffmanTable) char tables_[sizeof(GenericHuffmanTable) *
1381 TableIdentity::Limit];
1382
1383 GenericHuffmanTable& table(TableIdentity i) {
1384 return tableAtIndex(tableIndices_[i.toIndex()]);
1385 }
1386
table(TableIdentity i)1387 const GenericHuffmanTable& table(TableIdentity i) const {
1388 return tableAtIndex(tableIndices_[i.toIndex()]);
1389 }
1390
tableAtIndex(size_t i)1391 GenericHuffmanTable& tableAtIndex(size_t i) {
1392 return (reinterpret_cast<GenericHuffmanTable*>(tables_))[i];
1393 }
1394
tableAtIndex(size_t i)1395 const GenericHuffmanTable& tableAtIndex(size_t i) const {
1396 return (reinterpret_cast<const GenericHuffmanTable*>(tables_))[i];
1397 }
1398
1399 friend class HuffmanDictionaryForMetadata;
1400 };
1401
1402 /**
1403 * A token reader implementing the "context" serialization format for BinAST.
1404 *
1405 * This serialization format, which is also supported by the reference
1406 * implementation of the BinAST compression suite, is designed to be
1407 * space- and time-efficient.
1408 *
1409 * As other token readers for the BinAST:
1410 *
1411 * - the reader does not support error recovery;
1412 * - the reader does not support lookahead or pushback.
1413 */
1414 class MOZ_STACK_CLASS BinASTTokenReaderContext : public BinASTTokenReaderBase {
1415 using Base = BinASTTokenReaderBase;
1416
1417 public:
1418 class AutoList;
1419 class AutoTaggedTuple;
1420
1421 using CharSlice = BinaryASTSupport::CharSlice;
1422 using RootContext = BinASTTokenReaderBase::RootContext;
1423 using ListContext = BinASTTokenReaderBase::ListContext;
1424 using FieldContext = BinASTTokenReaderBase::FieldContext;
1425 using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
1426 using FieldOrListContext = BinASTTokenReaderBase::FieldOrListContext;
1427 using Chars = CharSlice;
1428
1429 public:
1430 /**
1431 * Construct a token reader.
1432 *
1433 * Does NOT copy the buffer.
1434 */
1435 BinASTTokenReaderContext(JSContext* cx, ErrorReporter* er,
1436 const uint8_t* start, const size_t length);
1437
1438 /**
1439 * Construct a token reader.
1440 *
1441 * Does NOT copy the buffer.
1442 */
1443 BinASTTokenReaderContext(JSContext* cx, ErrorReporter* er,
1444 const Vector<uint8_t>& chars);
1445
1446 ~BinASTTokenReaderContext();
1447
1448 // {readByte, readBuf, readVarU32} are implemented both for uncompressed
1449 // stream and brotli-compressed stream.
1450 //
1451 // Uncompressed variant is for reading the magic header, and compressed
1452 // variant is for reading the remaining part.
1453 //
1454 // Once compressed variant is called, the underlying uncompressed stream is
1455 // buffered and uncompressed variant cannot be called.
1456 enum class Compression { No, Yes };
1457
1458 // Determine what to do if we reach the end of the file.
1459 enum class EndOfFilePolicy {
1460 // End of file was not expected, raise an error.
1461 RaiseError,
1462 // End of file is ok, read as many bytes as possible.
1463 BestEffort
1464 };
1465
1466 protected:
1467 // A buffer of bits used to lookup data from the Huffman tables.
1468 // It may contain up to 64 bits.
1469 //
1470 // To interact with the buffer, see methods
1471 // - advanceBitBuffer()
1472 // - getHuffmanLookup()
1473 struct BitBuffer {
1474 BitBuffer();
1475
1476 // Return the HuffmanLookup for the next lookup in a Huffman table.
1477 // After calling this method, do not forget to call `advanceBitBuffer`.
1478 //
1479 // If `result.bitLength_ == 0`, you have reached the end of the stream.
1480 template <Compression Compression>
1481 MOZ_MUST_USE JS::Result<HuffmanLookup> getHuffmanLookup(
1482 BinASTTokenReaderContext& owner);
1483
1484 // Advance the bit buffer by `bitLength` bits.
1485 template <Compression Compression>
1486 void advanceBitBuffer(const uint8_t bitLength);
1487
1488 // Returns the number of buffered but unused bytes.
numUnusedBytesBitBuffer1489 size_t numUnusedBytes() const { return bitLength_ / 8; }
1490
1491 // Release all buffer.
flushBitBuffer1492 void flush() { bitLength_ = 0; }
1493
1494 private:
1495 // The contents of the buffer.
1496 //
1497 // - Bytes are added in the same order as the bytestream.
1498 // - Individual bits within bytes are mirrored.
1499 //
1500 // In other words, if the byte stream starts with
1501 // `0b_HGFE_DCBA`, `0b_PONM_LKJI`, `0b_0000_0000`,
1502 // .... `0b_0000_0000`, `bits_` will hold
1503 // `0b_0000_...0000__ABCD_EFGH__IJKL_MNOP`.
1504 //
1505 // Note: By opposition to `HuffmanKey` or `HuffmanLookup`,
1506 // the highest bits are NOT guaranteed to be `0`.
1507 uint64_t bits_;
1508
1509 // The number of elements in `bits_`.
1510 //
1511 // Until we start lookup up into Huffman tables, `bitLength_ == 0`.
1512 // Once we do, we refill the buffer before any lookup, i.e.
1513 // `MAX_PREFIX_BIT_LENGTH = 32 <= bitLength <= BIT_BUFFER_SIZE = 64`
1514 // until we reach the last few bytes of the stream,
1515 // in which case `length` decreases monotonically to 0.
1516 //
1517 // If `bitLength_ < BIT_BUFFER_SIZE = 64`, some of the highest
1518 // bits of `bits_` are unused.
1519 uint8_t bitLength_;
1520 } bitBuffer;
1521
1522 // Returns true if the brotli stream finished.
1523 bool isEOF() const;
1524
1525 /**
1526 * Read a single byte.
1527 */
1528 template <Compression compression>
1529 MOZ_MUST_USE JS::Result<uint8_t> readByte();
1530
1531 /**
1532 * Read several bytes.
1533 *
1534 * If the tokenizer has previously been poisoned, return an error.
1535 * If the end of file is reached, in the case of
1536 * EndOfFilePolicy::RaiseError, raise an error. Otherwise, update
1537 * `len` to indicate how many bytes have actually been read.
1538 */
1539 template <Compression compression, EndOfFilePolicy policy>
1540 MOZ_MUST_USE JS::Result<Ok> readBuf(uint8_t* bytes, uint32_t& len);
1541
1542 enum class FillResult { EndOfStream, Filled };
1543
1544 public:
1545 /**
1546 * Read the header of the file.
1547 */
1548 MOZ_MUST_USE JS::Result<Ok> readHeader();
1549
1550 /**
1551 * Read the footer of the tree, that contains lazy functions.
1552 */
1553 MOZ_MUST_USE JS::Result<Ok> readTreeFooter();
1554
1555 private:
1556 /**
1557 * Stop reading bit stream and unget unused buffer.
1558 */
1559 void flushBitStream();
1560
1561 public:
1562 /**
1563 * Read the string dictionary from the header of the file.
1564 */
1565 MOZ_MUST_USE JS::Result<Ok> readStringPrelude();
1566
1567 /**
1568 * Read the huffman dictionary from the header of the file.
1569 */
1570 MOZ_MUST_USE JS::Result<Ok> readHuffmanPrelude();
1571
1572 // --- Primitive values.
1573 //
1574 // Note that the underlying format allows for a `null` value for primitive
1575 // values.
1576 //
1577 // Reading will return an error either in case of I/O error or in case of
1578 // a format problem. Reading if an exception in pending is an error and
1579 // will cause assertion failures. Do NOT attempt to read once an exception
1580 // has been cleared: the token reader does NOT support recovery, by design.
1581
1582 /**
1583 * Read a single `true | false` value.
1584 */
1585 MOZ_MUST_USE JS::Result<bool> readBool(const FieldContext&);
1586
1587 /**
1588 * Read a single `number` value.
1589 */
1590 MOZ_MUST_USE JS::Result<double> readDouble(const FieldContext&);
1591
1592 /**
1593 * Read a single `string | null` value.
1594 *
1595 * Fails if that string is not valid UTF-8.
1596 */
1597 MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom(const FieldContext&);
1598 MOZ_MUST_USE JS::Result<JSAtom*> readAtom(const FieldContext&);
1599
1600 /**
1601 * Read a single IdentifierName value.
1602 */
1603 MOZ_MUST_USE JS::Result<JSAtom*> readMaybeIdentifierName(const FieldContext&);
1604 MOZ_MUST_USE JS::Result<JSAtom*> readIdentifierName(const FieldContext&);
1605
1606 /**
1607 * Read a single PropertyKey value.
1608 */
1609 MOZ_MUST_USE JS::Result<JSAtom*> readPropertyKey(const FieldContext&);
1610
1611 /**
1612 * Read a single `string | null` value.
1613 *
1614 * MAY check if that string is not valid UTF-8.
1615 */
1616 MOZ_MUST_USE JS::Result<Ok> readChars(Chars&, const FieldContext&);
1617
1618 /**
1619 * Read a single `BinASTVariant | null` value.
1620 */
1621 MOZ_MUST_USE JS::Result<BinASTVariant> readVariant(const ListContext&);
1622 MOZ_MUST_USE JS::Result<BinASTVariant> readVariant(const FieldContext&);
1623
1624 /**
1625 * Read over a single `[Skippable]` subtree value.
1626 *
1627 * This does *not* attempt to parse the subtree itself. Rather, the
1628 * returned `SkippableSubTree` contains the necessary information
1629 * to parse/tokenize the subtree at a later stage
1630 */
1631 MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree(
1632 const FieldContext&);
1633
1634 /**
1635 * Register lazy script for later modification.
1636 */
1637 MOZ_MUST_USE JS::Result<Ok> registerLazyScript(FunctionBox* lazy);
1638
1639 // --- Composite values.
1640 //
1641 // The underlying format does NOT allows for a `null` composite value.
1642 //
1643 // Reading will return an error either in case of I/O error or in case of
1644 // a format problem. Reading from a poisoned tokenizer is an error and
1645 // will cause assertion failures.
1646
1647 /**
1648 * Start reading a list.
1649 *
1650 * @param length (OUT) The number of elements in the list.
1651 * @param guard (OUT) A guard, ensuring that we read the list correctly.
1652 *
1653 * The `guard` is dedicated to ensuring that reading the list has consumed
1654 * exactly all the bytes from that list. The `guard` MUST therefore be
1655 * destroyed at the point where the caller has reached the end of the list.
1656 * If the caller has consumed too few/too many bytes, this will be reported
1657 * in the call go `guard.done()`.
1658 */
1659 MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, const ListContext&);
1660
1661 /**
1662 * Start reading a tagged tuple.
1663 *
1664 * @param tag (OUT) The tag of the tuple.
1665 * @param fields Ignored, provided for API compatibility.
1666 * @param guard (OUT) A guard, ensuring that we read the tagged tuple
1667 * correctly.
1668 *
1669 * The `guard` is dedicated to ensuring that reading the list has consumed
1670 * exactly all the bytes from that tuple. The `guard` MUST therefore be
1671 * destroyed at the point where the caller has reached the end of the tuple.
1672 * If the caller has consumed too few/too many bytes, this will be reported
1673 * in the call go `guard.done()`.
1674 *
1675 * @return out If the header of the tuple is invalid.
1676 */
enterInterface(BinASTKind & tag)1677 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag) {
1678 // We're entering a monomorphic interface, so the tag is encoded as 0 bits.
1679 MOZ_ASSERT(tag != BinASTKind::_Uninitialized);
1680 return Ok();
1681 }
enterInterface(BinASTKind & tag,const FieldOrRootContext &)1682 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
1683 const FieldOrRootContext&) {
1684 return enterInterface(tag);
1685 }
enterInterface(BinASTKind & tag,const FieldOrListContext &)1686 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
1687 const FieldOrListContext&) {
1688 return enterInterface(tag);
1689 }
enterInterface(BinASTKind & tag,const RootContext &)1690 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
1691 const RootContext&) {
1692 return enterInterface(tag);
1693 }
enterInterface(BinASTKind & tag,const ListContext &)1694 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
1695 const ListContext&) {
1696 return enterInterface(tag);
1697 }
enterInterface(BinASTKind & tag,const FieldContext &)1698 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
1699 const FieldContext&) {
1700 return enterInterface(tag);
1701 }
enterOptionalInterface(BinASTKind & tag,const FieldOrRootContext & context)1702 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
1703 BinASTKind& tag, const FieldOrRootContext& context) {
1704 return enterSum(tag, context);
1705 }
enterOptionalInterface(BinASTKind & tag,const FieldOrListContext & context)1706 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
1707 BinASTKind& tag, const FieldOrListContext& context) {
1708 return enterSum(tag, context);
1709 }
enterOptionalInterface(BinASTKind & tag,const RootContext & context)1710 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
1711 BinASTKind& tag, const RootContext& context) {
1712 return enterSum(tag, context);
1713 }
enterOptionalInterface(BinASTKind & tag,const ListContext & context)1714 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
1715 BinASTKind& tag, const ListContext& context) {
1716 return enterSum(tag, context);
1717 }
enterOptionalInterface(BinASTKind & tag,const FieldContext & context)1718 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
1719 BinASTKind& tag, const FieldContext& context) {
1720 return enterSum(tag, context);
1721 }
1722 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag,
1723 const FieldOrRootContext&);
1724 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag,
1725 const FieldOrListContext&);
1726 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const RootContext&);
1727 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const ListContext&);
1728 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const FieldContext&);
1729
1730 /**
1731 * Read a single unsigned long.
1732 */
1733 MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong(const FieldContext&);
1734 MOZ_MUST_USE JS::Result<uint32_t> readUnpackedLong();
1735
1736 private:
1737 MOZ_MUST_USE JS::Result<BinASTKind> readTagFromTable(
1738 const BinASTInterfaceAndField&);
1739
1740 MOZ_MUST_USE JS::Result<BinASTSymbol> readFieldFromTable(
1741 const BinASTInterfaceAndField&);
1742
1743 /**
1744 * Report an "invalid value error".
1745 */
1746 MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidValue();
1747
1748 /**
1749 * Report a "value not in prelude".
1750 */
1751 MOZ_MUST_USE ErrorResult<JS::Error&> raiseNotInPrelude();
1752
1753 private:
1754 /**
1755 * Read a single uint32_t.
1756 */
1757 template <Compression compression>
1758 MOZ_MUST_USE JS::Result<uint32_t> readVarU32();
1759
1760 template <EndOfFilePolicy policy>
1761 MOZ_MUST_USE JS::Result<Ok> handleEndOfStream();
1762
1763 template <EndOfFilePolicy policy>
1764 MOZ_MUST_USE JS::Result<Ok> readBufCompressedAux(uint8_t* bytes,
1765 uint32_t& len);
1766
1767 private:
1768 enum class MetadataOwnership { Owned, Unowned };
1769 MetadataOwnership metadataOwned_ = MetadataOwnership::Owned;
1770 BinASTSourceMetadataContext* metadata_;
1771
1772 const uint8_t* posBeforeTree_;
1773
1774 // Lazy script created while reading the tree.
1775 // After reading tree, the start/end offset are set to correct value.
1776 Vector<FunctionBox*> lazyScripts_;
1777
1778 public:
1779 BinASTTokenReaderContext(const BinASTTokenReaderContext&) = delete;
1780 BinASTTokenReaderContext(BinASTTokenReaderContext&&) = delete;
1781 BinASTTokenReaderContext& operator=(BinASTTokenReaderContext&) = delete;
1782
1783 public:
1784 void traceMetadata(JSTracer* trc);
1785 BinASTSourceMetadata* takeMetadata();
1786 MOZ_MUST_USE JS::Result<Ok> initFromScriptSource(ScriptSource* scriptSource);
1787
1788 protected:
1789 friend class HuffmanPreludeReader;
1790
1791 public:
1792 // The following classes are used whenever we encounter a tuple/tagged
1793 // tuple/list to make sure that:
1794 //
1795 // - if the construct "knows" its byte length, we have exactly consumed all
1796 // the bytes (otherwise, this means that the file is corrupted, perhaps on
1797 // purpose, so we need to reject the stream);
1798 // - if the construct has a footer, once we are done reading it, we have
1799 // reached the footer (this is to aid with debugging).
1800 //
1801 // In either case, the caller MUST call method `done()` of the guard once
1802 // it is done reading the tuple/tagged tuple/list, to report any pending
1803 // error.
1804
1805 // Base class used by other Auto* classes.
1806 class MOZ_STACK_CLASS AutoBase {
1807 protected:
AutoBase(BinASTTokenReaderContext & reader)1808 explicit AutoBase(BinASTTokenReaderContext& reader)
1809 #ifdef DEBUG
1810 : initialized_(false),
1811 reader_(reader)
1812 #endif
1813 {
1814 }
~AutoBase()1815 ~AutoBase() {
1816 // By now, the `AutoBase` must have been deinitialized by calling
1817 // `done()`. The only case in which we can accept not calling `done()` is
1818 // if we have bailed out because of an error.
1819 MOZ_ASSERT_IF(initialized_, reader_.hasRaisedError());
1820 }
1821
1822 friend BinASTTokenReaderContext;
1823
1824 public:
init()1825 inline void init() {
1826 #ifdef DEBUG
1827 initialized_ = true;
1828 #endif
1829 }
1830
done()1831 inline MOZ_MUST_USE JS::Result<Ok> done() {
1832 #ifdef DEBUG
1833 initialized_ = false;
1834 #endif
1835 return Ok();
1836 }
1837
1838 protected:
1839 #ifdef DEBUG
1840 bool initialized_;
1841 BinASTTokenReaderContext& reader_;
1842 #endif
1843 };
1844
1845 // Guard class used to ensure that `enterList` is used properly.
1846 class MOZ_STACK_CLASS AutoList : public AutoBase {
1847 public:
AutoList(BinASTTokenReaderContext & reader)1848 explicit AutoList(BinASTTokenReaderContext& reader) : AutoBase(reader) {}
1849 };
1850
1851 // Guard class used to ensure that `enterTaggedTuple` is used properly.
1852 class MOZ_STACK_CLASS AutoTaggedTuple : public AutoBase {
1853 public:
AutoTaggedTuple(BinASTTokenReaderContext & reader)1854 explicit AutoTaggedTuple(BinASTTokenReaderContext& reader)
1855 : AutoBase(reader) {}
1856 };
1857
1858 // Compare a `Chars` and a string literal (ONLY a string literal).
1859 template <size_t N>
equals(const Chars & left,const char (& right)[N])1860 static bool equals(const Chars& left, const char (&right)[N]) {
1861 MOZ_ASSERT(N > 0);
1862 MOZ_ASSERT(right[N - 1] == 0);
1863 if (left.byteLen_ + 1 /* implicit NUL */ != N) {
1864 return false;
1865 }
1866
1867 if (!std::equal(left.start_, left.start_ + left.byteLen_, right)) {
1868 return false;
1869 }
1870
1871 return true;
1872 }
1873 };
1874
1875 } // namespace frontend
1876 } // namespace js
1877
1878 #endif // frontend_BinASTTokenReaderContext_h
1879