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 #include "frontend/BinASTTokenReaderContext.h"
8 
9 #include "mozilla/OperatorNewExtensions.h"  // mozilla::KnownNotNull
10 #include "mozilla/Result.h"                 // MOZ_TRY*
11 #include "mozilla/ScopeExit.h"              // mozilla::MakeScopeExit
12 
13 #include <limits>    // std::numeric_limits
14 #include <string.h>  // memchr, memcmp, memmove
15 
16 #include "ds/FixedLengthVector.h"    // FixedLengthVector
17 #include "ds/Sort.h"                 // MergeSort
18 #include "frontend/BinAST-macros.h"  // BINJS_TRY*, BINJS_MOZ_TRY*
19 #include "gc/Rooting.h"              // RootedAtom
20 #include "js/AllocPolicy.h"          // SystemAllocPolicy
21 #include "js/UniquePtr.h"            // js::UniquePtr
22 #include "js/Utility.h"              // js_free
23 #include "js/Vector.h"               // js::Vector
24 #include "util/StringBuffer.h"       // StringBuffer
25 #include "vm/JSAtom.h"               // AtomizeWTF8Chars
26 #include "vm/JSScript.h"             // ScriptSource
27 
28 namespace js::frontend {
29 
30 #ifdef BINAST_CX_MAGIC_HEADER  // Context 0.1 doesn't implement the planned
31                                // magic header.
32 
33 // The magic header, at the start of every binjs file.
34 const char CX_MAGIC_HEADER[] =
35     "\x89"
36     "BJS\r\n\0\n";
37 
38 // The latest format version understood by this tokenizer.
39 const uint32_t MAGIC_FORMAT_VERSION = 2;
40 
41 #endif  // BINAST_CX_MAGIC_HEADER
42 
43 // The maximal length of a single Huffman code, in bits.
44 //
45 // Hardcoded in the format.
46 const uint8_t MAX_CODE_BIT_LENGTH = 20;
47 
48 // The maximal number of bits needed to represent bit lengths.
49 const uint8_t MAX_BIT_LENGTH_BIT_LENGTH = 5;
50 static_assert(1 << (MAX_BIT_LENGTH_BIT_LENGTH - 1) < MAX_CODE_BIT_LENGTH,
51               "MAX_BIT_LENGTH_BIT_LENGTH - 1 bits MUST be insufficient to "
52               "store MAX_CODE_BIT_LENGTH");
53 static_assert(
54     1 << MAX_BIT_LENGTH_BIT_LENGTH >= MAX_CODE_BIT_LENGTH,
55     "MAX_BIT_LENGTH bits MUST be sufficient to store MAX_CODE_BIT_LENGTH");
56 
57 // The maximal length of a Huffman prefix.
58 //
59 // This is distinct from MAX_CODE_BIT_LENGTH as we commonly load
60 // more bites than necessary, just to be on the safe side.
61 const uint8_t MAX_PREFIX_BIT_LENGTH = 32;
62 
63 // The length of the bit buffer, in bits.
64 const uint8_t BIT_BUFFER_SIZE = 64;
65 
66 // Number of bits into the `bitBuffer` read at each step.
67 const uint8_t BIT_BUFFER_READ_UNIT = 8;
68 
69 // Hardcoded limits to avoid allocating too eagerly.
70 const uint32_t MAX_NUMBER_OF_SYMBOLS = 32768;
71 const uint32_t MAX_LIST_LENGTH =
72     std::min((uint32_t)32768, NativeObject::MAX_DENSE_ELEMENTS_COUNT);
73 const size_t HUFFMAN_STACK_INITIAL_CAPACITY = 1024;
74 
75 // The number of elements in each sum.
76 // Note: `extern` is needed to forward declare.
77 extern const size_t SUM_LIMITS[BINAST_NUMBER_OF_SUM_TYPES];
78 
79 // For each sum, an array of BinASTKind corresponding to the possible value.
80 // The length of `SUM_RESOLUTIONS[i]` is `SUM_LIMITS[i]`.
81 // Note: `extern` is needed to forward declare.
82 extern const BinASTKind* SUM_RESOLUTIONS[BINAST_NUMBER_OF_SUM_TYPES];
83 
84 // The number of elements in each enum.
85 // Note: `extern` is needed to forward declare.
86 extern const size_t STRING_ENUM_LIMITS[BINASTSTRINGENUM_LIMIT];
87 
88 // For each string enum, an array of BinASTVariant corresponding to the possible
89 // value. The length of `STRING_ENUM_RESOLUTIONS[i]` is `STRING_ENUM_LIMITS[i]`.
90 // Note: `extern` is needed to forward declare.
91 extern const BinASTVariant* STRING_ENUM_RESOLUTIONS[BINASTSTRINGENUM_LIMIT];
92 
93 using Compression = BinASTTokenReaderContext::Compression;
94 using EndOfFilePolicy = BinASTTokenReaderContext::EndOfFilePolicy;
95 
96 #define WRAP_INTERFACE(TYPE) Interface::Maker(BinASTKind::TYPE)
97 #define WRAP_MAYBE_INTERFACE(TYPE) MaybeInterface::Maker(BinASTKind::TYPE)
98 #define WRAP_PRIMITIVE(TYPE) TYPE
99 #define WRAP_LIST(TYPE, _) List::Maker(BinASTList::TYPE)
100 #define WRAP_SUM(TYPE) Sum::Maker(BinASTSum::TYPE)
101 #define WRAP_MAYBE_SUM(TYPE) MaybeSum::Maker(BinASTSum::TYPE)
102 #define WRAP_STRING_ENUM(TYPE) StringEnum::Maker(BinASTStringEnum::TYPE)
103 #define WRAP_MAYBE_STRING_ENUM(TYPE) \
104   MaybeStringEnum::Maker(BinASTStringEnum::TYPE)
105 
106 using CharSlice = BinaryASTSupport::CharSlice;
107 using Chars = BinASTTokenReaderContext::Chars;
108 
109 // Read Huffman tables from the prelude.
110 class HuffmanPreludeReader {
111  public:
112   // Construct a prelude reader.
HuffmanPreludeReader(JSContext * cx,BinASTTokenReaderContext & reader)113   HuffmanPreludeReader(JSContext* cx, BinASTTokenReaderContext& reader)
114       : cx_(cx),
115         reader_(reader),
116         dictionary_(nullptr),
117         stack_(cx_),
118         auxStorageLength_(cx_) {}
119 
120   // Start reading the prelude.
121   MOZ_MUST_USE JS::Result<HuffmanDictionaryForMetadata*> run(
122       size_t initialCapacity);
123 
124  private:
125   JSContext* cx_;
126   BinASTTokenReaderContext& reader_;
127 
128   // The dictionary we're currently constructing.
129   UniquePtr<HuffmanDictionary> dictionary_;
130 
131   // Temporary storage for items allocated while reading huffman prelude.
132   // All items are freed after reading huffman prelude.
133   TemporaryStorage tempStorage_;
134 
135  public:
136   // Tables may have different representations in the stream.
137   //
138   // The value of each variant maps to the value inside the stream.
139   enum TableHeader : uint8_t {
140     // Table is optimized to represent a single value.
141     SingleValue = 0x00,
142 
143     // General table, with any number of values.
144     MultipleValues = 0x01,
145 
146     // A special table that contains no value because it is never used.
147     Unreachable = 0x02,
148   };
149 
150   // --- Representation of the grammar.
151   //
152   // We need to walk the grammar to read the Huffman tables. In this
153   // implementation, we represent the grammar as a variant type `Entry`,
154   // composed of subclasses of `EntryBase`.
155   //
156   // Note that, while some grammar constructions are theoretically possible
157   // (e.g. `bool?`), we only implement constructions that actually appear in the
158   // webidl. Future constructions may be implemented later.
159 
160   // Base class for grammar entries.
161   struct EntryBase {
162     // The field we are currently reading.
163     const NormalizedInterfaceAndField identity_;
164 
EntryBasejs::frontend::HuffmanPreludeReader::EntryBase165     explicit EntryBase(const NormalizedInterfaceAndField identity)
166         : identity_(identity) {}
167   };
168 
169   // Grammar entries for values that are represented by indexed in a set
170   // specified by the grammar, e.g booleans, string enums, sums etc
171   struct EntryIndexed : EntryBase {
172     // We use `Foo::Indexed` during template substitution to accept
173     // subclasses of `EntryIndexed`.
174     using Indexed = Ok;
EntryIndexedjs::frontend::HuffmanPreludeReader::EntryIndexed175     explicit EntryIndexed(const NormalizedInterfaceAndField identity)
176         : EntryBase(identity) {}
177   };
178 
179   struct EntryExplicit : EntryBase {
180     // We use `Foo::Explicit` during template substitution to accept
181     // subclasses of `EntryExplicit`.
182     using Explicit = Ok;
EntryExplicitjs::frontend::HuffmanPreludeReader::EntryExplicit183     explicit EntryExplicit(const NormalizedInterfaceAndField identity)
184         : EntryBase(identity) {}
185   };
186 
187   // A string.
188   // May be a literal string, identifier name or property key. May not be null.
189   struct String : EntryExplicit {
Stringjs::frontend::HuffmanPreludeReader::String190     explicit String(const NormalizedInterfaceAndField identity)
191         : EntryExplicit(identity) {}
192   };
193   using IdentifierName = String;
194   using PropertyKey = String;
195 
196   // An optional string.
197   // May be a literal string, identifier name or property key.
198   struct MaybeString : EntryExplicit {
MaybeStringjs::frontend::HuffmanPreludeReader::MaybeString199     explicit MaybeString(const NormalizedInterfaceAndField identity)
200         : EntryExplicit(identity) {}
201   };
202   using MaybeIdentifierName = MaybeString;
203   using MaybePropertyKey = MaybeString;
204 
205   // A JavaScript number. May not be null.
206   struct Number : EntryExplicit {
Numberjs::frontend::HuffmanPreludeReader::Number207     explicit Number(const NormalizedInterfaceAndField identity)
208         : EntryExplicit(identity) {}
209   };
210 
211   // A 32-bit integer. May not be null.
212   struct UnsignedLong : EntryExplicit {
UnsignedLongjs::frontend::HuffmanPreludeReader::UnsignedLong213     explicit UnsignedLong(const NormalizedInterfaceAndField identity)
214         : EntryExplicit(identity) {}
215   };
216 
217   // A boolean. May not be null.
218   struct Boolean : EntryIndexed {
Booleanjs::frontend::HuffmanPreludeReader::Boolean219     explicit Boolean(const NormalizedInterfaceAndField identity)
220         : EntryIndexed(identity) {}
221 
222     // Comparing booleans.
223     //
224     // As 0 == False < True == 1, we only compare indices.
lessThanjs::frontend::HuffmanPreludeReader::Boolean225     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
226       MOZ_ASSERT(aIndex <= 1);
227       MOZ_ASSERT(bIndex <= 1);
228       return aIndex < bIndex;
229     }
230   };
231 
232   // A field encoding a lazy offset.
233   struct Lazy : EntryExplicit {
Lazyjs::frontend::HuffmanPreludeReader::Lazy234     explicit Lazy(const NormalizedInterfaceAndField identity)
235         : EntryExplicit(identity) {}
236   };
237 
238   // A value of a given interface. May not be null.
239   struct Interface : EntryIndexed {
240     // The kind of the interface.
241     const BinASTKind kind_;
Interfacejs::frontend::HuffmanPreludeReader::Interface242     Interface(const NormalizedInterfaceAndField identity, BinASTKind kind)
243         : EntryIndexed(identity), kind_(kind) {}
244 
245     // Utility struct, used in macros to call the constructor as
246     // `Interface::Maker(kind)(identity)`.
247     struct Maker {
248       const BinASTKind kind_;
Makerjs::frontend::HuffmanPreludeReader::Interface::Maker249       explicit Maker(BinASTKind kind) : kind_(kind) {}
operator ()js::frontend::HuffmanPreludeReader::Interface::Maker250       Interface operator()(const NormalizedInterfaceAndField identity) {
251         return Interface(identity, kind_);
252       }
253     };
254   };
255 
256   // An optional value of a given interface.
257   struct MaybeInterface : EntryIndexed {
258     // The kind of the interface.
259     const BinASTKind kind_;
260 
261     // Comparing optional interfaces.
262     //
263     // As 0 == Null < _ == 1, we only compare indices.
lessThanjs::frontend::HuffmanPreludeReader::MaybeInterface264     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
265       MOZ_ASSERT(aIndex <= 1);
266       MOZ_ASSERT(bIndex <= 1);
267       return aIndex < bIndex;
268     }
269 
MaybeInterfacejs::frontend::HuffmanPreludeReader::MaybeInterface270     MaybeInterface(const NormalizedInterfaceAndField identity, BinASTKind kind)
271         : EntryIndexed(identity), kind_(kind) {}
272 
273     // The max number of symbols in a table for this type.
maxMumberOfSymbolsjs::frontend::HuffmanPreludeReader::MaybeInterface274     size_t maxMumberOfSymbols() const { return 2; }
275 
276     // Utility struct, used in macros to call the constructor as
277     // `MaybeInterface::Maker(kind)(identity)`.
278     struct Maker {
279       const BinASTKind kind_;
Makerjs::frontend::HuffmanPreludeReader::MaybeInterface::Maker280       explicit Maker(BinASTKind kind) : kind_(kind) {}
operator ()js::frontend::HuffmanPreludeReader::MaybeInterface::Maker281       MaybeInterface operator()(const NormalizedInterfaceAndField identity) {
282         return MaybeInterface(identity, kind_);
283       }
284     };
285   };
286 
287   // A FrozenArray. May not be null.
288   //
289   // In practice, this type is used to represent the length of the list.
290   // Once we have read the model for the length of the list, we push a
291   // `ListContents` to read the model for the contents of the list.
292   struct List : EntryExplicit {
293     // The type of the list, e.g. list of numbers.
294     // All lists with the same type share a model for their length.
295     const BinASTList contents_;
296 
Listjs::frontend::HuffmanPreludeReader::List297     List(const NormalizedInterfaceAndField identity, const BinASTList contents)
298         : EntryExplicit(identity), contents_(contents) {}
299 
300     // Utility struct, used in macros to call the constructor as
301     // `List::Maker(kind)(identity)`.
302     struct Maker {
303       const BinASTList contents_;
Makerjs::frontend::HuffmanPreludeReader::List::Maker304       explicit Maker(BinASTList contents) : contents_(contents) {}
operator ()js::frontend::HuffmanPreludeReader::List::Maker305       List operator()(const NormalizedInterfaceAndField identity) {
306         return {identity, contents_};
307       }
308     };
309   };
310 
311   // A FrozenArray. May not be null.
312   struct ListContents : EntryBase {
ListContentsjs::frontend::HuffmanPreludeReader::ListContents313     explicit ListContents(const NormalizedInterfaceAndField identity)
314         : EntryBase(identity) {}
315   };
316 
317   // A choice between several interfaces. May not be null.
318   struct Sum : EntryIndexed {
319     // The type of values in the sum.
320     const BinASTSum contents_;
321 
322     // Comparing sum entries alphabetically.
lessThanjs::frontend::HuffmanPreludeReader::Sum323     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
324       MOZ_ASSERT(aIndex <= maxNumberOfSymbols());
325       MOZ_ASSERT(bIndex <= maxNumberOfSymbols());
326       const size_t aKey = getBinASTKindSortKey(interfaceAt(aIndex));
327       const size_t bKey = getBinASTKindSortKey(interfaceAt(bIndex));
328       return aKey < bKey;
329     }
330 
Sumjs::frontend::HuffmanPreludeReader::Sum331     Sum(const NormalizedInterfaceAndField identity, const BinASTSum contents)
332         : EntryIndexed(identity), contents_(contents) {}
333 
maxNumberOfSymbolsjs::frontend::HuffmanPreludeReader::Sum334     size_t maxNumberOfSymbols() const {
335       return SUM_LIMITS[static_cast<size_t>(contents_)];
336     }
337 
interfaceAtjs::frontend::HuffmanPreludeReader::Sum338     BinASTKind interfaceAt(size_t index) const {
339       MOZ_ASSERT(index < maxNumberOfSymbols());
340       return SUM_RESOLUTIONS[static_cast<size_t>(contents_)][index];
341     }
342 
343     // Utility struct, used in macros to call the constructor as
344     // `Sum::Maker(kind)(identity)`.
345     struct Maker {
346       const BinASTSum contents_;
Makerjs::frontend::HuffmanPreludeReader::Sum::Maker347       explicit Maker(BinASTSum contents) : contents_(contents) {}
operator ()js::frontend::HuffmanPreludeReader::Sum::Maker348       Sum operator()(const NormalizedInterfaceAndField identity) {
349         return Sum(identity, contents_);
350       }
351     };
352   };
353 
354   // An optional choice between several interfaces.
355   struct MaybeSum : EntryIndexed {
356     // The type of values in the sum.
357     // We use `BinASTKind::_Null` to represent the null case.
358     const BinASTSum contents_;
359 
lessThanjs::frontend::HuffmanPreludeReader::MaybeSum360     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
361       return aIndex < bIndex;
362     }
363 
MaybeSumjs::frontend::HuffmanPreludeReader::MaybeSum364     MaybeSum(const NormalizedInterfaceAndField identity,
365              const BinASTSum contents)
366         : EntryIndexed(identity), contents_(contents) {}
367 
maxNumberOfSymbolsjs::frontend::HuffmanPreludeReader::MaybeSum368     size_t maxNumberOfSymbols() const {
369       return SUM_LIMITS[static_cast<size_t>(contents_)] + 1;
370     }
371 
interfaceAtjs::frontend::HuffmanPreludeReader::MaybeSum372     BinASTKind interfaceAt(size_t index) const {
373       MOZ_ASSERT(index < maxNumberOfSymbols());
374       if (index == 0) {
375         return BinASTKind::_Null;
376       }
377       return SUM_RESOLUTIONS[static_cast<size_t>(contents_)][index - 1];
378     }
379 
380     // Utility struct, used in macros to call the constructor as
381     // `MaybeSum::Maker(kind)(identity)`.
382     struct Maker {
383       const BinASTSum contents_;
Makerjs::frontend::HuffmanPreludeReader::MaybeSum::Maker384       explicit Maker(BinASTSum contents) : contents_(contents) {}
operator ()js::frontend::HuffmanPreludeReader::MaybeSum::Maker385       MaybeSum operator()(const NormalizedInterfaceAndField identity) {
386         return MaybeSum(identity, contents_);
387       }
388     };
389   };
390 
391   // A choice between several strings. May not be null.
392   struct StringEnum : EntryIndexed {
393     // Comparing string enums alphabetically.
lessThanjs::frontend::HuffmanPreludeReader::StringEnum394     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
395       MOZ_ASSERT(aIndex <= maxNumberOfSymbols());
396       MOZ_ASSERT(bIndex <= maxNumberOfSymbols());
397       const size_t aKey = getBinASTVariantSortKey(variantAt(aIndex));
398       const size_t bKey = getBinASTVariantSortKey(variantAt(bIndex));
399       return aKey < bKey;
400     }
401 
402     // The values in the enum.
403     const BinASTStringEnum contents_;
StringEnumjs::frontend::HuffmanPreludeReader::StringEnum404     StringEnum(const NormalizedInterfaceAndField identity,
405                const BinASTStringEnum contents)
406         : EntryIndexed(identity), contents_(contents) {}
407 
maxNumberOfSymbolsjs::frontend::HuffmanPreludeReader::StringEnum408     size_t maxNumberOfSymbols() const {
409       return STRING_ENUM_LIMITS[static_cast<size_t>(contents_)];
410     }
411 
variantAtjs::frontend::HuffmanPreludeReader::StringEnum412     BinASTVariant variantAt(size_t index) const {
413       MOZ_ASSERT(index < maxNumberOfSymbols());
414       return STRING_ENUM_RESOLUTIONS[static_cast<size_t>(contents_)][index];
415     }
416 
417     // Utility struct, used in macros to call the constructor as
418     // `StringEnum::Maker(kind)(identity)`.
419     struct Maker {
420       const BinASTStringEnum contents_;
Makerjs::frontend::HuffmanPreludeReader::StringEnum::Maker421       explicit Maker(BinASTStringEnum contents) : contents_(contents) {}
operator ()js::frontend::HuffmanPreludeReader::StringEnum::Maker422       StringEnum operator()(const NormalizedInterfaceAndField identity) {
423         return StringEnum(identity, contents_);
424       }
425     };
426   };
427 
428  public:
429   // The entries in the grammar.
430   using Entry = mozilla::Variant<
431       // Primitives
432       Boolean, String, MaybeString, Number, UnsignedLong, Lazy,
433       // Combinators
434       Interface, MaybeInterface, List, Sum, MaybeSum, StringEnum>;
435 
436 #ifdef DEBUG
437   // Utility struct, designed to inspect an `Entry` while debugging.
438   struct PrintEntry {
printjs::frontend::HuffmanPreludeReader::PrintEntry439     static void print(const char* text, const Entry& entry) {
440       fprintf(stderr, "%s ", text);
441       entry.match(PrintEntry());
442       fprintf(stderr, "\n");
443     }
operator ()js::frontend::HuffmanPreludeReader::PrintEntry444     void operator()(const EntryBase& entry) {
445       fprintf(stderr, "%s",
446               describeBinASTInterfaceAndField(entry.identity_.identity_));
447     }
448   };
449 #endif  // DEBUG
450 
451  private:
452   // A stack of entries to process. We always process the latest entry added.
453   Vector<Entry> stack_;
454 
455   /*
456   Implementation of "To push a field" from the specs.
457 
458   As per the spec:
459 
460   1. If the field is in the set of visited contexts, stop
461   2. Add the field to the set of visited fields
462   3. If the field has a FrozenArray type
463     a. Determine if the array type is always empty
464     b. If so, stop
465   4. If the effective type is a monomorphic interface, push all of the
466   interface’s fields
467   5. Otherwise, push the field onto the stack
468   */
469 
470   // Enqueue an entry to the stack.
pushValue(NormalizedInterfaceAndField identity,const List & list)471   MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
472                                         const List& list) {
473     const auto tableId = TableIdentity(list.contents_);
474     if (dictionary_->isUnreachable(tableId)) {
475       // Spec:
476       // 2. Add the field to the set of visited fields
477       dictionary_->setInitializing(tableId);
478 
479       // Read the lengths immediately.
480       MOZ_TRY((readTable<List>(tableId, list)));
481     }
482 
483     // Spec:
484     // 3. If the field has a FrozenArray type
485     //   a. Determine if the array type is always empty
486     //   b. If so, stop
487     const auto& table = dictionary_->getTable(tableId);
488     bool empty = true;
489     for (auto iter : table) {
490       if (iter->toListLength() > 0) {
491         empty = false;
492         break;
493       }
494     }
495     if (empty) {
496       return Ok();
497     }
498 
499     // Spec:
500     // To determine a field’s effective type:
501     // 1. If the field’s type is an array type, the effective type is the
502     // array element type. Stop.
503 
504     // We now recurse with the contents of the list/array, *without checking
505     // whether the field has already been visited*.
506     switch (list.contents_) {
507 #define WRAP_LIST_2(_, CONTENT) CONTENT
508 #define EMIT_CASE(LIST_NAME, _CONTENT_TYPE, _HUMAN_NAME, TYPE) \
509   case BinASTList::LIST_NAME:                                  \
510     return pushValue(list.identity_, TYPE(list.identity_));
511 
512       FOR_EACH_BIN_LIST(EMIT_CASE, WRAP_PRIMITIVE, WRAP_INTERFACE,
513                         WRAP_MAYBE_INTERFACE, WRAP_LIST_2, WRAP_SUM,
514                         WRAP_MAYBE_SUM, WRAP_STRING_ENUM,
515                         WRAP_MAYBE_STRING_ENUM)
516 #undef EMIT_CASE
517 #undef WRAP_LIST_2
518     }
519     return Ok();
520   }
521 
pushValue(NormalizedInterfaceAndField identity,const Interface & interface)522   MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
523                                         const Interface& interface) {
524     const auto tableId = TableIdentity(identity);
525     if (dictionary_->isUnreachable(tableId)) {
526       // Effectively, an `Interface` is a sum with a single entry.
527       auto& table = dictionary_->createTable(tableId);
528 
529       MOZ_TRY(table.initWithSingleValue(
530           cx_, BinASTSymbol::fromKind(BinASTKind(interface.kind_))));
531     }
532 
533     // Spec:
534     // 4. If the effective type is a monomorphic interface, push all of the
535     // interface’s fields
536     return pushFields(interface.kind_);
537   }
538 
539   // Generic implementation for other cases.
540   template <class Entry>
pushValue(NormalizedInterfaceAndField identity,const Entry & entry)541   MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
542                                         const Entry& entry) {
543     // Spec:
544     // 1. If the field is in the set of visited contexts, stop.
545     const auto tableId = TableIdentity(identity);
546     if (!dictionary_->isUnreachable(tableId)) {
547       // Entry already initialized/initializing.
548       return Ok();
549     }
550 
551     // Spec:
552     // 2. Add the field to the set of visited fields
553     dictionary_->setInitializing(tableId);
554 
555     // Spec:
556     // 5. Otherwise, push the field onto the stack
557     BINJS_TRY(stack_.append(entry));
558     return Ok();
559   }
560 
561   // Enqueue all the fields of an interface to the stack.
pushFields(BinASTKind tag)562   JS::Result<Ok> pushFields(BinASTKind tag) {
563     /*
564     Generate a static implementation of pushing fields.
565 
566     switch (tag) {
567       case BinASTKind::$TagName: {
568         MOZ_TRY(pushValue(NormalizedInterfaceAndField(BinASTIterfaceAndField::$Name),
569     Entry(FIELD_TYPE(NormalizedInterfaceAndField(BinASTInterfaceAndField::$Name)))));
570         // ...
571         break;
572       }
573       // ...
574     }
575     */
576 
577     switch (tag) {
578 #define EMIT_FIELD(TAG_NAME, FIELD_NAME, FIELD_INDEX, FIELD_TYPE, _)        \
579   MOZ_TRY(pushValue(NormalizedInterfaceAndField(                            \
580                         BinASTInterfaceAndField::TAG_NAME##__##FIELD_NAME), \
581                     FIELD_TYPE(NormalizedInterfaceAndField(                 \
582                         BinASTInterfaceAndField::TAG_NAME##__##FIELD_NAME))));
583 #define EMIT_CASE(TAG_ENUM_NAME, _2, TAG_MACRO_NAME)                      \
584   case BinASTKind::TAG_ENUM_NAME: {                                       \
585     FOR_EACH_BIN_FIELD_IN_INTERFACE_##TAG_MACRO_NAME(                     \
586         EMIT_FIELD, WRAP_PRIMITIVE, WRAP_INTERFACE, WRAP_MAYBE_INTERFACE, \
587         WRAP_LIST, WRAP_SUM, WRAP_MAYBE_SUM, WRAP_STRING_ENUM,            \
588         WRAP_MAYBE_STRING_ENUM);                                          \
589     break;                                                                \
590   }
591       FOR_EACH_BIN_KIND(EMIT_CASE)
592 #undef EMIT_CASE
593 #undef EMIT_FIELD
594     }
595 
596     return Ok();
597   }
598 
599   // Read a single symbol.
600   // For an indexed type, the symbol is fetched from the grammar using `index`.
601   // We have a guarantee that `index` is always in [0, numberOfSymbols).
602   template <typename Entry>
603   MOZ_MUST_USE JS::Result<BinASTSymbol> readSymbol(const Entry&, size_t index);
604 
605   // Read the number of symbols in an entry.
606   // For an indexed type, theis number is fetched from the grammar.
607   // We have a guarantee that `index` is always in [0, numberOfSymbols)
608   template <typename Entry>
609   MOZ_MUST_USE JS::Result<uint32_t> readNumberOfSymbols(const Entry&);
610 
611   // Read a table in the optimized "only one value" format.
612   template <typename Entry>
613   MOZ_MUST_USE JS::Result<Ok> readSingleValueTable(GenericHuffmanTable&,
614                                                    const Entry&);
615 
616   // Read a table in the non-optimized format.
617   //
618   // Entries in the table are represented in an order that is specific to each
619   // type. See the documentation of each `ReadPoppedEntryMatcher::operator()`
620   // for a discussion on each order.
621   template <typename Entry>
readMultipleValuesTable(GenericHuffmanTable & table,Entry entry)622   MOZ_MUST_USE JS::Result<Ok> readMultipleValuesTable(
623       GenericHuffmanTable& table, Entry entry) {
624     // Get the number of symbols.
625     // If `Entry` is an indexed type, this is fetched from the grammar.
626     BINJS_MOZ_TRY_DECL(numberOfSymbols, readNumberOfSymbols<Entry>(entry));
627 
628     MOZ_ASSERT(numberOfSymbols <= MAX_NUMBER_OF_SYMBOLS);
629 
630     if (numberOfSymbols == 1) {
631       // Special case: only one symbol.
632       BINJS_MOZ_TRY_DECL(bitLength, reader_.readByte<Compression::No>());
633       if (MOZ_UNLIKELY(bitLength != 0)) {
634         // Since there is a single symbol, it doesn't make sense to have a non-0
635         // bit length.
636         return raiseInvalidTableData(entry.identity_);
637       }
638 
639       // Read the symbol.
640       // If `Entry` is an indexed type, it is fetched directly from the grammar.
641       BINJS_MOZ_TRY_DECL(
642           symbol, readSymbol<Entry>(entry, /* First and only value */ 0));
643 
644       MOZ_TRY(table.initWithSingleValue(cx_, symbol));
645       return Ok();
646     }
647 
648     MOZ_TRY(readMultipleValuesTableAndAssignCode<Entry>(table, entry,
649                                                         numberOfSymbols));
650 
651     // Note that the table may be empty, in the case of a list that never has
652     // any elements.
653     return Ok();
654   }
655 
656   template <typename Entry>
657   MOZ_MUST_USE JS::Result<typename Entry::Explicit>
readMultipleValuesTableAndAssignCode(GenericHuffmanTable & table,Entry entry,uint32_t numberOfSymbols)658   readMultipleValuesTableAndAssignCode(GenericHuffmanTable& table, Entry entry,
659                                        uint32_t numberOfSymbols) {
660     // Explicit entry:
661     // - All symbols must be read from disk.
662     // - Lengths read from disk are bit lengths.
663     // - Bit lengths are ranked by increasing order.
664     // - Bit lengths MUST NOT be 0.
665 
666     // Data is presented in an order that doesn't match our memory
667     // representation, so we need to copy `numberOfSymbols` entries.
668     // We use an auxiliary vector to avoid allocating each time.
669     MOZ_ASSERT(
670         auxStorageLength_.empty());  // We must have cleaned it up properly.
671     BINJS_TRY(auxStorageLength_.reserve(numberOfSymbols + 1));
672 
673     uint8_t largestBitLength = 0;
674 
675     // First read and store the bit lengths for all symbols.
676     for (size_t i = 0; i < numberOfSymbols; ++i) {
677       BINJS_MOZ_TRY_DECL(bitLength, reader_.readByte<Compression::No>());
678       if (MOZ_UNLIKELY(bitLength == 0)) {
679         return raiseInvalidTableData(entry.identity_);
680       }
681       if (bitLength > largestBitLength) {
682         largestBitLength = bitLength;
683       }
684       // Already reserved sufficient space above.
685       auxStorageLength_.infallibleAppend(BitLengthAndIndex(bitLength, i));
686     }
687     // Append a terminator.
688     // Already reserved sufficient space above.
689     auxStorageLength_.infallibleAppend(
690         BitLengthAndIndex(MAX_CODE_BIT_LENGTH, numberOfSymbols));
691 
692     // Now read the symbols and assign bits.
693     uint32_t code = 0;
694     MOZ_TRY(
695         table.initStart(cx_, &tempStorage_, numberOfSymbols, largestBitLength));
696 
697     for (size_t i = 0; i < numberOfSymbols; ++i) {
698       const auto bitLength = auxStorageLength_[i].bitLength_;
699       const auto nextBitLength =
700           auxStorageLength_[i + 1]
701               .bitLength_;  // Guaranteed to exist, as we have a terminator.
702 
703       if (MOZ_UNLIKELY(bitLength > nextBitLength)) {
704         // By format invariant, bit lengths are always non-0
705         // and always ranked by increasing order.
706         return raiseInvalidTableData(entry.identity_);
707       }
708 
709       // Read and add symbol.
710       BINJS_MOZ_TRY_DECL(
711           symbol, readSymbol<Entry>(entry, i));  // Symbol is read from disk.
712       MOZ_TRY(table.addSymbol(i, code, bitLength, symbol));
713 
714       // Prepare next code.
715       code = (code + 1) << (nextBitLength - bitLength);
716     }
717 
718     MOZ_TRY(table.initComplete(cx_, &tempStorage_));
719     auxStorageLength_.clear();
720     return Ok();
721   }
722 
723   template <typename Entry>
724   MOZ_MUST_USE JS::Result<typename Entry::Indexed>
readMultipleValuesTableAndAssignCode(GenericHuffmanTable & table,Entry entry,uint32_t numberOfSymbols)725   readMultipleValuesTableAndAssignCode(GenericHuffmanTable& table, Entry entry,
726                                        uint32_t numberOfSymbols) {
727     // In this case, `numberOfSymbols` is actually an upper bound,
728     // rather than an actual number of symbols.
729 
730     // Data is presented in an order that doesn't match our memory
731     // representation, so we need to copy `numberOfSymbols` entries.
732     // We use an auxiliary vector to avoid allocating each time.
733     MOZ_ASSERT(
734         auxStorageLength_.empty());  // We must have cleaned it up properly.
735     BINJS_TRY(auxStorageLength_.reserve(numberOfSymbols + 1));
736 
737     // Implicit entry:
738     // - Symbols are known statically.
739     // - Lengths MAY be 0.
740     // - Lengths are not sorted on disk.
741 
742     uint8_t largestBitLength = 0;
743 
744     // First read the length for all symbols, only store non-0 lengths.
745     for (size_t i = 0; i < numberOfSymbols; ++i) {
746       BINJS_MOZ_TRY_DECL(bitLength, reader_.readByte<Compression::No>());
747       if (MOZ_UNLIKELY(bitLength > MAX_CODE_BIT_LENGTH)) {
748         MOZ_CRASH("FIXME: Implement error");
749       }
750       if (bitLength > 0) {
751         // Already reserved sufficient space above.
752         auxStorageLength_.infallibleAppend(BitLengthAndIndex(bitLength, i));
753         if (bitLength > largestBitLength) {
754           largestBitLength = bitLength;
755         }
756       }
757     }
758 
759     if (auxStorageLength_.length() == 1) {
760       // We have only one symbol, so let's use an optimized table.
761       BINJS_MOZ_TRY_DECL(symbol,
762                          readSymbol<Entry>(entry, auxStorageLength_[0].index_));
763       MOZ_TRY(table.initWithSingleValue(cx_, symbol));
764       auxStorageLength_.clear();
765       return Ok();
766     }
767 
768     // Sort by length then webidl order (which is also the index).
769     std::sort(auxStorageLength_.begin(), auxStorageLength_.end(),
770               [&entry](const BitLengthAndIndex& a,
771                        const BitLengthAndIndex& b) -> bool {
772                 MOZ_ASSERT(a.index_ != b.index_);
773                 // Compare first by bit length.
774                 if (a.bitLength_ < b.bitLength_) {
775                   return true;
776                 }
777                 if (a.bitLength_ > b.bitLength_) {
778                   return false;
779                 }
780                 // In case of equal bit length, compare by symbol value.
781                 return entry.lessThan(a.index_, b.index_);
782               });
783 
784     // Append a terminator.
785     // Already reserved sufficient space above.
786     auxStorageLength_.infallibleEmplaceBack(MAX_CODE_BIT_LENGTH,
787                                             numberOfSymbols);
788 
789     // Now read the symbols and assign bits.
790     uint32_t code = 0;
791     MOZ_TRY(table.initStart(cx_, &tempStorage_, auxStorageLength_.length() - 1,
792                             largestBitLength));
793 
794     for (size_t i = 0; i < auxStorageLength_.length() - 1; ++i) {
795       const auto bitLength = auxStorageLength_[i].bitLength_;
796       const auto nextBitLength =
797           auxStorageLength_[i + 1].bitLength_;  // Guaranteed to exist, as we
798                                                 // stop before the terminator.
799       MOZ_ASSERT(bitLength > 0);
800       MOZ_ASSERT(bitLength <= nextBitLength);
801 
802       // Read symbol from memory and add it.
803       BINJS_MOZ_TRY_DECL(symbol,
804                          readSymbol<Entry>(entry, auxStorageLength_[i].index_));
805 
806       MOZ_TRY(table.addSymbol(i, code, bitLength, symbol));
807 
808       // Prepare next code.
809       code = (code + 1) << (nextBitLength - bitLength);
810     }
811 
812     MOZ_TRY(table.initComplete(cx_, &tempStorage_));
813 
814     auxStorageLength_.clear();
815     return Ok();
816   }
817 
818   // Single-argument version: lookup the table using `dictionary_->table`
819   // then proceed as three-arguments version.
820   template <typename Entry>
readTable(Entry entry)821   MOZ_MUST_USE JS::Result<Ok> readTable(Entry entry) {
822     const auto tableId = TableIdentity(entry.identity_);
823     if (MOZ_UNLIKELY(!dictionary_->isInitializing(tableId))) {
824       // We're attempting to re-read a table that has already been read.
825       // FIXME: Shouldn't this be a MOZ_CRASH?
826       return raiseDuplicateTableError(entry.identity_);
827     }
828 
829     return readTable<Entry>(tableId, entry);
830   }
831 
832   // Two-arguments version: pass table identity explicitly. Generally called
833   // from single-argument version, but may be called manually, e.g. for list
834   // lengths.
835   template <typename Entry>
readTable(TableIdentity tableId,Entry entry)836   MOZ_MUST_USE JS::Result<Ok> readTable(TableIdentity tableId, Entry entry) {
837     uint8_t headerByte;
838     MOZ_TRY_VAR(headerByte, reader_.readByte<Compression::No>());
839     switch (headerByte) {
840       case TableHeader::SingleValue: {
841         auto& table = dictionary_->createTable(tableId);
842 
843         // The table contains a single value.
844         MOZ_TRY((readSingleValueTable<Entry>(table, entry)));
845         return Ok();
846       }
847       case TableHeader::MultipleValues: {
848         auto& table = dictionary_->createTable(tableId);
849 
850         // Table contains multiple values.
851         MOZ_TRY((readMultipleValuesTable<Entry>(table, entry)));
852         return Ok();
853       }
854       case TableHeader::Unreachable:
855         // Table is unreachable, nothing to do.
856         return Ok();
857       default:
858         return raiseInvalidTableData(entry.identity_);
859     }
860   }
861 
862  private:
863   // An auxiliary storage of bit lengths and indices, used when reading
864   // a table with multiple entries. Used to avoid (re)allocating a
865   // vector of lengths each time we read a table.
866   struct BitLengthAndIndex {
BitLengthAndIndexjs::frontend::HuffmanPreludeReader::BitLengthAndIndex867     BitLengthAndIndex(uint8_t bitLength, size_t index)
868         : bitLength_(bitLength), index_(index) {}
869 
870     // A bitLength, as read from disk.
871     uint8_t bitLength_;
872 
873     // The index of the entry in the table.
874     size_t index_;
875   };
876   Vector<BitLengthAndIndex> auxStorageLength_;
877 
878   // Implementation of pattern-matching cases for `entry.match`.
879   struct ReadPoppedEntryMatcher {
880     // The owning HuffmanPreludeReader.
881     HuffmanPreludeReader& owner;
882 
ReadPoppedEntryMatcherjs::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher883     explicit ReadPoppedEntryMatcher(HuffmanPreludeReader& owner)
884         : owner(owner) {}
885 
886     // Lazy.
887     // Values: no value/
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher888     MOZ_MUST_USE JS::Result<Ok> operator()(const Lazy& entry) {
889       // Nothing to do.
890       return Ok();
891     }
892 
893     // Boolean.
894     // Values: [false, true], in this order.
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher895     MOZ_MUST_USE JS::Result<Ok> operator()(const Boolean& entry) {
896       return owner.readTable<Boolean>(entry);
897     }
898 
899     // Interface.
900     // Values: no value.
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher901     MOZ_MUST_USE JS::Result<Ok> operator()(const Interface& entry) {
902       // No table here, just push fields.
903       return owner.pushFields(entry.kind_);
904     }
905 
906     // Optional Interface.
907     // Values: [null, non-null].
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher908     MOZ_MUST_USE JS::Result<Ok> operator()(const MaybeInterface& entry) {
909       // First, we need a table to determine whether the value is `null`.
910       MOZ_TRY((owner.readTable<MaybeInterface>(entry)));
911 
912       // Then, if the table contains `true`, we need the fields of the
913       // interface.
914       // FIXME: readTable could return a reference to the table, eliminating an
915       // array lookup.
916       const auto tableId = TableIdentity(entry.identity_);
917       if (owner.dictionary_->isUnreachable(tableId)) {
918         return Ok();
919       }
920 
921       const auto& table = owner.dictionary_->getTable(tableId);
922       if (!table.isMaybeInterfaceAlwaysNull()) {
923         MOZ_TRY(owner.pushFields(entry.kind_));
924       }
925       return Ok();
926     }
927 
928     // Sum of interfaces, non-nullable.
929     // Values: interfaces by the order in `FOR_EACH_BIN_INTERFACE_IN_SUM_*`.
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher930     MOZ_MUST_USE JS::Result<Ok> operator()(const Sum& entry) {
931       // First, we need a table to determine which values are present.
932       MOZ_TRY((owner.readTable<Sum>(entry)));
933 
934       // Now, walk the table to enqueue each value if necessary.
935       // FIXME: readTable could return a reference to the table, eliminating an
936       // array lookup.
937 
938       const auto tableId = TableIdentity(entry.identity_);
939       if (owner.dictionary_->isInitializing(tableId)) {
940         return Ok();
941       }
942 
943       auto index = entry.identity_;
944       const auto& table = owner.dictionary_->getTable(tableId);
945       for (auto iter : table) {
946         MOZ_TRY(owner.pushValue(index, Interface(index, iter->toKind())));
947       }
948       return Ok();
949     }
950 
951     // Sum of interfaces, nullable.
952     // Values: `null`, followed by interfaces by the order in
953     // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`.
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher954     MOZ_MUST_USE JS::Result<Ok> operator()(const MaybeSum& entry) {
955       // First, we need a table to determine which values are present.
956       MOZ_TRY((owner.readTable<MaybeSum>(entry)));
957 
958       // Now, walk the table to enqueue each value if necessary.
959       // FIXME: readTable could return a reference to the table, eliminating an
960       // array lookup.
961       const auto tableId = TableIdentity(entry.identity_);
962       if (owner.dictionary_->isUnreachable(tableId)) {
963         return Ok();
964       }
965 
966       auto index = entry.identity_;
967       const auto& table = owner.dictionary_->getTable(tableId);
968       for (auto iter : table) {
969         MOZ_TRY(owner.pushValue(index, Interface(index, iter->toKind())));
970       }
971       return Ok();
972     }
973 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher974     MOZ_MUST_USE JS::Result<Ok> operator()(const Number& entry) {
975       return owner.readTable<Number>(entry);
976     }
977 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher978     MOZ_MUST_USE JS::Result<Ok> operator()(const String& entry) {
979       return owner.readTable<String>(entry);
980     }
981 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher982     MOZ_MUST_USE JS::Result<Ok> operator()(const MaybeString& entry) {
983       return owner.readTable<MaybeString>(entry);
984     }
985 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher986     MOZ_MUST_USE JS::Result<Ok> operator()(const StringEnum& entry) {
987       return owner.readTable<StringEnum>(entry);
988     }
989 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher990     MOZ_MUST_USE JS::Result<Ok> operator()(const UnsignedLong& entry) {
991       return owner.readTable<UnsignedLong>(entry);
992     }
993 
operator ()js::frontend::HuffmanPreludeReader::ReadPoppedEntryMatcher994     MOZ_MUST_USE JS::Result<Ok> operator()(const List&) {
995       MOZ_CRASH("Unreachable");
996       return Ok();
997     }
998   };
999 
1000   template <typename T>
1001   using ErrorResult = BinASTTokenReaderBase::ErrorResult<T>;
1002 
raiseDuplicateTableError(const NormalizedInterfaceAndField identity)1003   MOZ_MUST_USE ErrorResult<JS::Error&> raiseDuplicateTableError(
1004       const NormalizedInterfaceAndField identity) {
1005     return reader_.raiseError("Duplicate table.");
1006   }
1007 
raiseInvalidTableData(const NormalizedInterfaceAndField identity)1008   MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidTableData(
1009       const NormalizedInterfaceAndField identity) {
1010     return reader_.raiseError("Invalid data while reading table.");
1011   }
1012 };
1013 
1014 using Boolean = HuffmanPreludeReader::Boolean;
1015 using Interface = HuffmanPreludeReader::Interface;
1016 using List = HuffmanPreludeReader::List;
1017 using MaybeInterface = HuffmanPreludeReader::MaybeInterface;
1018 using MaybeString = HuffmanPreludeReader::MaybeString;
1019 using MaybeSum = HuffmanPreludeReader::MaybeSum;
1020 using Number = HuffmanPreludeReader::Number;
1021 using String = HuffmanPreludeReader::String;
1022 using StringEnum = HuffmanPreludeReader::StringEnum;
1023 using Sum = HuffmanPreludeReader::Sum;
1024 using UnsignedLong = HuffmanPreludeReader::UnsignedLong;
1025 
BinASTTokenReaderContext(JSContext * cx,ErrorReporter * er,const uint8_t * start,const size_t length)1026 BinASTTokenReaderContext::BinASTTokenReaderContext(JSContext* cx,
1027                                                    ErrorReporter* er,
1028                                                    const uint8_t* start,
1029                                                    const size_t length)
1030     : BinASTTokenReaderBase(cx, er, start, length),
1031       metadata_(nullptr),
1032       posBeforeTree_(nullptr),
1033       lazyScripts_(cx) {
1034   MOZ_ASSERT(er);
1035 }
1036 
~BinASTTokenReaderContext()1037 BinASTTokenReaderContext::~BinASTTokenReaderContext() {
1038   if (metadata_ && metadataOwned_ == MetadataOwnership::Owned) {
1039     UniqueBinASTSourceMetadataPtr ptr(metadata_);
1040   }
1041 }
1042 
1043 template <>
1044 JS::Result<Ok>
readBuf(uint8_t * bytes,uint32_t & len)1045 BinASTTokenReaderContext::readBuf<Compression::No, EndOfFilePolicy::RaiseError>(
1046     uint8_t* bytes, uint32_t& len) {
1047   return Base::readBuf(bytes, len);
1048 }
1049 
1050 template <>
1051 JS::Result<Ok>
readBuf(uint8_t * bytes,uint32_t & len)1052 BinASTTokenReaderContext::readBuf<Compression::No, EndOfFilePolicy::BestEffort>(
1053     uint8_t* bytes, uint32_t& len) {
1054   len = std::min((uint32_t)(stop_ - current_), len);
1055   return Base::readBuf(bytes, len);
1056 }
1057 
1058 template <>
1059 JS::Result<Ok>
handleEndOfStream()1060 BinASTTokenReaderContext::handleEndOfStream<EndOfFilePolicy::RaiseError>() {
1061   return raiseError("Unexpected end of stream");
1062 }
1063 
1064 template <>
1065 JS::Result<Ok>
handleEndOfStream()1066 BinASTTokenReaderContext::handleEndOfStream<EndOfFilePolicy::BestEffort>() {
1067   return Ok();
1068 }
1069 
1070 template <>
readByte()1071 JS::Result<uint8_t> BinASTTokenReaderContext::readByte<Compression::No>() {
1072   return Base::readByte();
1073 }
1074 
takeMetadata()1075 BinASTSourceMetadata* BinASTTokenReaderContext::takeMetadata() {
1076   MOZ_ASSERT(metadataOwned_ == MetadataOwnership::Owned);
1077   metadataOwned_ = MetadataOwnership::Unowned;
1078   return metadata_;
1079 }
1080 
initFromScriptSource(ScriptSource * scriptSource)1081 JS::Result<Ok> BinASTTokenReaderContext::initFromScriptSource(
1082     ScriptSource* scriptSource) {
1083   metadata_ = scriptSource->binASTSourceMetadata()->asContext();
1084   metadataOwned_ = MetadataOwnership::Unowned;
1085 
1086   return Ok();
1087 }
1088 
readHeader()1089 JS::Result<Ok> BinASTTokenReaderContext::readHeader() {
1090   // Check that we don't call this function twice.
1091   MOZ_ASSERT(!posBeforeTree_);
1092 
1093 #if BINAST_CX_MAGIC_HEADER
1094   // Read global headers.
1095   MOZ_TRY(readConst(CX_MAGIC_HEADER));
1096   BINJS_MOZ_TRY_DECL(version, readVarU32<Compression::No>());
1097 
1098   if (MOZ_UNLIKELY(version != MAGIC_FORMAT_VERSION)) {
1099     return raiseError("Format version not implemented");
1100   }
1101 #endif  // BINAST_CX_MAGIC_HEADER
1102 
1103   MOZ_TRY(readStringPrelude());
1104   MOZ_TRY(readHuffmanPrelude());
1105 
1106   return Ok();
1107 }
1108 
readTreeFooter()1109 JS::Result<Ok> BinASTTokenReaderContext::readTreeFooter() {
1110   flushBitStream();
1111 
1112   BINJS_MOZ_TRY_DECL(numLazy, readVarU32<Compression::No>());
1113   if (numLazy != lazyScripts_.length()) {
1114     return raiseError("The number of lazy functions does not match");
1115   }
1116 
1117   for (size_t i = 0; i < numLazy; i++) {
1118     BINJS_MOZ_TRY_DECL(len, readVarU32<Compression::No>());
1119     // Use sourceEnd as temporary space to store length of each script.
1120     lazyScripts_[i]->setEnd(len);
1121   }
1122 
1123   for (size_t i = 0; i < numLazy; i++) {
1124     uint32_t begin = offset();
1125     uint32_t len = lazyScripts_[i]->extent.sourceEnd;
1126 
1127     current_ += len;
1128 
1129     lazyScripts_[i]->setStart(begin, 0, begin);
1130     lazyScripts_[i]->setEnd(begin + len);
1131   }
1132 
1133   return Ok();
1134 }
1135 
flushBitStream()1136 void BinASTTokenReaderContext::flushBitStream() {
1137   current_ -= bitBuffer.numUnusedBytes();
1138   bitBuffer.flush();
1139 }
1140 
readStringPrelude()1141 JS::Result<Ok> BinASTTokenReaderContext::readStringPrelude() {
1142   BINJS_MOZ_TRY_DECL(stringsNumberOfEntries, readVarU32<Compression::No>());
1143 
1144   const uint32_t MAX_NUMBER_OF_STRINGS = 32768;
1145 
1146   if (MOZ_UNLIKELY(stringsNumberOfEntries > MAX_NUMBER_OF_STRINGS)) {
1147     return raiseError("Too many entries in strings dictionary");
1148   }
1149 
1150   BinASTSourceMetadataContext* metadata =
1151       BinASTSourceMetadataContext::create(stringsNumberOfEntries);
1152   if (MOZ_UNLIKELY(!metadata)) {
1153     return raiseOOM();
1154   }
1155 
1156   // Free it if we don't make it out of here alive. Since we don't want to
1157   // calloc(), we need to avoid marking atoms that might not be there.
1158   auto se = mozilla::MakeScopeExit([metadata]() { js_free(metadata); });
1159 
1160   // Below, we read at most DECODED_BUFFER_SIZE bytes at once and look for
1161   // strings there. We can overrun to the model part, and in that case put
1162   // unused part back to `decodedBuffer_`.
1163 
1164   RootedAtom atom(cx_);
1165 
1166   for (uint32_t stringIndex = 0; stringIndex < stringsNumberOfEntries;
1167        stringIndex++) {
1168     if (MOZ_UNLIKELY(current_ >= stop_)) {
1169       return raiseError("End of file reached while reading strings table");
1170     }
1171 
1172     const uint8_t* end =
1173         static_cast<const uint8_t*>(memchr(current_, '\0', stop_ - current_));
1174     if (MOZ_UNLIKELY(!end)) {
1175       return raiseError("Invalid string, missing NUL");
1176     }
1177 
1178     // FIXME: handle 0x01-escaped string here.
1179 
1180     const char* start = reinterpret_cast<const char*>(current_);
1181     BINJS_TRY_VAR(atom, AtomizeWTF8Chars(cx_, start, end - current_));
1182 
1183     metadata->getAtom(stringIndex) = atom;
1184 
1185     // +1 for skipping 0x00.
1186     current_ = end + 1;
1187   }
1188 
1189   // Found all strings. The remaining data is kept in buffer and will be
1190   // used for later read.
1191 
1192   MOZ_ASSERT(!metadata_);
1193   se.release();
1194   metadata_ = metadata;
1195   metadataOwned_ = MetadataOwnership::Owned;
1196 
1197   return Ok();
1198 }
1199 
readHuffmanPrelude()1200 JS::Result<Ok> BinASTTokenReaderContext::readHuffmanPrelude() {
1201   HuffmanPreludeReader reader(cx_, *this);
1202   BINJS_MOZ_TRY_DECL(dictionary, reader.run(HUFFMAN_STACK_INITIAL_CAPACITY));
1203   metadata_->setDictionary(dictionary);
1204   return Ok();
1205 }
1206 
BitBuffer()1207 BinASTTokenReaderContext::BitBuffer::BitBuffer() : bits_(0), bitLength_(0) {
1208   static_assert(sizeof(bits_) * 8 == BIT_BUFFER_SIZE,
1209                 "Expecting bitBuffer to match BIT_BUFFER_SIZE");
1210 }
1211 
1212 template <Compression C>
getHuffmanLookup(BinASTTokenReaderContext & owner)1213 JS::Result<HuffmanLookup> BinASTTokenReaderContext::BitBuffer::getHuffmanLookup(
1214     BinASTTokenReaderContext& owner) {
1215   // First, read data if necessary.
1216 
1217   // The algorithm is not intuitive, so consider an example, where the byte
1218   // stream starts with `0b_HGFE_DCBA`, `0b_PONM_LKJI`, `0b_XWVU_TRSQ`,
1219   // `0b_6543_21ZY`
1220   //
1221   // In each byte, bits are stored in the reverse order, so what we want
1222   // is `0b_ABCD_EFGH`, `0b_IJML_MNOP`, `0b_QRST_UVWX`, `0b_YZ12_3456`.
1223   // For the example, let's assume that we have already read
1224   // `0b_ABCD_EFGH`, `0b_IJKL_MNOP` into `bits`, so before the call to
1225   // `getHuffmanLookup`, `bits` initially contains
1226   // `0b_XXXX_XXXX__XXXX_XXXX__ABCD_EFGH__IJKL_MNOP`, where `X` are bits that
1227   // are beyond `bitLength_`
1228 
1229   if (bitLength_ <= MAX_PREFIX_BIT_LENGTH) {
1230     // Keys can be up to MAX_PREFIX_BIT_LENGTH bits long. If we have fewer bits
1231     // available, it's time to reload. We'll try and get as close to 64 bits as
1232     // possible.
1233 
1234     // Make an array to store the new data. We can read up to 8 bytes
1235     uint8_t bytes[8] = {};
1236     // Determine the number of bytes in `bits` rounded up to the nearest byte.
1237     uint32_t bytesInBits =
1238         (bitLength_ + BIT_BUFFER_READ_UNIT - 1) / BIT_BUFFER_READ_UNIT;
1239     // Determine the number of bytes we need to read to get `bitLength` as close
1240     // as possible to 64
1241     uint32_t readLen = sizeof(bytes) - bytesInBits;
1242     // Try to read `readLen` bytes. If we read less than 8 bytes, the last `8 -
1243     // readLen` indices contain zeros. Since the most significant bytes of the
1244     // data are stored in the lowest indices, `bytes` is big endian.
1245     MOZ_TRY((owner.readBuf<Compression::No, EndOfFilePolicy::BestEffort>(
1246         bytes, readLen)));
1247     // Combine `bytes` array into `newBits`
1248     uint64_t newBits = (static_cast<uint64_t>(bytes[0]) << 56) |
1249                        (static_cast<uint64_t>(bytes[1]) << 48) |
1250                        (static_cast<uint64_t>(bytes[2]) << 40) |
1251                        (static_cast<uint64_t>(bytes[3]) << 32) |
1252                        (static_cast<uint64_t>(bytes[4]) << 24) |
1253                        (static_cast<uint64_t>(bytes[5]) << 16) |
1254                        (static_cast<uint64_t>(bytes[6]) << 8) |
1255                        static_cast<uint64_t>(bytes[7]);
1256     static_assert(sizeof(bytes) == sizeof(newBits),
1257                   "Expecting bytes array to match size of newBits");
1258     // After reading `readLen` bytes in our example, `bytes` will contain
1259     // `{0b_XWSU_TSRQ, 0b_6543_21ZY, 0, 0, 0, 0, 0, 0}`
1260     // and `newBits` contains zeros in the lower 32 bits and
1261     // `0b_XWSU_TSRQ__6543_21ZY__0000_0000__0000_0000` in the upper 32 bits
1262 
1263     // Remove any zeros if we read less than 8 bytes
1264     newBits >>= (BIT_BUFFER_READ_UNIT * (sizeof(bytes) - readLen));
1265     // Now the upper 32 bits of `newBits` are all zero and the lower 32 bits
1266     // contain `0b_0000_0000__0000_0000__XWSU_TSRQ__6543_21ZY`
1267 
1268     // Let's reverse the bits in each of the 8 bytes in `newBits` in place
1269     // First swap alternating bits
1270     newBits = ((newBits >> 1) & 0x5555555555555555) |
1271               ((newBits & 0x5555555555555555) << 1);
1272     // Then swap alternating groups of 2 bits
1273     newBits = ((newBits >> 2) & 0x3333333333333333) |
1274               ((newBits & 0x3333333333333333) << 2);
1275     // Finally swap alternating nibbles
1276     newBits = ((newBits >> 4) & 0x0F0F0F0F0F0F0F0F) |
1277               ((newBits & 0x0F0F0F0F0F0F0F0F) << 4);
1278     // Now the lower 32 bits of `newBits` contain
1279     // `0b_0000_0000__0000_0000__QRST_UVWX__YZ12_3456`
1280 
1281     bitLength_ += (BIT_BUFFER_READ_UNIT * readLen);
1282     // Make room for the new data by shifting `bits` to get
1283     // `0b_ABCD_EFGH__IJKL_MNOP__0000_0000__0000_0000`
1284     // check that we're not shifting by 64 since it's UB for a uint64_t
1285     if (readLen != 8) {
1286       bits_ <<= (BIT_BUFFER_READ_UNIT * readLen);
1287       // Finally, combine `newBits` with `bits` to get
1288       // `0b_ABCD_EFGH__IJKL_MNOP__QRST_UVWX__YZ12_3456`
1289       bits_ += newBits;
1290     }
1291     // If read 8 bytes just set `bits` to the new data
1292     else {
1293       bits_ = newBits;
1294     }
1295     // Now, we may prepare a `HuffmanLookup` with up to 32 bits.
1296     if (bitLength_ <= MAX_PREFIX_BIT_LENGTH) {
1297       return HuffmanLookup(bits_, bitLength_);
1298     }
1299   }
1300 
1301   // Keep only 32 bits. We perform the operation on 64 bits to avoid any
1302   // arithmetics surprise.
1303   const uint64_t bitPrefix = bits_ >> (bitLength_ - MAX_PREFIX_BIT_LENGTH);
1304   MOZ_ASSERT(bitPrefix <= uint32_t(-1));
1305   return HuffmanLookup(bitPrefix, MAX_PREFIX_BIT_LENGTH);
1306 }
1307 
1308 template <>
advanceBitBuffer(const uint8_t bitLength)1309 void BinASTTokenReaderContext::BitBuffer::advanceBitBuffer<Compression::No>(
1310     const uint8_t bitLength) {
1311   // It should be impossible to call `advanceBitBuffer`
1312   // with more bits than what we just handed out.
1313   MOZ_ASSERT(bitLength <= bitLength_);
1314 
1315   bitLength_ -= bitLength;
1316 
1317   // Now zero out the bits that are beyond `bitLength_`.
1318   const uint64_t mask = bitLength_ == 0
1319                             ? 0  // >> 64 is UB for a uint64_t
1320                             : uint64_t(-1) >> (BIT_BUFFER_SIZE - bitLength_);
1321   bits_ &= mask;
1322   MOZ_ASSERT_IF(bitLength_ != BIT_BUFFER_SIZE, bits_ >> bitLength_ == 0);
1323 }
1324 
traceMetadata(JSTracer * trc)1325 void BinASTTokenReaderContext::traceMetadata(JSTracer* trc) {
1326   if (metadata_) {
1327     metadata_->trace(trc);
1328   }
1329 }
1330 
1331 MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
raiseInvalidValue()1332 BinASTTokenReaderContext::raiseInvalidValue() {
1333   errorReporter_->errorNoOffset(JSMSG_BINAST, "Invalid value");
1334   return cx_->alreadyReportedError();
1335 }
1336 
1337 MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
raiseNotInPrelude()1338 BinASTTokenReaderContext::raiseNotInPrelude() {
1339   errorReporter_->errorNoOffset(JSMSG_BINAST, "Value is not in prelude");
1340   return cx_->alreadyReportedError();
1341 }
1342 
1343 struct ExtractBinASTInterfaceAndFieldMatcher {
operator ()js::frontend::ExtractBinASTInterfaceAndFieldMatcher1344   BinASTInterfaceAndField operator()(
1345       const BinASTTokenReaderBase::FieldContext& context) {
1346     return context.position_;
1347   }
operator ()js::frontend::ExtractBinASTInterfaceAndFieldMatcher1348   BinASTInterfaceAndField operator()(
1349       const BinASTTokenReaderBase::ListContext& context) {
1350     return context.position_;
1351   }
operator ()js::frontend::ExtractBinASTInterfaceAndFieldMatcher1352   BinASTInterfaceAndField operator()(
1353       const BinASTTokenReaderBase::RootContext&) {
1354     MOZ_CRASH("The root context has no interface/field");
1355   }
1356 };
1357 
readTagFromTable(const BinASTInterfaceAndField & identity)1358 JS::Result<BinASTKind> BinASTTokenReaderContext::readTagFromTable(
1359     const BinASTInterfaceAndField& identity) {
1360   // Extract the table.
1361   const auto tableId = TableIdentity(NormalizedInterfaceAndField(identity));
1362   const auto& table = metadata_->dictionary()->getTable(tableId);
1363   BINJS_MOZ_TRY_DECL(bits_,
1364                      (bitBuffer.getHuffmanLookup<Compression::No>(*this)));
1365 
1366   // We're entering either a single interface or a sum.
1367   const auto result = table.lookup(bits_);
1368   if (MOZ_UNLIKELY(!result.isFound())) {
1369     return raiseInvalidValue();
1370   }
1371   bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
1372   return result.value().toKind();
1373 }
1374 
readFieldFromTable(const BinASTInterfaceAndField & identity)1375 JS::Result<BinASTSymbol> BinASTTokenReaderContext::readFieldFromTable(
1376     const BinASTInterfaceAndField& identity) {
1377   const auto tableId = TableIdentity(NormalizedInterfaceAndField(identity));
1378   if (!metadata_->dictionary()->isReady(tableId)) {
1379     return raiseNotInPrelude();
1380   }
1381 
1382   const auto& table = metadata_->dictionary()->getTable(tableId);
1383   BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
1384 
1385   const auto result = table.lookup(bits_);
1386   if (MOZ_UNLIKELY(!result.isFound())) {
1387     return raiseInvalidValue();
1388   }
1389 
1390   bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
1391   return result.value();
1392 }
1393 
readBool(const FieldContext & context)1394 JS::Result<bool> BinASTTokenReaderContext::readBool(
1395     const FieldContext& context) {
1396   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1397   return result.toBool();
1398 }
1399 
readDouble(const FieldContext & context)1400 JS::Result<double> BinASTTokenReaderContext::readDouble(
1401     const FieldContext& context) {
1402   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1403   return result.toDouble();
1404 }
1405 
readMaybeAtom(const FieldContext & context)1406 JS::Result<JSAtom*> BinASTTokenReaderContext::readMaybeAtom(
1407     const FieldContext& context) {
1408   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1409   if (result.isNullAtom()) {
1410     return nullptr;
1411   }
1412   return metadata_->getAtom(result.toAtomIndex());
1413 }
1414 
readAtom(const FieldContext & context)1415 JS::Result<JSAtom*> BinASTTokenReaderContext::readAtom(
1416     const FieldContext& context) {
1417   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1418   MOZ_ASSERT(!result.isNullAtom());
1419   return metadata_->getAtom(result.toAtomIndex());
1420 }
1421 
readMaybeIdentifierName(const FieldContext & context)1422 JS::Result<JSAtom*> BinASTTokenReaderContext::readMaybeIdentifierName(
1423     const FieldContext& context) {
1424   return readMaybeAtom(context);
1425 }
1426 
readIdentifierName(const FieldContext & context)1427 JS::Result<JSAtom*> BinASTTokenReaderContext::readIdentifierName(
1428     const FieldContext& context) {
1429   return readAtom(context);
1430 }
1431 
readPropertyKey(const FieldContext & context)1432 JS::Result<JSAtom*> BinASTTokenReaderContext::readPropertyKey(
1433     const FieldContext& context) {
1434   return readAtom(context);
1435 }
1436 
readChars(Chars & out,const FieldContext &)1437 JS::Result<Ok> BinASTTokenReaderContext::readChars(Chars& out,
1438                                                    const FieldContext&) {
1439   return raiseError("readChars is not implemented in BinASTTokenReaderContext");
1440 }
1441 
readVariant(const ListContext & context)1442 JS::Result<BinASTVariant> BinASTTokenReaderContext::readVariant(
1443     const ListContext& context) {
1444   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1445   return result.toVariant();
1446 }
1447 
readVariant(const FieldContext & context)1448 JS::Result<BinASTVariant> BinASTTokenReaderContext::readVariant(
1449     const FieldContext& context) {
1450   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1451   return result.toVariant();
1452 }
1453 
readUnsignedLong(const FieldContext & context)1454 JS::Result<uint32_t> BinASTTokenReaderContext::readUnsignedLong(
1455     const FieldContext& context) {
1456   BINJS_MOZ_TRY_DECL(result, readFieldFromTable(context.position_));
1457   return result.toUnsignedLong();
1458 }
1459 
1460 JS::Result<BinASTTokenReaderBase::SkippableSubTree>
readSkippableSubTree(const FieldContext &)1461 BinASTTokenReaderContext::readSkippableSubTree(const FieldContext&) {
1462   // Postions are set when reading lazy functions after the tree.
1463   return SkippableSubTree(0, 0);
1464 }
1465 
registerLazyScript(FunctionBox * lazy)1466 JS::Result<Ok> BinASTTokenReaderContext::registerLazyScript(FunctionBox* lazy) {
1467   BINJS_TRY(lazyScripts_.append(lazy));
1468   return Ok();
1469 }
1470 
enterSum(BinASTKind & tag,const FieldOrRootContext & context)1471 JS::Result<Ok> BinASTTokenReaderContext::enterSum(
1472     BinASTKind& tag, const FieldOrRootContext& context) {
1473   return context.match(
1474       [this, &tag](const BinASTTokenReaderBase::FieldContext& asFieldContext)
1475           -> JS::Result<Ok> {
1476         // This tuple is the value of the field we're currently reading.
1477         MOZ_TRY_VAR(tag, readTagFromTable(asFieldContext.position_));
1478         return Ok();
1479       },
1480       [&tag](const BinASTTokenReaderBase::RootContext&) -> JS::Result<Ok> {
1481         // For the moment, the format hardcodes `Script` as root.
1482         tag = BinASTKind::Script;
1483         return Ok();
1484       });
1485 }
1486 
enterSum(BinASTKind & tag,const FieldOrListContext & context)1487 JS::Result<Ok> BinASTTokenReaderContext::enterSum(
1488     BinASTKind& tag, const FieldOrListContext& context) {
1489   return context.match(
1490       [this, &tag](const BinASTTokenReaderBase::FieldContext& asFieldContext)
1491           -> JS::Result<Ok> {
1492         // This tuple is the value of the field we're currently reading.
1493         MOZ_TRY_VAR(tag, readTagFromTable(asFieldContext.position_));
1494         return Ok();
1495       },
1496       [this, &tag](const BinASTTokenReaderBase::ListContext& asListContext)
1497           -> JS::Result<Ok> {
1498         // This tuple is an element in a list we're currently reading.
1499         MOZ_TRY_VAR(tag, readTagFromTable(asListContext.position_));
1500         return Ok();
1501       });
1502 }
1503 
enterSum(BinASTKind & tag,const RootContext & context)1504 JS::Result<Ok> BinASTTokenReaderContext::enterSum(BinASTKind& tag,
1505                                                   const RootContext& context) {
1506   // For the moment, the format hardcodes `Script` as root.
1507   tag = BinASTKind::Script;
1508   return Ok();
1509 }
1510 
enterSum(BinASTKind & tag,const ListContext & context)1511 JS::Result<Ok> BinASTTokenReaderContext::enterSum(BinASTKind& tag,
1512                                                   const ListContext& context) {
1513   // This tuple is an element in a list we're currently reading.
1514   MOZ_TRY_VAR(tag, readTagFromTable(context.position_));
1515   return Ok();
1516 }
1517 
enterSum(BinASTKind & tag,const FieldContext & context)1518 JS::Result<Ok> BinASTTokenReaderContext::enterSum(BinASTKind& tag,
1519                                                   const FieldContext& context) {
1520   // This tuple is the value of the field we're currently reading.
1521   MOZ_TRY_VAR(tag, readTagFromTable(context.position_));
1522   return Ok();
1523 }
1524 
enterList(uint32_t & items,const ListContext & context)1525 JS::Result<Ok> BinASTTokenReaderContext::enterList(uint32_t& items,
1526                                                    const ListContext& context) {
1527   const auto tableId = TableIdentity(context.content_);
1528   const auto& table = metadata_->dictionary()->getTable(tableId);
1529   BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
1530   const auto result = table.lookup(bits_);
1531   if (MOZ_UNLIKELY(!result.isFound())) {
1532     return raiseInvalidValue();
1533   }
1534   bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
1535   items = result.value().toListLength();
1536   return Ok();
1537 }
1538 
1539 // Internal uint32_t
1540 // Note that this is different than varnum in multipart.
1541 //
1542 // Encoded as variable length number.
1543 
1544 template <Compression compression>
readVarU32()1545 JS::Result<uint32_t> BinASTTokenReaderContext::readVarU32() {
1546   uint32_t result = 0;
1547   uint32_t shift = 0;
1548   while (true) {
1549     MOZ_ASSERT(shift < 32);
1550     uint32_t byte;
1551     MOZ_TRY_VAR(byte, readByte<compression>());
1552 
1553     const uint32_t newResult = result | (byte & 0x7f) << shift;
1554     if (MOZ_UNLIKELY(newResult < result)) {
1555       return raiseError("Overflow during readVarU32");
1556     }
1557 
1558     result = newResult;
1559     shift += 7;
1560 
1561     if ((byte & 0x80) == 0) {
1562       return result;
1563     }
1564 
1565     if (MOZ_UNLIKELY(shift >= 32)) {
1566       return raiseError("Overflow during readVarU32");
1567     }
1568   }
1569 }
1570 
readUnpackedLong()1571 JS::Result<uint32_t> BinASTTokenReaderContext::readUnpackedLong() {
1572   uint8_t bytes[4];
1573   uint32_t length = 4;
1574   MOZ_TRY(
1575       (readBuf<Compression::No, EndOfFilePolicy::RaiseError>(bytes, length)));
1576   const uint32_t result = uint32_t(bytes[0]) << 24 | uint32_t(bytes[1]) << 16 |
1577                           uint32_t(bytes[2]) << 8 | uint32_t(bytes[3]);
1578   return result;
1579 }
1580 
HuffmanKey(const uint32_t bits,const uint8_t bitLength)1581 HuffmanKey::HuffmanKey(const uint32_t bits, const uint8_t bitLength)
1582     : bits_(bits), bitLength_(bitLength) {
1583   MOZ_ASSERT(bitLength_ <= MAX_PREFIX_BIT_LENGTH);
1584   MOZ_ASSERT_IF(bitLength_ != 32 /* >> 32 is UB */, bits >> bitLength == 0);
1585 }
1586 
1587 template <typename T>
alloc(JSContext * cx,size_t count)1588 T* TemporaryStorageItem<T>::alloc(JSContext* cx, size_t count) {
1589   total_ += count;
1590 
1591   if (MOZ_LIKELY(head_)) {
1592     if (MOZ_LIKELY(head_->used_ + count < head_->size_)) {
1593       // This chunk still has sufficient space to allocate `count` bytes.
1594       T* ret = head_->entries_ + head_->used_;
1595       head_->used_ += count;
1596       return ret;
1597     }
1598   }
1599 
1600   size_t chunkSize = Chunk::DefaultSize;
1601   if (count > chunkSize) {
1602     chunkSize = count;
1603   }
1604 
1605   // Subtract `sizeof(T)` because `Chunk` already has `T entries_[1]`
1606   // and that we want `T entries_[chunkSize]`.
1607   Chunk* chunk =
1608       reinterpret_cast<Chunk*>(cx->template maybe_pod_malloc<uint8_t>(
1609           sizeof(Chunk) - sizeof(T) + chunkSize * sizeof(T)));
1610   if (!chunk) {
1611     ReportOutOfMemory(cx);
1612     return nullptr;
1613   }
1614   chunk->used_ = count;
1615   chunk->size_ = chunkSize;
1616   chunk->next_ = head_;
1617 
1618   head_ = chunk;
1619 
1620   return head_->entries_;
1621 }
1622 
1623 template <typename T>
alloc(JSContext * cx,size_t count)1624 JS::Result<mozilla::Span<T>> TemporaryStorage::alloc(JSContext* cx,
1625                                                      size_t count) {
1626   MOZ_CRASH("unsupported type");
1627   return nullptr;
1628 }
1629 
1630 template <>
alloc(JSContext * cx,size_t count)1631 JS::Result<mozilla::Span<HuffmanEntry>> TemporaryStorage::alloc<HuffmanEntry>(
1632     JSContext* cx, size_t count) {
1633   auto* items = huffmanEntries_.alloc(cx, count);
1634   if (!items) {
1635     return cx->alreadyReportedError();
1636   }
1637   return mozilla::MakeSpan(items, count);
1638 }
1639 
1640 template <>
1641 JS::Result<mozilla::Span<TemporaryStorage::InternalIndex>>
alloc(JSContext * cx,size_t count)1642 TemporaryStorage::alloc<TemporaryStorage::InternalIndex>(JSContext* cx,
1643                                                          size_t count) {
1644   auto* items = internalIndices_.alloc(cx, count);
1645   if (!items) {
1646     return cx->alreadyReportedError();
1647   }
1648   return mozilla::MakeSpan(items, count);
1649 }
1650 
1651 template <>
1652 JS::Result<mozilla::Span<SingleLookupHuffmanTable>>
alloc(JSContext * cx,size_t count)1653 TemporaryStorage::alloc<SingleLookupHuffmanTable>(JSContext* cx, size_t count) {
1654   auto* items = singleTables_.alloc(cx, count);
1655   if (!items) {
1656     return cx->alreadyReportedError();
1657   }
1658   return mozilla::MakeSpan(items, count);
1659 }
1660 
1661 template <>
1662 JS::Result<mozilla::Span<TwoLookupsHuffmanTable>>
alloc(JSContext * cx,size_t count)1663 TemporaryStorage::alloc<TwoLookupsHuffmanTable>(JSContext* cx, size_t count) {
1664   auto* items = twoTables_.alloc(cx, count);
1665   if (!items) {
1666     return cx->alreadyReportedError();
1667   }
1668   return mozilla::MakeSpan(items, count);
1669 }
1670 
1671 // ---- Implementation of Huffman Tables
1672 
Iterator(typename SingleEntryHuffmanTable::Iterator && iterator)1673 GenericHuffmanTable::Iterator::Iterator(
1674     typename SingleEntryHuffmanTable::Iterator&& iterator)
1675     : implementation_(std::move(iterator)) {}
1676 
Iterator(typename TwoEntriesHuffmanTable::Iterator && iterator)1677 GenericHuffmanTable::Iterator::Iterator(
1678     typename TwoEntriesHuffmanTable::Iterator&& iterator)
1679     : implementation_(std::move(iterator)) {}
1680 
Iterator(typename SingleLookupHuffmanTable::Iterator && iterator)1681 GenericHuffmanTable::Iterator::Iterator(
1682     typename SingleLookupHuffmanTable::Iterator&& iterator)
1683     : implementation_(std::move(iterator)) {}
1684 
Iterator(typename TwoLookupsHuffmanTable::Iterator && iterator)1685 GenericHuffmanTable::Iterator::Iterator(
1686     typename TwoLookupsHuffmanTable::Iterator&& iterator)
1687     : implementation_(std::move(iterator)) {}
1688 
Iterator(typename ThreeLookupsHuffmanTable::Iterator && iterator)1689 GenericHuffmanTable::Iterator::Iterator(
1690     typename ThreeLookupsHuffmanTable::Iterator&& iterator)
1691     : implementation_(std::move(iterator)) {}
1692 
operator ++()1693 void GenericHuffmanTable::Iterator::operator++() {
1694   implementation_.match(
1695       [](typename SingleEntryHuffmanTable::Iterator& iterator) {
1696         iterator.operator++();
1697       },
1698       [](typename TwoEntriesHuffmanTable::Iterator& iterator) {
1699         iterator.operator++();
1700       },
1701       [](typename SingleLookupHuffmanTable::Iterator& iterator) {
1702         iterator.operator++();
1703       },
1704       [](typename TwoLookupsHuffmanTable::Iterator& iterator) {
1705         iterator.operator++();
1706       },
1707       [](typename ThreeLookupsHuffmanTable::Iterator& iterator) {
1708         iterator.operator++();
1709       });
1710 }
1711 
operator ==(const GenericHuffmanTable::Iterator & other) const1712 bool GenericHuffmanTable::Iterator::operator==(
1713     const GenericHuffmanTable::Iterator& other) const {
1714   return implementation_.match(
1715       [other](const typename SingleEntryHuffmanTable::Iterator& iterator) {
1716         return iterator ==
1717                other.implementation_
1718                    .template as<typename SingleEntryHuffmanTable::Iterator>();
1719       },
1720       [other](const typename TwoEntriesHuffmanTable::Iterator& iterator) {
1721         return iterator ==
1722                other.implementation_
1723                    .template as<typename TwoEntriesHuffmanTable::Iterator>();
1724       },
1725       [other](const typename SingleLookupHuffmanTable::Iterator& iterator) {
1726         return iterator ==
1727                other.implementation_
1728                    .template as<typename SingleLookupHuffmanTable::Iterator>();
1729       },
1730       [other](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
1731         return iterator ==
1732                other.implementation_
1733                    .template as<typename TwoLookupsHuffmanTable::Iterator>();
1734       },
1735       [other](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
1736         return iterator ==
1737                other.implementation_
1738                    .template as<typename ThreeLookupsHuffmanTable::Iterator>();
1739       });
1740 }
1741 
operator !=(const GenericHuffmanTable::Iterator & other) const1742 bool GenericHuffmanTable::Iterator::operator!=(
1743     const GenericHuffmanTable::Iterator& other) const {
1744   return implementation_.match(
1745       [other](const typename SingleEntryHuffmanTable::Iterator& iterator) {
1746         return iterator !=
1747                other.implementation_
1748                    .template as<typename SingleEntryHuffmanTable::Iterator>();
1749       },
1750       [other](const typename TwoEntriesHuffmanTable::Iterator& iterator) {
1751         return iterator !=
1752                other.implementation_
1753                    .template as<typename TwoEntriesHuffmanTable::Iterator>();
1754       },
1755       [other](const typename SingleLookupHuffmanTable::Iterator& iterator) {
1756         return iterator !=
1757                other.implementation_
1758                    .template as<typename SingleLookupHuffmanTable::Iterator>();
1759       },
1760       [other](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
1761         return iterator !=
1762                other.implementation_
1763                    .template as<typename TwoLookupsHuffmanTable::Iterator>();
1764       },
1765       [other](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
1766         return iterator !=
1767                other.implementation_
1768                    .template as<typename ThreeLookupsHuffmanTable::Iterator>();
1769       });
1770 }
1771 
operator *() const1772 const BinASTSymbol* GenericHuffmanTable::Iterator::operator*() const {
1773   return implementation_.match(
1774       [](const typename SingleEntryHuffmanTable::Iterator& iterator) {
1775         return iterator.operator*();
1776       },
1777       [](const typename TwoEntriesHuffmanTable::Iterator& iterator) {
1778         return iterator.operator*();
1779       },
1780       [](const typename SingleLookupHuffmanTable::Iterator& iterator) {
1781         return iterator.operator*();
1782       },
1783       [](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
1784         return iterator.operator*();
1785       },
1786       [](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
1787         return iterator.operator*();
1788       });
1789 }
1790 
operator ->() const1791 const BinASTSymbol* GenericHuffmanTable::Iterator::operator->() const {
1792   return implementation_.match(
1793       [](const typename SingleEntryHuffmanTable::Iterator& iterator) {
1794         return iterator.operator->();
1795       },
1796       [](const typename TwoEntriesHuffmanTable::Iterator& iterator) {
1797         return iterator.operator->();
1798       },
1799       [](const typename SingleLookupHuffmanTable::Iterator& iterator) {
1800         return iterator.operator->();
1801       },
1802       [](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
1803         return iterator.operator->();
1804       },
1805       [](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
1806         return iterator.operator->();
1807       });
1808 }
1809 
GenericHuffmanTable()1810 GenericHuffmanTable::GenericHuffmanTable()
1811     : implementation_(TableImplementationUninitialized{}) {}
1812 
initComplete(JSContext * cx,TemporaryStorage * tempStorage)1813 JS::Result<Ok> GenericHuffmanTable::initComplete(
1814     JSContext* cx, TemporaryStorage* tempStorage) {
1815   return implementation_.match(
1816       [](SingleEntryHuffmanTable& implementation) -> JS::Result<Ok> {
1817         MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
1818       },
1819       [cx,
1820        tempStorage](TwoEntriesHuffmanTable& implementation) -> JS::Result<Ok> {
1821         return implementation.initComplete(cx, tempStorage);
1822       },
1823       [cx, tempStorage](
1824           SingleLookupHuffmanTable& implementation) -> JS::Result<Ok> {
1825         return implementation.initComplete(cx, tempStorage);
1826       },
1827       [cx,
1828        tempStorage](TwoLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
1829         return implementation.initComplete(cx, tempStorage);
1830       },
1831       [cx, tempStorage](
1832           ThreeLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
1833         return implementation.initComplete(cx, tempStorage);
1834       },
1835       [](TableImplementationUninitialized&) -> JS::Result<Ok> {
1836         MOZ_CRASH("GenericHuffmanTable is unitialized!");
1837       });
1838 }
1839 
begin() const1840 typename GenericHuffmanTable::Iterator GenericHuffmanTable::begin() const {
1841   return implementation_.match(
1842       [](const SingleEntryHuffmanTable& implementation)
1843           -> GenericHuffmanTable::Iterator {
1844         return Iterator(implementation.begin());
1845       },
1846       [](const TwoEntriesHuffmanTable& implementation)
1847           -> GenericHuffmanTable::Iterator {
1848         return Iterator(implementation.begin());
1849       },
1850       [](const SingleLookupHuffmanTable& implementation)
1851           -> GenericHuffmanTable::Iterator {
1852         return Iterator(implementation.begin());
1853       },
1854       [](const TwoLookupsHuffmanTable& implementation)
1855           -> GenericHuffmanTable::Iterator {
1856         return Iterator(implementation.begin());
1857       },
1858       [](const ThreeLookupsHuffmanTable& implementation)
1859           -> GenericHuffmanTable::Iterator {
1860         return Iterator(implementation.begin());
1861       },
1862       [](const TableImplementationUninitialized&)
1863           -> GenericHuffmanTable::Iterator {
1864         MOZ_CRASH("GenericHuffmanTable is unitialized!");
1865       });
1866 }
1867 
end() const1868 typename GenericHuffmanTable::Iterator GenericHuffmanTable::end() const {
1869   return implementation_.match(
1870       [](const SingleEntryHuffmanTable& implementation)
1871           -> GenericHuffmanTable::Iterator {
1872         return Iterator(implementation.end());
1873       },
1874       [](const TwoEntriesHuffmanTable& implementation)
1875           -> GenericHuffmanTable::Iterator {
1876         return Iterator(implementation.end());
1877       },
1878       [](const SingleLookupHuffmanTable& implementation)
1879           -> GenericHuffmanTable::Iterator {
1880         return Iterator(implementation.end());
1881       },
1882       [](const TwoLookupsHuffmanTable& implementation)
1883           -> GenericHuffmanTable::Iterator {
1884         return Iterator(implementation.end());
1885       },
1886       [](const ThreeLookupsHuffmanTable& implementation)
1887           -> GenericHuffmanTable::Iterator {
1888         return Iterator(implementation.end());
1889       },
1890       [](const TableImplementationUninitialized&)
1891           -> GenericHuffmanTable::Iterator {
1892         MOZ_CRASH("GenericHuffmanTable is unitialized!");
1893       });
1894 }
1895 
initWithSingleValue(JSContext * cx,const BinASTSymbol & value)1896 JS::Result<Ok> GenericHuffmanTable::initWithSingleValue(
1897     JSContext* cx, const BinASTSymbol& value) {
1898   // Only one value: use HuffmanImplementationSaturated
1899   MOZ_ASSERT(implementation_.template is<
1900              TableImplementationUninitialized>());  // Make sure that we're
1901                                                     // initializing.
1902 
1903   implementation_ = {mozilla::VariantType<SingleEntryHuffmanTable>{}, value};
1904   return Ok();
1905 }
1906 
initStart(JSContext * cx,TemporaryStorage * tempStorage,size_t numberOfSymbols,uint8_t largestBitLength)1907 JS::Result<Ok> GenericHuffmanTable::initStart(JSContext* cx,
1908                                               TemporaryStorage* tempStorage,
1909                                               size_t numberOfSymbols,
1910                                               uint8_t largestBitLength) {
1911   // Make sure that we have a way to represent all legal bit lengths.
1912   static_assert(MAX_CODE_BIT_LENGTH <= ThreeLookupsHuffmanTable::MAX_BIT_LENGTH,
1913                 "ThreeLookupsHuffmanTable cannot hold all bit lengths");
1914 
1915   // Make sure that we're initializing.
1916   MOZ_ASSERT(implementation_.template is<TableImplementationUninitialized>());
1917 
1918   // Make sure we don't accidentally end up with only one symbol.
1919   MOZ_ASSERT(numberOfSymbols != 1,
1920              "Should have used `initWithSingleValue` instead");
1921 
1922   if (numberOfSymbols == 2) {
1923     implementation_ = {mozilla::VariantType<TwoEntriesHuffmanTable>{}};
1924     return implementation_.template as<TwoEntriesHuffmanTable>().initStart(
1925         cx, tempStorage, numberOfSymbols, largestBitLength);
1926   }
1927 
1928   // Find the (hopefully) fastest implementation of HuffmanTable for
1929   // `largestBitLength`.
1930   // ...hopefully, only one lookup.
1931   if (largestBitLength <= SingleLookupHuffmanTable::MAX_BIT_LENGTH) {
1932     implementation_ = {mozilla::VariantType<SingleLookupHuffmanTable>{},
1933                        SingleLookupHuffmanTable::Use::ToplevelTable};
1934     return implementation_.template as<SingleLookupHuffmanTable>().initStart(
1935         cx, tempStorage, numberOfSymbols, largestBitLength);
1936   }
1937 
1938   // ...if a single-lookup table would be too large, let's see if
1939   // we can fit in a two-lookup table.
1940   if (largestBitLength <= TwoLookupsHuffmanTable::MAX_BIT_LENGTH) {
1941     implementation_ = {mozilla::VariantType<TwoLookupsHuffmanTable>{}};
1942     return implementation_.template as<TwoLookupsHuffmanTable>().initStart(
1943         cx, tempStorage, numberOfSymbols, largestBitLength);
1944   }
1945 
1946   // ...otherwise, we'll need three lookups.
1947   implementation_ = {mozilla::VariantType<ThreeLookupsHuffmanTable>{}};
1948   return implementation_.template as<ThreeLookupsHuffmanTable>().initStart(
1949       cx, tempStorage, numberOfSymbols, largestBitLength);
1950 }
1951 
addSymbol(size_t index,uint32_t bits,uint8_t bitLength,const BinASTSymbol & value)1952 JS::Result<Ok> GenericHuffmanTable::addSymbol(size_t index, uint32_t bits,
1953                                               uint8_t bitLength,
1954                                               const BinASTSymbol& value) {
1955   return implementation_.match(
1956       [](SingleEntryHuffmanTable&) -> JS::Result<Ok> {
1957         MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
1958         return Ok();
1959       },
1960       [index, bits, bitLength,
1961        value](TwoEntriesHuffmanTable&
1962                   implementation) mutable /* discard implicit const */
1963       -> JS::Result<Ok> {
1964         return implementation.addSymbol(index, bits, bitLength, value);
1965       },
1966       [index, bits, bitLength,
1967        value](SingleLookupHuffmanTable&
1968                   implementation) mutable /* discard implicit const */
1969       -> JS::Result<Ok> {
1970         return implementation.addSymbol(index, bits, bitLength, value);
1971       },
1972       [index, bits, bitLength,
1973        value](TwoLookupsHuffmanTable&
1974                   implementation) mutable /* discard implicit const */
1975       -> JS::Result<Ok> {
1976         return implementation.addSymbol(index, bits, bitLength, value);
1977       },
1978       [index, bits, bitLength,
1979        value = value](ThreeLookupsHuffmanTable&
1980                           implementation) mutable /* discard implicit const */
1981       -> JS::Result<Ok> {
1982         return implementation.addSymbol(index, bits, bitLength, value);
1983       },
1984       [](TableImplementationUninitialized&) -> JS::Result<Ok> {
1985         MOZ_CRASH("GenericHuffmanTable is unitialized!");
1986         return Ok();
1987       });
1988 }
1989 
lookup(HuffmanLookup key) const1990 HuffmanLookupResult GenericHuffmanTable::lookup(HuffmanLookup key) const {
1991   return implementation_.match(
1992       [key](const SingleEntryHuffmanTable& implementation)
1993           -> HuffmanLookupResult { return implementation.lookup(key); },
1994       [key](const TwoEntriesHuffmanTable& implementation)
1995           -> HuffmanLookupResult { return implementation.lookup(key); },
1996       [key](const SingleLookupHuffmanTable& implementation)
1997           -> HuffmanLookupResult { return implementation.lookup(key); },
1998       [key](const TwoLookupsHuffmanTable& implementation)
1999           -> HuffmanLookupResult { return implementation.lookup(key); },
2000       [key](const ThreeLookupsHuffmanTable& implementation)
2001           -> HuffmanLookupResult { return implementation.lookup(key); },
2002       [](const TableImplementationUninitialized&) -> HuffmanLookupResult {
2003         MOZ_CRASH("GenericHuffmanTable is unitialized!");
2004       });
2005 }
2006 
length() const2007 size_t GenericHuffmanTable::length() const {
2008   return implementation_.match(
2009       [](const SingleEntryHuffmanTable& implementation) -> size_t {
2010         return implementation.length();
2011       },
2012       [](const TwoEntriesHuffmanTable& implementation) -> size_t {
2013         return implementation.length();
2014       },
2015       [](const SingleLookupHuffmanTable& implementation) -> size_t {
2016         return implementation.length();
2017       },
2018       [](const TwoLookupsHuffmanTable& implementation) -> size_t {
2019         return implementation.length();
2020       },
2021       [](const ThreeLookupsHuffmanTable& implementation) -> size_t {
2022         return implementation.length();
2023       },
2024       [](const TableImplementationUninitialized& implementation) -> size_t {
2025         MOZ_CRASH("GenericHuffmanTable is unitialized!");
2026       });
2027 }
2028 
Iterator(const BinASTSymbol * position)2029 SingleEntryHuffmanTable::Iterator::Iterator(const BinASTSymbol* position)
2030     : position_(position) {}
2031 
operator ++()2032 void SingleEntryHuffmanTable::Iterator::operator++() {
2033   // There's only one entry, and `nullptr` means `end`.
2034   position_ = nullptr;
2035 }
2036 
operator *() const2037 const BinASTSymbol* SingleEntryHuffmanTable::Iterator::operator*() const {
2038   return position_;
2039 }
2040 
operator ->() const2041 const BinASTSymbol* SingleEntryHuffmanTable::Iterator::operator->() const {
2042   return position_;
2043 }
2044 
operator ==(const Iterator & other) const2045 bool SingleEntryHuffmanTable::Iterator::operator==(
2046     const Iterator& other) const {
2047   return position_ == other.position_;
2048 }
2049 
operator !=(const Iterator & other) const2050 bool SingleEntryHuffmanTable::Iterator::operator!=(
2051     const Iterator& other) const {
2052   return position_ != other.position_;
2053 }
2054 
lookup(HuffmanLookup key) const2055 HuffmanLookupResult SingleEntryHuffmanTable::lookup(HuffmanLookup key) const {
2056   return HuffmanLookupResult::found(0, &value_);
2057 }
2058 
Iterator(const BinASTSymbol * position)2059 TwoEntriesHuffmanTable::Iterator::Iterator(const BinASTSymbol* position)
2060     : position_(position) {}
2061 
operator ++()2062 void TwoEntriesHuffmanTable::Iterator::operator++() { position_++; }
2063 
operator *() const2064 const BinASTSymbol* TwoEntriesHuffmanTable::Iterator::operator*() const {
2065   return position_;
2066 }
2067 
operator ->() const2068 const BinASTSymbol* TwoEntriesHuffmanTable::Iterator::operator->() const {
2069   return position_;
2070 }
2071 
operator ==(const Iterator & other) const2072 bool TwoEntriesHuffmanTable::Iterator::operator==(const Iterator& other) const {
2073   return position_ == other.position_;
2074 }
2075 
operator !=(const Iterator & other) const2076 bool TwoEntriesHuffmanTable::Iterator::operator!=(const Iterator& other) const {
2077   return position_ != other.position_;
2078 }
2079 
initStart(JSContext * cx,TemporaryStorage * tempStorage,size_t numberOfSymbols,uint8_t largestBitLength)2080 JS::Result<Ok> TwoEntriesHuffmanTable::initStart(JSContext* cx,
2081                                                  TemporaryStorage* tempStorage,
2082                                                  size_t numberOfSymbols,
2083                                                  uint8_t largestBitLength) {
2084   // Make sure that we're initializing.
2085   MOZ_ASSERT(numberOfSymbols == 2);
2086   MOZ_ASSERT(largestBitLength == 1);
2087   return Ok();
2088 }
2089 
initComplete(JSContext * cx,TemporaryStorage * tempStorage)2090 JS::Result<Ok> TwoEntriesHuffmanTable::initComplete(
2091     JSContext* cx, TemporaryStorage* tempStorage) {
2092   return Ok();
2093 }
2094 
addSymbol(size_t index,uint32_t bits,uint8_t bitLength,const BinASTSymbol & value)2095 JS::Result<Ok> TwoEntriesHuffmanTable::addSymbol(size_t index, uint32_t bits,
2096                                                  uint8_t bitLength,
2097                                                  const BinASTSymbol& value) {
2098   // Symbols must be ranked by increasing bits length
2099   MOZ_ASSERT_IF(index == 0, bits == 0);
2100   MOZ_ASSERT_IF(index == 1, bits == 1);
2101 
2102   // FIXME: Throw soft error instead of assert.
2103   MOZ_ASSERT(bitLength == 1);
2104 
2105   values_[index] = value;
2106   return Ok();
2107 }
2108 
lookup(HuffmanLookup key) const2109 HuffmanLookupResult TwoEntriesHuffmanTable::lookup(HuffmanLookup key) const {
2110   // By invariant, bit lengths are 1.
2111   const auto index = key.leadingBits(1);
2112   return HuffmanLookupResult::found(1, &values_[index]);
2113 }
2114 
Iterator(const HuffmanEntry * position)2115 SingleLookupHuffmanTable::Iterator::Iterator(const HuffmanEntry* position)
2116     : position_(position) {}
2117 
operator ++()2118 void SingleLookupHuffmanTable::Iterator::operator++() { position_++; }
2119 
operator *() const2120 const BinASTSymbol* SingleLookupHuffmanTable::Iterator::operator*() const {
2121   return &position_->value();
2122 }
2123 
operator ->() const2124 const BinASTSymbol* SingleLookupHuffmanTable::Iterator::operator->() const {
2125   return &position_->value();
2126 }
2127 
operator ==(const Iterator & other) const2128 bool SingleLookupHuffmanTable::Iterator::operator==(
2129     const Iterator& other) const {
2130   return position_ == other.position_;
2131 }
2132 
operator !=(const Iterator & other) const2133 bool SingleLookupHuffmanTable::Iterator::operator!=(
2134     const Iterator& other) const {
2135   return position_ != other.position_;
2136 }
2137 
initStart(JSContext * cx,TemporaryStorage * tempStorage,size_t numberOfSymbols,uint8_t largestBitLength)2138 JS::Result<Ok> SingleLookupHuffmanTable::initStart(
2139     JSContext* cx, TemporaryStorage* tempStorage, size_t numberOfSymbols,
2140     uint8_t largestBitLength) {
2141   MOZ_ASSERT_IF(largestBitLength != 32,
2142                 (uint32_t(1) << largestBitLength) - 1 <=
2143                     std::numeric_limits<InternalIndex>::max());
2144 
2145   largestBitLength_ = largestBitLength;
2146 
2147   MOZ_TRY_VAR(values_, tempStorage->alloc<HuffmanEntry>(cx, numberOfSymbols));
2148 
2149   const size_t saturatedLength = 1 << largestBitLength_;
2150   MOZ_TRY_VAR(saturated_,
2151               tempStorage->alloc<InternalIndex>(cx, saturatedLength));
2152 
2153   // Enlarge `saturated_`, as we're going to fill it in random order.
2154   for (size_t i = 0; i < saturatedLength; ++i) {
2155     // Capacity reserved in this method.
2156     saturated_[i] = InternalIndex(-1);
2157   }
2158   return Ok();
2159 }
2160 
initComplete(JSContext * cx,TemporaryStorage * tempStorage)2161 JS::Result<Ok> SingleLookupHuffmanTable::initComplete(
2162     JSContext* cx, TemporaryStorage* tempStorage) {
2163   // Double-check that we've initialized properly.
2164   MOZ_ASSERT(largestBitLength_ <= MAX_CODE_BIT_LENGTH);
2165 
2166   // We can end up with empty tables, if this `SingleLookupHuffmanTable`
2167   // is used to store suffixes in a `MultiLookupHuffmanTable` and
2168   // the corresponding prefix is never used.
2169   if (values_.empty()) {
2170     MOZ_ASSERT(largestBitLength_ == 0);
2171     return Ok();
2172   }
2173 
2174 #ifdef DEBUG
2175   bool foundMaxBitLength = false;
2176   for (size_t i = 0; i < saturated_.size(); ++i) {
2177     const uint8_t index = saturated_[i];
2178     if (use_ != Use::ToplevelTable) {
2179       // The table may not be full.
2180       if (index >= values_.size()) {
2181         continue;
2182       }
2183     }
2184     MOZ_ASSERT(values_[index].key().bitLength_ <= largestBitLength_);
2185     if (values_[index].key().bitLength_ == largestBitLength_) {
2186       foundMaxBitLength = true;
2187     }
2188   }
2189   MOZ_ASSERT(foundMaxBitLength);
2190 #endif  // DEBUG
2191 
2192   return Ok();
2193 }
2194 
addSymbol(size_t index,uint32_t bits,uint8_t bitLength,const BinASTSymbol & value)2195 JS::Result<Ok> SingleLookupHuffmanTable::addSymbol(size_t index, uint32_t bits,
2196                                                    uint8_t bitLength,
2197                                                    const BinASTSymbol& value) {
2198   MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
2199   MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
2200   MOZ_ASSERT(bitLength <= largestBitLength_);
2201 
2202   // First add the value to `values_`.
2203   new (mozilla::KnownNotNull, &values_[index])
2204       HuffmanEntry(bits, bitLength, value);
2205 
2206   // Notation: in the following, unless otherwise specified, we consider
2207   // values with `largestBitLength_` bits exactly.
2208   //
2209   // When we perform lookup, we will extract `largestBitLength_` bits from the
2210   // key into a value `0bB...B`. We have a match for `value` if and only if
2211   // `0bB...B` may be decomposed into `0bC...CX...X` such that
2212   //    - `0bC...C` is `bitLength` bits long;
2213   //    - `0bC...C == bits`.
2214   //
2215   // To perform a fast lookup, we precompute all possible values of `0bB...B`
2216   // for which this condition is true. That's all the values of segment
2217   // `[0bC...C0...0, 0bC...C1...1]`.
2218   const HuffmanLookup base(bits, bitLength);
2219   for (size_t i : base.suffixes(largestBitLength_)) {
2220     saturated_[i] = index;
2221   }
2222 
2223   return Ok();
2224 }
2225 
lookup(HuffmanLookup key) const2226 HuffmanLookupResult SingleLookupHuffmanTable::lookup(HuffmanLookup key) const {
2227   if (values_.empty()) {
2228     // If the table is empty, any lookup fails.
2229     return HuffmanLookupResult::notFound();
2230   }
2231   // ...otherwise, all lookups succeed.
2232 
2233   // Take the `largestBitLength_` highest weight bits of `key`.
2234   // In the documentation of `addSymbol`, this is
2235   // `0bB...B`.
2236   const uint32_t bits = key.leadingBits(largestBitLength_);
2237 
2238   // Invariants: `saturated_.size() == 1 << largestBitLength_`
2239   // and `bits <= 1 << largestBitLength_`.
2240   const size_t index = saturated_[bits];
2241   if (index >= values_.size()) {
2242     // This is useful only when the `SingleLookupHuffmanTable`
2243     // is used as a cache inside a `MultiLookupHuffmanTable`.
2244     MOZ_ASSERT(use_ == Use::ShortKeys);
2245     return HuffmanLookupResult::notFound();
2246   }
2247 
2248   // Invariants: `saturated_[i] < values_.size()`.
2249   const auto& entry = values_[index];
2250   return HuffmanLookupResult::found(entry.key().bitLength_, &entry.value());
2251 }
2252 
2253 template <typename Subtable, uint8_t PrefixBitLength>
Iterator(const HuffmanEntry * position)2254 MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::Iterator(
2255     const HuffmanEntry* position)
2256     : position_(position) {}
2257 
2258 template <typename Subtable, uint8_t PrefixBitLength>
2259 void MultiLookupHuffmanTable<Subtable,
operator ++()2260                              PrefixBitLength>::Iterator::operator++() {
2261   position_++;
2262 }
2263 
2264 template <typename Subtable, uint8_t PrefixBitLength>
2265 const BinASTSymbol* MultiLookupHuffmanTable<
operator *() const2266     Subtable, PrefixBitLength>::Iterator::operator*() const {
2267   return &position_->value();
2268 }
2269 
2270 template <typename Subtable, uint8_t PrefixBitLength>
2271 const BinASTSymbol* MultiLookupHuffmanTable<
operator ->() const2272     Subtable, PrefixBitLength>::Iterator::operator->() const {
2273   return &position_->value();
2274 }
2275 
2276 template <typename Subtable, uint8_t PrefixBitLength>
operator ==(const Iterator & other) const2277 bool MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::operator==(
2278     const Iterator& other) const {
2279   return position_ == other.position_;
2280 }
2281 
2282 template <typename Subtable, uint8_t PrefixBitLength>
operator !=(const Iterator & other) const2283 bool MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::operator!=(
2284     const Iterator& other) const {
2285   return position_ != other.position_;
2286 }
2287 
2288 template <typename Subtable, uint8_t PrefixBitLength>
initStart(JSContext * cx,TemporaryStorage * tempStorage,size_t numberOfSymbols,uint8_t largestBitLength)2289 JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initStart(
2290     JSContext* cx, TemporaryStorage* tempStorage, size_t numberOfSymbols,
2291     uint8_t largestBitLength) {
2292   static_assert(PrefixBitLength < MAX_CODE_BIT_LENGTH,
2293                 "Invalid PrefixBitLength");
2294   largestBitLength_ = largestBitLength;
2295 
2296   MOZ_TRY_VAR(values_, tempStorage->alloc<HuffmanEntry>(cx, numberOfSymbols));
2297 
2298   auto numTables = 1 << PrefixBitLength;
2299   MOZ_TRY_VAR(suffixTables_, tempStorage->alloc<Subtable>(cx, numTables));
2300 
2301   return Ok();
2302 }
2303 
2304 template <typename Subtable, uint8_t PrefixBitLength>
addSymbol(size_t index,uint32_t bits,uint8_t bitLength,const BinASTSymbol & value)2305 JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::addSymbol(
2306     size_t index, uint32_t bits, uint8_t bitLength, const BinASTSymbol& value) {
2307   MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
2308   MOZ_ASSERT(index == 0 || values_[index - 1].key().bitLength_ <= bitLength,
2309              "Symbols must be ranked by increasing bits length");
2310   MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
2311 
2312   new (mozilla::KnownNotNull, &values_[index])
2313       HuffmanEntry(bits, bitLength, value);
2314 
2315   return Ok();
2316 }
2317 
2318 template <typename Subtable, uint8_t PrefixBitLength>
initComplete(JSContext * cx,TemporaryStorage * tempStorage)2319 JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete(
2320     JSContext* cx, TemporaryStorage* tempStorage) {
2321   // First, we need to collect the `largestBitLength_`
2322   // and `numberofSymbols` for each subtable.
2323   struct Bucket {
2324     Bucket() : largestBitLength_(0), numberOfSymbols_(0){};
2325     uint8_t largestBitLength_;
2326     uint32_t numberOfSymbols_;
2327     void addSymbol(uint8_t bitLength) {
2328       ++numberOfSymbols_;
2329       if (bitLength > largestBitLength_) {
2330         largestBitLength_ = bitLength;
2331       }
2332     }
2333   };
2334   FixedLengthVector<Bucket> buckets;
2335   if (!buckets.allocate(cx, 1 << PrefixBitLength)) {
2336     return cx->alreadyReportedError();
2337   }
2338   Bucket shortKeysBucket;
2339 
2340   for (const auto& entry : values_) {
2341     if (entry.key().bitLength_ <= SingleLookupHuffmanTable::MAX_BIT_LENGTH) {
2342       // If the key is short, we put it in `shortKeys_` instead of
2343       // `suffixTables`.
2344       shortKeysBucket.addSymbol(entry.key().bitLength_);
2345       continue;
2346     }
2347     const HuffmanLookup lookup(entry.key().bits_, entry.key().bitLength_);
2348     const auto split = lookup.split(PrefixBitLength);
2349     MOZ_ASSERT_IF(split.suffix_.bitLength_ != 32,
2350                   split.suffix_.bits_ >> split.suffix_.bitLength_ == 0);
2351 
2352     // Entries that have a sufficient number of bits will be dispatched
2353     // to a single subtable (e.g. A, B, C, D, E, F in the documentation).
2354     // Other entries need to be dispatched to several subtables
2355     // (e.g. G, H in the documentation).
2356     for (const auto index : lookup.suffixes(PrefixBitLength)) {
2357       Bucket& bucket = buckets[index];
2358       bucket.addSymbol(split.suffix_.bitLength_);
2359     }
2360   }
2361 
2362   FixedLengthVector<size_t> suffixTablesIndices;
2363   if (MOZ_UNLIKELY(!suffixTablesIndices.allocateUninitialized(
2364           cx, suffixTables_.size()))) {
2365     return cx->alreadyReportedError();
2366   }
2367 
2368   // We may now create the subtables.
2369   size_t i = 0;
2370   for (auto& bucket : buckets) {
2371     new (mozilla::KnownNotNull, &suffixTables_[i]) Subtable();
2372     suffixTablesIndices[i] = 0;
2373 
2374     if (bucket.numberOfSymbols_ != 0) {
2375       // Often, a subtable will end up empty because all the prefixes end up
2376       // in `shortKeys_`. In such a case, we want to avoid initializing the
2377       // table.
2378       MOZ_TRY(suffixTables_[i].initStart(
2379           cx, tempStorage,
2380           /* numberOfSymbols = */ bucket.numberOfSymbols_,
2381           /* maxBitLength = */ bucket.largestBitLength_));
2382     }
2383 
2384     i++;
2385   }
2386 
2387   // Also, create the shortKeys_ fast lookup.
2388   MOZ_TRY(shortKeys_.initStart(cx, tempStorage,
2389                                shortKeysBucket.numberOfSymbols_,
2390                                shortKeysBucket.largestBitLength_));
2391 
2392   // Now that all the subtables are created, let's dispatch the values
2393   // among these tables.
2394   size_t shortKeysIndex = 0;
2395   for (size_t i = 0; i < values_.size(); ++i) {
2396     const auto& entry = values_[i];
2397     if (entry.key().bitLength_ <= SingleLookupHuffmanTable::MAX_BIT_LENGTH) {
2398       // The key fits in `shortKeys_`, let's use this table.
2399       MOZ_TRY(shortKeys_.addSymbol(shortKeysIndex++, entry.key().bits_,
2400                                    entry.key().bitLength_,
2401                                    BinASTSymbol::fromSubtableIndex(i)));
2402       continue;
2403     }
2404 
2405     // Otherwise, use one of the suffix tables.
2406     const HuffmanLookup lookup(entry.key().bits_, entry.key().bitLength_);
2407     const auto split = lookup.split(PrefixBitLength);
2408     MOZ_ASSERT_IF(split.suffix_.bitLength_ != 32,
2409                   split.suffix_.bits_ >> split.suffix_.bitLength_ == 0);
2410     for (const auto index : lookup.suffixes(PrefixBitLength)) {
2411       auto& sub = suffixTables_[index];
2412 
2413       // We may now add a reference to `entry` into the sybtable.
2414       MOZ_TRY(sub.addSymbol(suffixTablesIndices[index]++, split.suffix_.bits_,
2415                             split.suffix_.bitLength_,
2416                             BinASTSymbol::fromSubtableIndex(i)));
2417     }
2418   }
2419 
2420   // Finally, complete initialization of shortKeys_ and subtables.
2421   MOZ_TRY(shortKeys_.initComplete(cx, tempStorage));
2422   for (size_t i = 0; i < buckets.length(); ++i) {
2423     if (buckets[i].numberOfSymbols_ == 0) {
2424       // Again, we don't want to initialize empty subtables.
2425       continue;
2426     }
2427     auto& sub = suffixTables_[i];
2428     MOZ_TRY(sub.initComplete(cx, tempStorage));
2429   }
2430 
2431   return Ok();
2432 }
2433 
2434 template <typename Subtable, uint8_t PrefixBitLength>
lookup(HuffmanLookup key) const2435 HuffmanLookupResult MultiLookupHuffmanTable<Subtable, PrefixBitLength>::lookup(
2436     HuffmanLookup key) const {
2437   {
2438     // Let's first look in shortkeys.
2439     auto subResult = shortKeys_.lookup(key);
2440     if (subResult.isFound()) {
2441       // We have found a result in the shortKeys_ fastpath.
2442       const auto& result = values_[subResult.value().toSubtableIndex()];
2443 
2444       return HuffmanLookupResult::found(result.key().bitLength_,
2445                                         &result.value());
2446     }
2447   }
2448   const auto split = key.split(PrefixBitLength);
2449   if (split.prefix_.bits_ >= suffixTables_.size()) {
2450     return HuffmanLookupResult::notFound();
2451   }
2452   const Subtable& subtable = suffixTables_[split.prefix_.bits_];
2453 
2454   auto subResult = subtable.lookup(split.suffix_);
2455   if (!subResult.isFound()) {
2456     // Propagate "not found".
2457     return HuffmanLookupResult::notFound();
2458   }
2459 
2460   // Otherwise, restore the entire `HuffmanEntry`.
2461   const auto& result = values_[subResult.value().toSubtableIndex()];
2462 
2463   return HuffmanLookupResult::found(result.key().bitLength_, &result.value());
2464 }
2465 
2466 // -----
2467 
2468 // The number of possible interfaces in each sum, indexed by
2469 // `static_cast<size_t>(BinASTSum)`.
2470 const size_t SUM_LIMITS[]{
2471 #define WITH_SUM(_ENUM_NAME, _HUMAN_NAME, MACRO_NAME, _TYPE_NAME) \
2472   BINAST_SUM_##MACRO_NAME##_LIMIT,
2473     FOR_EACH_BIN_SUM(WITH_SUM)
2474 #undef WITH_SUM
2475 };
2476 
2477 // For each sum S, an array from [0, SUM_LIMITS[S]( to the BinASTKind of the ith
2478 // interface of the sum, ranked by the same sum order as the rest of the .
2479 //
2480 // For instance, as we have
2481 // ArrowExpression ::= EagerArrowExpressionWithExpression
2482 //                 |   EagerArrowExpressionWithFunctionBody
2483 //                 |   ...
2484 //
2485 // SUM_RESOLUTION_ARROW_EXPRESSION[0] ==
2486 // BinASTKind::EagerArrowExpressionWithExpression
2487 // SUM_RESOLUTION_ARROW_EXPRESSION[1] ==
2488 // BinASTKind::EagerArrowExpressionWithFunctionBody
2489 // ...
2490 #define WITH_SUM_CONTENTS(_SUM_NAME, _INDEX, INTERFACE_NAME, _MACRO_NAME, \
2491                           _SPEC_NAME)                                     \
2492   BinASTKind::INTERFACE_NAME,
2493 #define WITH_SUM(_ENUM_NAME, _HUMAN_NAME, MACRO_NAME, _TYPE_NAME) \
2494   const BinASTKind SUM_RESOLUTION_##MACRO_NAME[]{                 \
2495       FOR_EACH_BIN_INTERFACE_IN_SUM_##MACRO_NAME(WITH_SUM_CONTENTS)};
2496 FOR_EACH_BIN_SUM(WITH_SUM)
2497 #undef WITH_SUM
2498 #undef WITH_SUM_CONTENTS
2499 
2500 const BinASTKind* SUM_RESOLUTIONS[BINAST_NUMBER_OF_SUM_TYPES]{
2501 #define WITH_SUM(_ENUM_NAME, _HUMAN_NAME, MACRO_NAME, _TYPE_NAME) \
2502   SUM_RESOLUTION_##MACRO_NAME,
2503     FOR_EACH_BIN_SUM(WITH_SUM)
2504 #undef WITH_SUM
2505 };
2506 
2507 // The number of possible interfaces in each string enum, indexed by
2508 // `static_cast<size_t>(BinASTStringEnum)`.
2509 const size_t STRING_ENUM_LIMITS[]{
2510 #define WITH_ENUM(name, _, MACRO_NAME) BIN_AST_STRING_ENUM_##MACRO_NAME##_LIMIT,
2511     FOR_EACH_BIN_STRING_ENUM(WITH_ENUM)
2512 #undef WITH_ENUM
2513 };
2514 
2515 #define WITH_ENUM_CONTENTS(_ENUM_NAME, VARIANT_NAME, _HUMAN_NAME) \
2516   BinASTVariant::VARIANT_NAME,
2517 #define WITH_ENUM(_ENUM_NAME, _, MACRO_NAME)                              \
2518   const BinASTVariant STRING_ENUM_RESOLUTION_##MACRO_NAME[]{              \
2519       FOR_EACH_BIN_VARIANT_IN_STRING_ENUM_##MACRO_NAME##_BY_WEBIDL_ORDER( \
2520           WITH_ENUM_CONTENTS)};
2521 FOR_EACH_BIN_STRING_ENUM(WITH_ENUM)
2522 #undef WITH_ENUM
2523 #undef WITH_ENUM_CONTENTS
2524 
2525 const BinASTVariant* STRING_ENUM_RESOLUTIONS[BINASTSTRINGENUM_LIMIT]{
2526 #define WITH_ENUM(name, _, MACRO_NAME) STRING_ENUM_RESOLUTION_##MACRO_NAME,
2527     FOR_EACH_BIN_STRING_ENUM(WITH_ENUM)
2528 #undef WITH_ENUM
2529 };
2530 
2531 // Start reading the prelude.
2532 MOZ_MUST_USE JS::Result<HuffmanDictionaryForMetadata*>
run(size_t initialCapacity)2533 HuffmanPreludeReader::run(size_t initialCapacity) {
2534   BINJS_TRY(stack_.reserve(initialCapacity));
2535 
2536   dictionary_.reset(cx_->new_<HuffmanDictionary>());
2537   BINJS_TRY(dictionary_);
2538 
2539   // For the moment, the root node is hardcoded to be a BinASTKind::Script.
2540   // In future versions of the codec, we'll extend the format to handle
2541   // other possible roots (e.g. BinASTKind::Module).
2542   MOZ_TRY(pushFields(BinASTKind::Script));
2543   while (stack_.length() > 0) {
2544     const Entry entry = stack_.popCopy();
2545     MOZ_TRY(entry.match(ReadPoppedEntryMatcher(*this)));
2546   }
2547 
2548   auto dictForMetadata = HuffmanDictionaryForMetadata::createFrom(
2549       dictionary_.get(), &tempStorage_);
2550   if (!dictForMetadata) {
2551     ReportOutOfMemory(cx_);
2552     return cx_->alreadyReportedError();
2553   }
2554 
2555   return dictForMetadata;
2556 }
2557 
2558 // ------ Reading booleans.
2559 // 0 -> False
2560 // 1 -> True
2561 
2562 // Extract the number of symbols from the grammar.
2563 template <>
readNumberOfSymbols(const Boolean &)2564 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2565     const Boolean&) {
2566   // Sadly, there are only two booleans known to this date.
2567   return 2;
2568 }
2569 
2570 // Extract symbol from the grammar.
2571 template <>
readSymbol(const Boolean &,size_t index)2572 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2573     const Boolean&, size_t index) {
2574   MOZ_ASSERT(index < 2);
2575   return BinASTSymbol::fromBool(index != 0);
2576 }
2577 
2578 // Reading a single-value table of booleans
2579 template <>
readSingleValueTable(GenericHuffmanTable & table,const Boolean & entry)2580 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Boolean>(
2581     GenericHuffmanTable& table, const Boolean& entry) {
2582   uint8_t indexByte;
2583   MOZ_TRY_VAR(indexByte, reader_.readByte<Compression::No>());
2584   if (MOZ_UNLIKELY(indexByte >= 2)) {
2585     return raiseInvalidTableData(entry.identity_);
2586   }
2587 
2588   MOZ_TRY(
2589       table.initWithSingleValue(cx_, BinASTSymbol::fromBool(indexByte != 0)));
2590   return Ok();
2591 }
2592 
2593 // ------ Optional interfaces.
2594 // 0 -> Null
2595 // 1 -> NonNull
2596 
2597 // Extract the number of symbols from the grammar.
2598 template <>
readNumberOfSymbols(const MaybeInterface &)2599 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2600     const MaybeInterface&) {
2601   // Null, NonNull
2602   return 2;
2603 }
2604 
2605 // Extract symbol from the grammar.
2606 template <>
readSymbol(const MaybeInterface & entry,size_t index)2607 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2608     const MaybeInterface& entry, size_t index) {
2609   MOZ_ASSERT(index < 2);
2610   return BinASTSymbol::fromKind(index == 0 ? BinASTKind::_Null : entry.kind_);
2611 }
2612 
2613 // Reading a single-value table of optional interfaces
2614 template <>
2615 MOZ_MUST_USE JS::Result<Ok>
readSingleValueTable(GenericHuffmanTable & table,const MaybeInterface & entry)2616 HuffmanPreludeReader::readSingleValueTable<MaybeInterface>(
2617     GenericHuffmanTable& table, const MaybeInterface& entry) {
2618   uint8_t indexByte;
2619   MOZ_TRY_VAR(indexByte, reader_.readByte<Compression::No>());
2620   if (MOZ_UNLIKELY(indexByte >= 2)) {
2621     return raiseInvalidTableData(entry.identity_);
2622   }
2623 
2624   MOZ_TRY(table.initWithSingleValue(
2625       cx_, BinASTSymbol::fromKind(indexByte == 0 ? BinASTKind::_Null
2626                                                  : entry.kind_)));
2627   return Ok();
2628 }
2629 
2630 // ------ Sums of interfaces
2631 // varnum i -> index `i` in the order defined by
2632 // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`
2633 
2634 // Extract the number of symbols from the grammar.
2635 template <>
readNumberOfSymbols(const Sum & sum)2636 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2637     const Sum& sum) {
2638   return sum.maxNumberOfSymbols();
2639 }
2640 
2641 // Extract symbol from the grammar.
2642 template <>
readSymbol(const Sum & entry,size_t index)2643 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2644     const Sum& entry, size_t index) {
2645   MOZ_ASSERT(index < entry.maxNumberOfSymbols());
2646   return BinASTSymbol::fromKind(entry.interfaceAt(index));
2647 }
2648 
2649 // Reading a single-value table of sums of interfaces.
2650 template <>
readSingleValueTable(GenericHuffmanTable & table,const Sum & sum)2651 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Sum>(
2652     GenericHuffmanTable& table, const Sum& sum) {
2653   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2654   if (MOZ_UNLIKELY(index >= sum.maxNumberOfSymbols())) {
2655     return raiseInvalidTableData(sum.identity_);
2656   }
2657 
2658   MOZ_TRY(table.initWithSingleValue(
2659       cx_, BinASTSymbol::fromKind(sum.interfaceAt(index))));
2660   return Ok();
2661 }
2662 
2663 // ------ Optional sums of interfaces
2664 // varnum 0 -> null
2665 // varnum i > 0 -> index `i - 1` in the order defined by
2666 // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`
2667 
2668 // Extract the number of symbols from the grammar.
2669 template <>
readNumberOfSymbols(const MaybeSum & sum)2670 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2671     const MaybeSum& sum) {
2672   return sum.maxNumberOfSymbols();
2673 }
2674 
2675 // Extract symbol from the grammar.
2676 template <>
readSymbol(const MaybeSum & sum,size_t index)2677 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2678     const MaybeSum& sum, size_t index) {
2679   MOZ_ASSERT(index < sum.maxNumberOfSymbols());
2680   return BinASTSymbol::fromKind(sum.interfaceAt(index));
2681 }
2682 
2683 // Reading a single-value table of sums of interfaces.
2684 template <>
2685 MOZ_MUST_USE JS::Result<Ok>
readSingleValueTable(GenericHuffmanTable & table,const MaybeSum & sum)2686 HuffmanPreludeReader::readSingleValueTable<MaybeSum>(GenericHuffmanTable& table,
2687                                                      const MaybeSum& sum) {
2688   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2689   if (MOZ_UNLIKELY(index >= sum.maxNumberOfSymbols())) {
2690     return raiseInvalidTableData(sum.identity_);
2691   }
2692 
2693   MOZ_TRY(table.initWithSingleValue(
2694       cx_, BinASTSymbol::fromKind(sum.interfaceAt(index))));
2695   return Ok();
2696 }
2697 
2698 // ------ Numbers
2699 // 64 bits, IEEE 754, big endian
2700 
2701 // Read the number of symbols from the stream.
2702 template <>
readNumberOfSymbols(const Number & number)2703 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2704     const Number& number) {
2705   BINJS_MOZ_TRY_DECL(length, reader_.readVarU32<Compression::No>());
2706   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
2707     return raiseInvalidTableData(number.identity_);
2708   }
2709   return length;
2710 }
2711 
2712 // Read a single symbol from the stream.
2713 template <>
readSymbol(const Number & number,size_t)2714 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2715     const Number& number, size_t) {
2716   uint8_t bytes[8];
2717   MOZ_ASSERT(sizeof(bytes) == sizeof(double));
2718 
2719   uint32_t len = mozilla::ArrayLength(bytes);
2720   MOZ_TRY((reader_.readBuf<Compression::No, EndOfFilePolicy::RaiseError>(
2721       reinterpret_cast<uint8_t*>(bytes), len)));
2722 
2723   // Decode big-endian.
2724   const uint64_t asInt = mozilla::BigEndian::readUint64(bytes);
2725 
2726   // Canonicalize NaN, just to make sure another form of signalling NaN
2727   // doesn't slip past us.
2728   return BinASTSymbol::fromDouble(
2729       JS::CanonicalizeNaN(mozilla::BitwiseCast<double>(asInt)));
2730 }
2731 
2732 // Reading a single-value table of numbers.
2733 template <>
readSingleValueTable(GenericHuffmanTable & table,const Number & number)2734 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Number>(
2735     GenericHuffmanTable& table, const Number& number) {
2736   BINJS_MOZ_TRY_DECL(value, readSymbol(number, 0 /* ignored */));
2737   MOZ_TRY(table.initWithSingleValue(cx_, value));
2738   return Ok();
2739 }
2740 
2741 // ------ List lengths
2742 // varnum
2743 
2744 // Read the number of symbols from the grammar.
2745 template <>
readNumberOfSymbols(const List & list)2746 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2747     const List& list) {
2748   BINJS_MOZ_TRY_DECL(length, reader_.readVarU32<Compression::No>());
2749   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
2750     return raiseInvalidTableData(list.identity_);
2751   }
2752   return length;
2753 }
2754 
2755 // Read a single symbol from the stream.
2756 template <>
readSymbol(const List & list,size_t)2757 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2758     const List& list, size_t) {
2759   BINJS_MOZ_TRY_DECL(length, reader_.readUnpackedLong());
2760   if (MOZ_UNLIKELY(length > MAX_LIST_LENGTH)) {
2761     return raiseInvalidTableData(list.identity_);
2762   }
2763   return BinASTSymbol::fromListLength(length);
2764 }
2765 
2766 // Reading a single-value table of list lengths.
2767 template <>
readSingleValueTable(GenericHuffmanTable & table,const List & list)2768 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<List>(
2769     GenericHuffmanTable& table, const List& list) {
2770   BINJS_MOZ_TRY_DECL(length, reader_.readUnpackedLong());
2771   if (MOZ_UNLIKELY(length > MAX_LIST_LENGTH)) {
2772     return raiseInvalidTableData(list.identity_);
2773   }
2774   MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromListLength(length)));
2775   return Ok();
2776 }
2777 
2778 // ------ Strings, non-nullable
2779 // varnum (index)
2780 
2781 // Read the number of symbols from the stream.
2782 template <>
readNumberOfSymbols(const String & string)2783 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2784     const String& string) {
2785   BINJS_MOZ_TRY_DECL(length, reader_.readVarU32<Compression::No>());
2786   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS ||
2787                    length > reader_.metadata_->numStrings())) {
2788     return raiseInvalidTableData(string.identity_);
2789   }
2790   return length;
2791 }
2792 
2793 // Read a single symbol from the stream.
2794 template <>
readSymbol(const String & entry,size_t)2795 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2796     const String& entry, size_t) {
2797   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2798   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings())) {
2799     return raiseInvalidTableData(entry.identity_);
2800   }
2801   return BinASTSymbol::fromAtomIndex(index);
2802 }
2803 
2804 // Reading a single-value table of string indices.
2805 template <>
readSingleValueTable(GenericHuffmanTable & table,const String & entry)2806 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<String>(
2807     GenericHuffmanTable& table, const String& entry) {
2808   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2809   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings())) {
2810     return raiseInvalidTableData(entry.identity_);
2811   }
2812   MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromAtomIndex(index)));
2813   return Ok();
2814 }
2815 
2816 // ------ Optional strings
2817 // varnum 0 -> null
2818 // varnum i > 0 -> string at index i - 1
2819 
2820 // Read the number of symbols from the metadata.
2821 template <>
readNumberOfSymbols(const MaybeString & entry)2822 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2823     const MaybeString& entry) {
2824   BINJS_MOZ_TRY_DECL(length, reader_.readVarU32<Compression::No>());
2825   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS ||
2826                    length > reader_.metadata_->numStrings() + 1)) {
2827     return raiseInvalidTableData(entry.identity_);
2828   }
2829   return length;
2830 }
2831 
2832 // Read a single symbol from the stream.
2833 template <>
readSymbol(const MaybeString & entry,size_t)2834 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2835     const MaybeString& entry, size_t) {
2836   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2837   // (index == 0) is specified as `null` value and
2838   // (index > 0) maps to (index - 1)-th atom.
2839   if (index == 0) {
2840     return BinASTSymbol::nullAtom();
2841   }
2842   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings() + 1)) {
2843     return raiseInvalidTableData(entry.identity_);
2844   }
2845   return BinASTSymbol::fromAtomIndex(index - 1);
2846 }
2847 
2848 // Reading a single-value table of string indices.
2849 template <>
2850 MOZ_MUST_USE JS::Result<Ok>
readSingleValueTable(GenericHuffmanTable & table,const MaybeString & entry)2851 HuffmanPreludeReader::readSingleValueTable<MaybeString>(
2852     GenericHuffmanTable& table, const MaybeString& entry) {
2853   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2854   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings() + 1)) {
2855     return raiseInvalidTableData(entry.identity_);
2856   }
2857   // (index == 0) is specified as `null` value and
2858   // (index > 0) maps to (index - 1)-th atom.
2859   if (index == 0) {
2860     MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::nullAtom()));
2861   } else {
2862     MOZ_TRY(
2863         table.initWithSingleValue(cx_, BinASTSymbol::fromAtomIndex(index - 1)));
2864   }
2865   return Ok();
2866 }
2867 
2868 // ------ String Enums
2869 // varnum index in the enum
2870 
2871 // Read the number of symbols from the grammar.
2872 template <>
readNumberOfSymbols(const StringEnum & entry)2873 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2874     const StringEnum& entry) {
2875   return entry.maxNumberOfSymbols();
2876 }
2877 
2878 // Read a single symbol from the grammar.
2879 template <>
readSymbol(const StringEnum & entry,size_t index)2880 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2881     const StringEnum& entry, size_t index) {
2882   return BinASTSymbol::fromVariant(entry.variantAt(index));
2883 }
2884 
2885 // Reading a single-value table of string indices.
2886 template <>
2887 MOZ_MUST_USE JS::Result<Ok>
readSingleValueTable(GenericHuffmanTable & table,const StringEnum & entry)2888 HuffmanPreludeReader::readSingleValueTable<StringEnum>(
2889     GenericHuffmanTable& table, const StringEnum& entry) {
2890   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
2891   if (MOZ_UNLIKELY(index > entry.maxNumberOfSymbols())) {
2892     return raiseInvalidTableData(entry.identity_);
2893   }
2894 
2895   BinASTVariant symbol = entry.variantAt(index);
2896   MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromVariant(symbol)));
2897   return Ok();
2898 }
2899 
2900 // ------ Unsigned Longs
2901 // Unpacked 32-bit
2902 
2903 // Read the number of symbols from the stream.
2904 template <>
readNumberOfSymbols(const UnsignedLong & entry)2905 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
2906     const UnsignedLong& entry) {
2907   BINJS_MOZ_TRY_DECL(length, reader_.readVarU32<Compression::No>());
2908   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
2909     return raiseInvalidTableData(entry.identity_);
2910   }
2911   return length;
2912 }
2913 
2914 // Read a single symbol from the stream.
2915 template <>
readSymbol(const UnsignedLong & entry,size_t)2916 MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
2917     const UnsignedLong& entry, size_t) {
2918   BINJS_MOZ_TRY_DECL(result, reader_.readUnpackedLong());
2919   return BinASTSymbol::fromUnsignedLong(result);
2920 }
2921 
2922 // Reading a single-value table of string indices.
2923 template <>
2924 MOZ_MUST_USE JS::Result<Ok>
readSingleValueTable(GenericHuffmanTable & table,const UnsignedLong & entry)2925 HuffmanPreludeReader::readSingleValueTable<UnsignedLong>(
2926     GenericHuffmanTable& table, const UnsignedLong& entry) {
2927   BINJS_MOZ_TRY_DECL(index, reader_.readUnpackedLong());
2928   MOZ_TRY(
2929       table.initWithSingleValue(cx_, BinASTSymbol::fromUnsignedLong(index)));
2930   return Ok();
2931 }
2932 
~HuffmanDictionaryForMetadata()2933 HuffmanDictionaryForMetadata::~HuffmanDictionaryForMetadata() {
2934   // WARNING: If you change the code of this destructor,
2935   //          `clearFromIncompleteInitialization` needs to be synchronized.
2936   for (size_t i = 0; i < numTables_; i++) {
2937     tablesBase()[i].~GenericHuffmanTable();
2938   }
2939   for (size_t i = 0; i < numSingleTables_; i++) {
2940     singleTablesBase()[i].~SingleLookupHuffmanTable();
2941   }
2942   for (size_t i = 0; i < numTwoTables_; i++) {
2943     twoTablesBase()[i].~TwoLookupsHuffmanTable();
2944   }
2945 }
2946 
2947 /* static */
createFrom(HuffmanDictionary * dictionary,TemporaryStorage * tempStorage)2948 HuffmanDictionaryForMetadata* HuffmanDictionaryForMetadata::createFrom(
2949     HuffmanDictionary* dictionary, TemporaryStorage* tempStorage) {
2950   size_t numTables = dictionary->numTables();
2951   size_t numHuffmanEntries = tempStorage->numHuffmanEntries();
2952   size_t numInternalIndices = tempStorage->numInternalIndices();
2953   while (numInternalIndices * sizeof(InternalIndex) % sizeof(uintptr_t) != 0) {
2954     numInternalIndices++;
2955   }
2956   size_t numSingleTables = tempStorage->numSingleTables();
2957   size_t numTwoTables = tempStorage->numTwoTables();
2958 
2959   HuffmanDictionaryForMetadata* data =
2960       static_cast<HuffmanDictionaryForMetadata*>(
2961           js_malloc(totalSize(numTables, numHuffmanEntries, numInternalIndices,
2962                               numSingleTables, numTwoTables)));
2963   if (MOZ_UNLIKELY(!data)) {
2964     return nullptr;
2965   }
2966 
2967   new (mozilla::KnownNotNull, data) HuffmanDictionaryForMetadata(
2968       numTables, numHuffmanEntries, numInternalIndices, numSingleTables,
2969       numTwoTables);
2970 
2971   data->moveFrom(dictionary, tempStorage);
2972   return data;
2973 }
2974 
2975 /* static */
create(size_t numTables,size_t numHuffmanEntries,size_t numInternalIndices,size_t numSingleTables,size_t numTwoTables)2976 HuffmanDictionaryForMetadata* HuffmanDictionaryForMetadata::create(
2977     size_t numTables, size_t numHuffmanEntries, size_t numInternalIndices,
2978     size_t numSingleTables, size_t numTwoTables) {
2979   HuffmanDictionaryForMetadata* data =
2980       static_cast<HuffmanDictionaryForMetadata*>(
2981           js_malloc(totalSize(numTables, numHuffmanEntries, numInternalIndices,
2982                               numSingleTables, numTwoTables)));
2983   if (MOZ_UNLIKELY(!data)) {
2984     return nullptr;
2985   }
2986 
2987   new (mozilla::KnownNotNull, data) HuffmanDictionaryForMetadata(
2988       numTables, numHuffmanEntries, numInternalIndices, numSingleTables,
2989       numTwoTables);
2990 
2991   return data;
2992 }
2993 
clearFromIncompleteInitialization(size_t numInitializedTables,size_t numInitializedSingleTables,size_t numInitializedTwoTables)2994 void HuffmanDictionaryForMetadata::clearFromIncompleteInitialization(
2995     size_t numInitializedTables, size_t numInitializedSingleTables,
2996     size_t numInitializedTwoTables) {
2997   // This is supposed to be called from AutoClearHuffmanDictionaryForMetadata.
2998   // See AutoClearHuffmanDictionaryForMetadata class comment for more details.
2999 
3000   // Call destructors for already-initialized tables.
3001   for (size_t i = 0; i < numInitializedTables; i++) {
3002     tablesBase()[i].~GenericHuffmanTable();
3003   }
3004   for (size_t i = 0; i < numInitializedSingleTables; i++) {
3005     singleTablesBase()[i].~SingleLookupHuffmanTable();
3006   }
3007   for (size_t i = 0; i < numInitializedTwoTables; i++) {
3008     twoTablesBase()[i].~TwoLookupsHuffmanTable();
3009   }
3010 
3011   // Set the following fields to 0 so that destructor doesn't call tables'
3012   // destructor.
3013   numTables_ = 0;
3014   numSingleTables_ = 0;
3015   numTwoTables_ = 0;
3016 }
3017 
moveFrom(HuffmanDictionary * dictionary,TemporaryStorage * tempStorage)3018 void HuffmanDictionaryForMetadata::moveFrom(HuffmanDictionary* dictionary,
3019                                             TemporaryStorage* tempStorage) {
3020   // Move tableIndices_ from HuffmanDictionary to the payload of
3021   // HuffmanDictionaryForMetadata.
3022   for (size_t i = 0; i < TableIdentity::Limit; i++) {
3023     // HuffmanDictionaryForMetadata.tableIndices_ is initialized to
3024     // UnreachableIndex, and we don't have to move if
3025     // dictionary->status_[i] == HuffmanDictionary::TableStatus::Unreachable.
3026     if (dictionary->status_[i] == HuffmanDictionary::TableStatus::Ready) {
3027       tableIndices_[i] = dictionary->tableIndices_[i];
3028     }
3029   }
3030 
3031   // Fill items of each array from the beginning.
3032   auto tablePtr = tablesBase();
3033   auto huffmanEntryPtr = huffmanEntriesBase();
3034   auto internalIndexPtr = internalIndicesBase();
3035   auto singleTablePtr = singleTablesBase();
3036   auto twoTablePtr = twoTablesBase();
3037 
3038   // Move the content of SingleLookupHuffmanTable from
3039   // TemporaryStorage to the payload of HuffmanDictionaryForMetadata.
3040   //
3041   // SingleLookupHuffmanTable itself should already be moved to
3042   // HuffmanDictionaryForMetadata.
3043   auto moveSingleTableContent =
3044       [&huffmanEntryPtr, &internalIndexPtr](SingleLookupHuffmanTable& table) {
3045         // table.{values_,saturated_} points the spans in TemporaryStorage.
3046         // Move those items to the payload and then update
3047         // table.{values_,saturated_} to point that range.
3048 
3049         {
3050           size_t size = table.values_.size();
3051           memmove(huffmanEntryPtr.get(), table.values_.data(),
3052                   sizeof(HuffmanEntry) * size);
3053           table.values_ = mozilla::MakeSpan(huffmanEntryPtr.get(), size);
3054           huffmanEntryPtr += size;
3055         }
3056 
3057         {
3058           size_t size = table.saturated_.size();
3059           memmove(internalIndexPtr.get(), table.saturated_.data(),
3060                   sizeof(InternalIndex) * size);
3061           table.saturated_ = mozilla::MakeSpan(internalIndexPtr.get(), size);
3062           internalIndexPtr += size;
3063         }
3064       };
3065 
3066   // Move the content of TwoLookupsHuffmanTable from
3067   // TemporaryStorage to the payload of HuffmanDictionaryForMetadata.
3068   auto moveTwoTableContent =
3069       [&huffmanEntryPtr, &singleTablePtr,
3070        moveSingleTableContent](TwoLookupsHuffmanTable& table) {
3071         // table.shortKeys_ instance itself is already moved.
3072         // Move the contents to the payload.
3073         moveSingleTableContent(table.shortKeys_);
3074 
3075         // table.{values_,suffixTables_} points the spans in TemporaryStorage.
3076         // Move those items to the payload and then update
3077         // table.{values_,suffixTables_} to point that range.
3078         // Also recursively move the content of suffixTables_.
3079 
3080         {
3081           size_t size = table.values_.size();
3082           memmove(huffmanEntryPtr.get(), table.values_.data(),
3083                   sizeof(HuffmanEntry) * size);
3084           table.values_ = mozilla::MakeSpan(huffmanEntryPtr.get(), size);
3085           huffmanEntryPtr += size;
3086         }
3087 
3088         {
3089           size_t size = table.suffixTables_.size();
3090           auto head = singleTablePtr.get();
3091           for (auto& fromSubTable : table.suffixTables_) {
3092             memmove(singleTablePtr.get(), &fromSubTable,
3093                     sizeof(SingleLookupHuffmanTable));
3094             auto& toSubTable = *singleTablePtr;
3095             singleTablePtr++;
3096 
3097             moveSingleTableContent(toSubTable);
3098           }
3099           table.suffixTables_ = mozilla::MakeSpan(head, size);
3100         }
3101       };
3102 
3103   // Move the content of ThreeLookupsHuffmanTable from
3104   // TemporaryStorage to the payload of HuffmanDictionaryForMetadata.
3105   auto moveThreeTableContent =
3106       [&huffmanEntryPtr, &twoTablePtr, moveSingleTableContent,
3107        moveTwoTableContent](ThreeLookupsHuffmanTable& table) {
3108         // table.shortKeys_ instance itself is already moved.
3109         // Move the contents to the payload.
3110         moveSingleTableContent(table.shortKeys_);
3111 
3112         // table.{values_,suffixTables_} points the spans in TemporaryStorage.
3113         // Move those items to the payload and then update
3114         // table.{values_,suffixTables_} to point that range.
3115         // Also recursively move the content of suffixTables_.
3116 
3117         {
3118           size_t size = table.values_.size();
3119           memmove(huffmanEntryPtr.get(), table.values_.data(),
3120                   sizeof(HuffmanEntry) * size);
3121           table.values_ = mozilla::MakeSpan(huffmanEntryPtr.get(), size);
3122           huffmanEntryPtr += size;
3123         }
3124 
3125         {
3126           size_t size = table.suffixTables_.size();
3127           auto head = twoTablePtr.get();
3128           for (auto& fromSubTable : table.suffixTables_) {
3129             memmove(twoTablePtr.get(), &fromSubTable,
3130                     sizeof(TwoLookupsHuffmanTable));
3131             auto& toSubTable = *twoTablePtr;
3132             twoTablePtr++;
3133 
3134             moveTwoTableContent(toSubTable);
3135           }
3136           table.suffixTables_ = mozilla::MakeSpan(head, size);
3137         }
3138       };
3139 
3140   // Move tables from HuffmanDictionary to the payload of
3141   // HuffmanDictionaryForMetadata, and then move contents of those tables
3142   // to the payload of HuffmanDictionaryForMetadata.
3143   for (size_t i = 0; i < numTables_; i++) {
3144     auto& fromTable = dictionary->tableAtIndex(i);
3145 
3146     if (fromTable.implementation_.is<SingleEntryHuffmanTable>() ||
3147         fromTable.implementation_.is<TwoEntriesHuffmanTable>()) {
3148       memmove(tablePtr.get(), &fromTable, sizeof(GenericHuffmanTable));
3149       tablePtr++;
3150     } else if (fromTable.implementation_.is<SingleLookupHuffmanTable>()) {
3151       memmove(tablePtr.get(), &fromTable, sizeof(GenericHuffmanTable));
3152       auto& specialized =
3153           tablePtr->implementation_.as<SingleLookupHuffmanTable>();
3154       tablePtr++;
3155 
3156       moveSingleTableContent(specialized);
3157     } else if (fromTable.implementation_.is<TwoLookupsHuffmanTable>()) {
3158       memmove(tablePtr.get(), &fromTable, sizeof(GenericHuffmanTable));
3159       auto& specialized =
3160           tablePtr->implementation_.as<TwoLookupsHuffmanTable>();
3161       tablePtr++;
3162 
3163       moveTwoTableContent(specialized);
3164     } else {
3165       MOZ_ASSERT(fromTable.implementation_.is<ThreeLookupsHuffmanTable>());
3166 
3167       memmove(tablePtr.get(), &fromTable, sizeof(GenericHuffmanTable));
3168       auto& specialized =
3169           tablePtr->implementation_.as<ThreeLookupsHuffmanTable>();
3170       tablePtr++;
3171 
3172       moveThreeTableContent(specialized);
3173     }
3174   }
3175 }
3176 
3177 /* static */
totalSize(size_t numTables,size_t numHuffmanEntries,size_t numInternalIndices,size_t numSingleTables,size_t numTwoTables)3178 size_t HuffmanDictionaryForMetadata::totalSize(size_t numTables,
3179                                                size_t numHuffmanEntries,
3180                                                size_t numInternalIndices,
3181                                                size_t numSingleTables,
3182                                                size_t numTwoTables) {
3183   static_assert(alignof(GenericHuffmanTable) % sizeof(uintptr_t) == 0,
3184                 "should be aligned to pointer size");
3185   static_assert(alignof(HuffmanEntry) % sizeof(uintptr_t) == 0,
3186                 "should be aligned to pointer size");
3187   static_assert(alignof(SingleLookupHuffmanTable) % sizeof(uintptr_t) == 0,
3188                 "should be aligned to pointer size");
3189   static_assert(alignof(TwoLookupsHuffmanTable) % sizeof(uintptr_t) == 0,
3190                 "should be aligned to pointer size");
3191 
3192   // InternalIndex is not guaranteed to be aligned to pointer size.
3193   // Make sure `numInternalIndices` meets the requirement that
3194   // the entire block size is aligned to pointer size.
3195   MOZ_ASSERT(numInternalIndices * sizeof(InternalIndex) % sizeof(uintptr_t) ==
3196              0);
3197 
3198   return sizeof(HuffmanDictionaryForMetadata) +
3199          numTables * sizeof(GenericHuffmanTable) +
3200          numHuffmanEntries * sizeof(HuffmanEntry) +
3201          numInternalIndices * sizeof(InternalIndex) +
3202          numSingleTables * sizeof(SingleLookupHuffmanTable) +
3203          numTwoTables * sizeof(TwoLookupsHuffmanTable);
3204 }
3205 
~HuffmanDictionary()3206 HuffmanDictionary::~HuffmanDictionary() {
3207   for (size_t i = 0; i < nextIndex_; i++) {
3208     tableAtIndex(i).~GenericHuffmanTable();
3209   }
3210 }
3211 
leadingBits(const uint8_t aBitLength) const3212 uint32_t HuffmanLookup::leadingBits(const uint8_t aBitLength) const {
3213   MOZ_ASSERT(aBitLength <= bitLength_);
3214   const uint32_t result = (aBitLength == 0)
3215                               ? 0  // Shifting a uint32_t by 32 bits is UB.
3216                               : bits_ >> uint32_t(bitLength_ - aBitLength);
3217   return result;
3218 }
3219 
split(const uint8_t prefixLength) const3220 Split<HuffmanLookup> HuffmanLookup::split(const uint8_t prefixLength) const {
3221   if (bitLength_ <= prefixLength) {
3222     // Not enough bits, pad with zeros.
3223     return {
3224         /* prefix: HuffmanLookup */ {bits_ << (prefixLength - bitLength_),
3225                                      prefixLength},
3226         /* suffix: HuffmanLookup */ {0, 0},
3227     };
3228   }
3229 
3230   // Keep `prefixLength` bits from `bits`.
3231   // Pad the rest with 0s to build the suffix.
3232   const uint8_t shift = bitLength_ - prefixLength;
3233   switch (shift) {
3234     case 0:  // Special case, as we can't >> 32
3235       return {
3236           /* prefix: HuffmanLookup */ {bits_, prefixLength},
3237           /* suffix: HuffmanLookup */ {0, 0},
3238       };
3239     case 32:  // Special case, as we can't >> 32
3240       return {
3241           /* prefix: HuffmanLookup */ {0, prefixLength},
3242           /* suffix: HuffmanLookup */ {bits_, shift},
3243       };
3244   }
3245   return {
3246       /* prefix: HuffmanLookup */ {bits_ >> shift, prefixLength},
3247       /* suffix: HuffmanLookup */
3248       {bits_ & (std::numeric_limits<uint32_t>::max() >> (32 - shift)), shift},
3249   };
3250 }
3251 
suffixes(uint8_t expectedBitLength) const3252 mozilla::detail::IntegerRange<size_t> HuffmanLookup::suffixes(
3253     uint8_t expectedBitLength) const {
3254   if (expectedBitLength <= bitLength_) {
3255     // We have too many bits, we need to truncate the HuffmanLookup,
3256     // then return a single element.
3257     const uint8_t shearing = bitLength_ - expectedBitLength;
3258     const size_t first = size_t(bits_) >> shearing;
3259     const size_t last = first;
3260     return mozilla::IntegerRange<size_t>(first, last + 1);
3261   }
3262 
3263   // We need to pad with lower-weight 0s.
3264   const uint8_t padding = expectedBitLength - bitLength_;
3265   const size_t first = bits_ << padding;
3266   const size_t last = first + (size_t(-1) >> (8 * sizeof(size_t) - padding));
3267   return mozilla::IntegerRange<size_t>(first, last + 1);
3268 }
3269 
3270 }  // namespace js::frontend
3271