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