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