Bug 1587663 - Add BinASTSymbol and de-templatize HuffmanTable classes. r=Yoric
authorTooru Fujisawa <arai_a@mac.com>
Fri, 11 Oct 2019 14:42:41 +0000
changeset 497276 15ff5c7542067cfa94da1ce0c896141be4afea78
parent 497275 c9ea9966d4cdc4007eb3c4a023ad87fa891eda33
child 497277 ac3d70b6b575df0460216c306f2b3e2d35634b26
push id36681
push usercbrindusan@mozilla.com
push dateFri, 11 Oct 2019 21:50:12 +0000
treeherdermozilla-central@c5e6477c3a24 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1587663
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1587663 - Add BinASTSymbol and de-templatize HuffmanTable classes. r=Yoric Differential Revision: https://phabricator.services.mozilla.com/D48793
js/src/frontend/BinASTTokenReaderContext.cpp
js/src/frontend/BinASTTokenReaderContext.h
--- a/js/src/frontend/BinASTTokenReaderContext.cpp
+++ b/js/src/frontend/BinASTTokenReaderContext.cpp
@@ -180,54 +180,49 @@ class HuffmanPreludeReader {
     using Explicit = Ok;
     explicit EntryExplicit(const NormalizedInterfaceAndField identity)
         : EntryBase(identity) {}
   };
 
   // A string.
   // May be a literal string, identifier name or property key. May not be null.
   struct String : EntryExplicit {
-    using SymbolType = JSAtom*;
     using Table = HuffmanTableIndexedSymbolsLiteralString;
     explicit String(const NormalizedInterfaceAndField identity)
         : EntryExplicit(identity) {}
   };
   using IdentifierName = String;
   using PropertyKey = String;
 
   // An optional string.
   // May be a literal string, identifier name or property key.
   struct MaybeString : EntryExplicit {
-    using SymbolType = JSAtom*;
     using Table = HuffmanTableIndexedSymbolsOptionalLiteralString;
     explicit MaybeString(const NormalizedInterfaceAndField identity)
         : EntryExplicit(identity) {}
   };
   using MaybeIdentifierName = MaybeString;
   using MaybePropertyKey = MaybeString;
 
   // A JavaScript number. May not be null.
   struct Number : EntryExplicit {
-    using SymbolType = double;
     using Table = HuffmanTableExplicitSymbolsF64;
     explicit Number(const NormalizedInterfaceAndField identity)
         : EntryExplicit(identity) {}
   };
 
   // A 32-bit integer. May not be null.
   struct UnsignedLong : EntryExplicit {
-    using SymbolType = uint32_t;
     using Table = HuffmanTableExplicitSymbolsU32;
     explicit UnsignedLong(const NormalizedInterfaceAndField identity)
         : EntryExplicit(identity) {}
   };
 
   // A boolean. May not be null.
   struct Boolean : EntryIndexed {
-    using SymbolType = bool;
     using Table = HuffmanTableIndexedSymbolsBool;
 
     explicit Boolean(const NormalizedInterfaceAndField identity)
         : EntryIndexed(identity) {}
 
     // Comparing booleans.
     //
     // As 0 == False < True == 1, we only compare indices.
@@ -241,18 +236,16 @@ class HuffmanPreludeReader {
   // A field encoding a lazy offset.
   struct Lazy : EntryExplicit {
     explicit Lazy(const NormalizedInterfaceAndField identity)
         : EntryExplicit(identity) {}
   };
 
   // A value of a given interface. May not be null.
   struct Interface : EntryIndexed {
-    using SymbolType = BinASTKind;
-
     // The kind of the interface.
     const BinASTKind kind_;
     Interface(const NormalizedInterfaceAndField identity, BinASTKind kind)
         : EntryIndexed(identity), kind_(kind) {}
 
     // Utility struct, used in macros to call the constructor as
     // `Interface::Maker(kind)(identity)`.
     struct Maker {
@@ -261,17 +254,16 @@ class HuffmanPreludeReader {
       Interface operator()(const NormalizedInterfaceAndField identity) {
         return Interface(identity, kind_);
       }
     };
   };
 
   // An optional value of a given interface.
   struct MaybeInterface : EntryIndexed {
-    using SymbolType = BinASTKind;
     using Table = HuffmanTableIndexedSymbolsMaybeInterface;
     // The kind of the interface.
     const BinASTKind kind_;
 
     // Comparing optional interfaces.
     //
     // As 0 == Null < _ == 1, we only compare indices.
     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
@@ -298,19 +290,16 @@ class HuffmanPreludeReader {
   };
 
   // A FrozenArray. May not be null.
   //
   // In practice, this type is used to represent the length of the list.
   // Once we have read the model for the length of the list, we push a
   // `ListContents` to read the model for the contents of the list.
   struct List : EntryExplicit {
-    // The symbol type for the length of the list.
-    using SymbolType = uint32_t;
-
     // The table for the length of the list.
     using Table = HuffmanTableExplicitSymbolsListLength;
 
     // The type of the list, e.g. list of numbers.
     // All lists with the same type share a model for their length.
     const BinASTList contents_;
 
     List(const NormalizedInterfaceAndField identity, const BinASTList contents)
@@ -330,17 +319,16 @@ class HuffmanPreludeReader {
   // A FrozenArray. May not be null.
   struct ListContents : EntryBase {
     explicit ListContents(const NormalizedInterfaceAndField identity)
         : EntryBase(identity) {}
   };
 
   // A choice between several interfaces. May not be null.
   struct Sum : EntryIndexed {
-    using SymbolType = BinASTKind;
     // The type of table used for this entry.
     using Table = HuffmanTableIndexedSymbolsSum;
 
     // The type of values in the sum.
     const BinASTSum contents_;
 
     // Comparing sum entries alphabetically.
     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
@@ -371,24 +359,22 @@ class HuffmanPreludeReader {
       Sum operator()(const NormalizedInterfaceAndField identity) {
         return Sum(identity, contents_);
       }
     };
   };
 
   // An optional choice between several interfaces.
   struct MaybeSum : EntryIndexed {
-    // The type of symbols for this entry.
-    // We use `BinASTKind::_Null` to represent the null case.
-    using SymbolType = BinASTKind;
     // The type of table used for this entry.
     // We use `BinASTKind::_Null` to represent the null case.
     using Table = HuffmanTableIndexedSymbolsSum;
 
     // The type of values in the sum.
+    // We use `BinASTKind::_Null` to represent the null case.
     const BinASTSum contents_;
 
     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
       return aIndex < bIndex;
     }
 
     MaybeSum(const NormalizedInterfaceAndField identity,
              const BinASTSum contents)
@@ -414,17 +400,16 @@ class HuffmanPreludeReader {
       MaybeSum operator()(const NormalizedInterfaceAndField identity) {
         return MaybeSum(identity, contents_);
       }
     };
   };
 
   // A choice between several strings. May not be null.
   struct StringEnum : EntryIndexed {
-    using SymbolType = BinASTVariant;
     using Table = HuffmanTableIndexedSymbolsStringEnum;
 
     // Comparing string enums alphabetically.
     inline bool lessThan(uint32_t aIndex, uint32_t bIndex) {
       MOZ_ASSERT(aIndex <= maxNumberOfSymbols());
       MOZ_ASSERT(bIndex <= maxNumberOfSymbols());
       const size_t aKey = getBinASTVariantSortKey(variantAt(aIndex));
       const size_t bKey = getBinASTVariantSortKey(variantAt(bIndex));
@@ -514,17 +499,17 @@ class HuffmanPreludeReader {
 
     // Spec:
     // 3. If the field has a FrozenArray type
     //   a. Determine if the array type is always empty
     //   b. If so, stop
     auto& lengthTable = table.as<HuffmanTableExplicitSymbolsListLength>();
     bool empty = true;
     for (auto iter : lengthTable) {
-      if (*iter > 0) {
+      if (iter->toListLength() > 0) {
         empty = false;
         break;
       }
     }
     if (empty) {
       return Ok();
     }
 
@@ -554,17 +539,18 @@ class HuffmanPreludeReader {
   MOZ_MUST_USE JS::Result<Ok> pushValue(NormalizedInterfaceAndField identity,
                                         const Interface& interface) {
     // Note: In this case, for compatibility, we do *not* check whether
     // the interface has already been visited.
     auto& table = dictionary_.tableForField(identity);
     if (table.is<HuffmanTableUnreachable>()) {
       // Effectively, an `Interface` is a sum with a single entry.
       HuffmanTableIndexedSymbolsSum sum(cx_);
-      MOZ_TRY(sum.initWithSingleValue(cx_, BinASTKind(interface.kind_)));
+      MOZ_TRY(sum.initWithSingleValue(
+          cx_, BinASTSymbol::fromKind(BinASTKind(interface.kind_))));
       table = {mozilla::VariantType<HuffmanTableIndexedSymbolsSum>{},
                std::move(sum)};
     }
 
     // Spec:
     // 4. If the effective type is a monomorphic interface, push all of the
     // interface’s fields
     return pushFields(interface.kind_);
@@ -629,18 +615,17 @@ class HuffmanPreludeReader {
 
     return Ok();
   }
 
   // Read a single symbol.
   // For an indexed type, the symbol is fetched from the grammar using `index`.
   // We have a guarantee that `index` is always in [0, numberOfSymbols).
   template <typename Entry>
-  MOZ_MUST_USE JS::Result<typename Entry::SymbolType> readSymbol(const Entry&,
-                                                                 size_t index);
+  MOZ_MUST_USE JS::Result<BinASTSymbol> readSymbol(const Entry&, size_t index);
 
   // Read the number of symbols in an entry.
   // For an indexed type, theis number is fetched from the grammar.
   // We have a guarantee that `index` is always in [0, numberOfSymbols)
   template <typename Entry>
   MOZ_MUST_USE JS::Result<uint32_t> readNumberOfSymbols(const Entry&);
 
   // Read a table in the optimized "only one value" format.
@@ -671,17 +656,17 @@ class HuffmanPreludeReader {
         return raiseInvalidTableData(entry.identity_);
       }
 
       // Read the symbol.
       // If `Entry` is an indexed type, it is fetched directly from the grammar.
       BINJS_MOZ_TRY_DECL(
           symbol, readSymbol<Entry>(entry, /* First and only value */ 0));
 
-      MOZ_TRY(table.initWithSingleValue(cx_, std::move(symbol)));
+      MOZ_TRY(table.initWithSingleValue(cx_, symbol));
       return Ok();
     }
 
     MOZ_TRY(readMultipleValuesTableAndAssignCode<Entry>(table, entry,
                                                         numberOfSymbols));
 
     // Note that the table may be empty, in the case of a list that never has
     // any elements.
@@ -736,17 +721,17 @@ class HuffmanPreludeReader {
         // By format invariant, bit lengths are always non-0
         // and always ranked by increasing order.
         return raiseInvalidTableData(entry.identity_);
       }
 
       // Read and add symbol.
       BINJS_MOZ_TRY_DECL(
           symbol, readSymbol<Entry>(entry, i));  // Symbol is read from disk.
-      MOZ_TRY(table.addSymbol(code, bitLength, std::move(symbol)));
+      MOZ_TRY(table.addSymbol(code, bitLength, symbol));
 
       // Prepare next code.
       code = (code + 1) << (nextBitLength - bitLength);
     }
 
     MOZ_TRY(table.initComplete());
     auxStorageLength_.clear();
     return Ok();
@@ -819,17 +804,17 @@ class HuffmanPreludeReader {
 
       // Read symbol from memory and add it.
       BINJS_MOZ_TRY_DECL(
           symbol,
           readSymbol<Entry>(
               entry,
               auxStorageLength_[i].index_));  // Symbol is read from memory.
 
-      MOZ_TRY(table.addSymbol(code, bitLength, std::move(symbol)));
+      MOZ_TRY(table.addSymbol(code, bitLength, symbol));
 
       // Prepare next code.
       code = (code + 1) << (nextBitLength - bitLength);
     }
 
     MOZ_TRY(table.initComplete());
 
     auxStorageLength_.clear();
@@ -963,17 +948,17 @@ class HuffmanPreludeReader {
       const auto& table = owner.dictionary_.tableForField(entry.identity_);
       if (table.is<HuffmanTableInitializing>()) {
         return Ok();
       }
       const auto& tableRef = table.as<HuffmanTableIndexedSymbolsSum>();
 
       for (auto iter : tableRef) {
         MOZ_TRY(owner.pushValue(entry.identity_,
-                                Interface(entry.identity_, *iter)));
+                                Interface(entry.identity_, iter->toKind())));
       }
       return Ok();
     }
 
     // Sum of interfaces, nullable.
     // Values: `null`, followed by interfaces by the order in
     // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`.
     MOZ_MUST_USE JS::Result<Ok> operator()(const MaybeSum& entry) {
@@ -987,17 +972,17 @@ class HuffmanPreludeReader {
       const auto& table = owner.dictionary_.tableForField(entry.identity_);
       if (table.is<HuffmanTableUnreachable>()) {
         return Ok();
       }
       const auto& tableRef = table.as<HuffmanTableIndexedSymbolsSum>();
 
       for (auto iter : tableRef) {
         MOZ_TRY(owner.pushValue(entry.identity_,
-                                Interface(entry.identity_, *iter)));
+                                Interface(entry.identity_, iter->toKind())));
       }
       return Ok();
     }
 
     MOZ_MUST_USE JS::Result<Ok> operator()(const Number& entry) {
       return owner.readTable<Number>(entry);
     }
 
@@ -1295,39 +1280,36 @@ JS::Result<HuffmanLookup> BinASTTokenRea
     // Now, we may prepare a `HuffmanLookup` with up to 32 bits.
     if (bitLength_ <= MAX_PREFIX_BIT_LENGTH) {
       return HuffmanLookup(bits_, bitLength_);
     }
   }
 
   // Keep only 32 bits. We perform the operation on 64 bits to avoid any
   // arithmetics surprise.
-  const uint64_t bitPrefix =
-      bits_ >> (bitLength_ - MAX_PREFIX_BIT_LENGTH);
+  const uint64_t bitPrefix = bits_ >> (bitLength_ - MAX_PREFIX_BIT_LENGTH);
   MOZ_ASSERT(bitPrefix <= uint32_t(-1));
   return HuffmanLookup(bitPrefix, MAX_PREFIX_BIT_LENGTH);
 }
 
 template <>
 void BinASTTokenReaderContext::BitBuffer::advanceBitBuffer<Compression::No>(
     const uint8_t bitLength) {
   // It should be impossible to call `advanceBitBuffer`
   // with more bits than what we just handed out.
   MOZ_ASSERT(bitLength <= bitLength_);
 
   bitLength_ -= bitLength;
 
   // Now zero out the bits that are beyond `bitLength_`.
-  const uint64_t mask =
-      bitLength_ == 0
-          ? 0  // >> 64 is UB for a uint64_t
-          : uint64_t(-1) >> (BIT_BUFFER_SIZE - bitLength_);
+  const uint64_t mask = bitLength_ == 0
+                            ? 0  // >> 64 is UB for a uint64_t
+                            : uint64_t(-1) >> (BIT_BUFFER_SIZE - bitLength_);
   bits_ &= mask;
-  MOZ_ASSERT_IF(bitLength_ != BIT_BUFFER_SIZE,
-                bits_ >> bitLength_ == 0);
+  MOZ_ASSERT_IF(bitLength_ != BIT_BUFFER_SIZE, bits_ >> bitLength_ == 0);
 }
 
 void BinASTTokenReaderContext::traceMetadata(JSTracer* trc) {
   if (metadata_) {
     metadata_->trace(trc);
   }
 }
 
@@ -1365,76 +1347,83 @@ JS::Result<BinASTKind> BinASTTokenReader
       dictionary_.tableForField(NormalizedInterfaceAndField(identity));
   BINJS_MOZ_TRY_DECL(bits_,
                      (bitBuffer.getHuffmanLookup<Compression::No>(*this)));
 
   if (table.is<HuffmanTableIndexedSymbolsSum>()) {
     const auto& specialized = table.as<HuffmanTableIndexedSymbolsSum>();
 
     // We're entering either a single interface or a sum.
-    const auto lookup = specialized.lookup(bits_);
-    bitBuffer.advanceBitBuffer<Compression::No>(lookup.key_.bitLength_);
-    if (MOZ_UNLIKELY(!lookup.value_)) {
+    const auto result = specialized.lookup(bits_);
+    if (MOZ_UNLIKELY(!result.isFound())) {
       return raiseInvalidValue();
     }
-    return *lookup.value_;
+    bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
+    return result.value().toKind();
   }
 
   MOZ_ASSERT(table.is<HuffmanTableIndexedSymbolsMaybeInterface>());
   const auto& specialized =
       table.as<HuffmanTableIndexedSymbolsMaybeInterface>();
   // We're entering an optional interface.
-  const auto lookup = specialized.lookup(bits_);
-  bitBuffer.advanceBitBuffer<Compression::No>(lookup.key_.bitLength_);
-  if (MOZ_UNLIKELY(!lookup.value_)) {
+  const auto result = specialized.lookup(bits_);
+  if (MOZ_UNLIKELY(!result.isFound())) {
     return raiseInvalidValue();
   }
-  return *lookup.value_;
+  bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
+  return result.value().toKind();
 }
 
 template <typename Table>
-JS::Result<typename Table::Contents>
-BinASTTokenReaderContext::readFieldFromTable(
+JS::Result<BinASTSymbol> BinASTTokenReaderContext::readFieldFromTable(
     const BinASTInterfaceAndField& identity) {
   // Extract the table.
   const auto& table =
       dictionary_.tableForField(NormalizedInterfaceAndField(identity));
   if (MOZ_UNLIKELY(!table.is<Table>())) {
     return raiseNotInPrelude();
   }
   BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
-  const auto lookup = table.as<Table>().lookup(bits_);
-
-  bitBuffer.advanceBitBuffer<Compression::No>(lookup.key_.bitLength_);
-  if (MOZ_UNLIKELY(!lookup.value_)) {
+  const auto result = table.as<Table>().lookup(bits_);
+  if (MOZ_UNLIKELY(!result.isFound())) {
     return raiseInvalidValue();
   }
-  return *lookup.value_;
+  bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
+  return result.value();
 }
 
 JS::Result<bool> BinASTTokenReaderContext::readBool(
     const FieldContext& context) {
-  return readFieldFromTable<HuffmanTableIndexedSymbolsBool>(context.position_);
+  BINJS_MOZ_TRY_DECL(result, readFieldFromTable<HuffmanTableIndexedSymbolsBool>(
+                                 context.position_));
+  return result.toBool();
 }
 
 JS::Result<double> BinASTTokenReaderContext::readDouble(
     const FieldContext& context) {
-  return readFieldFromTable<HuffmanTableExplicitSymbolsF64>(context.position_);
+  BINJS_MOZ_TRY_DECL(result, readFieldFromTable<HuffmanTableExplicitSymbolsF64>(
+                                 context.position_));
+  return result.toDouble();
 }
 
 JS::Result<JSAtom*> BinASTTokenReaderContext::readMaybeAtom(
     const FieldContext& context) {
-  return readFieldFromTable<HuffmanTableIndexedSymbolsOptionalLiteralString>(
-      context.position_);
+  BINJS_MOZ_TRY_DECL(
+      result,
+      readFieldFromTable<HuffmanTableIndexedSymbolsOptionalLiteralString>(
+          context.position_));
+  return result.toAtom();
 }
 
 JS::Result<JSAtom*> BinASTTokenReaderContext::readAtom(
     const FieldContext& context) {
-  return readFieldFromTable<HuffmanTableIndexedSymbolsLiteralString>(
-      context.position_);
+  BINJS_MOZ_TRY_DECL(
+      result, readFieldFromTable<HuffmanTableIndexedSymbolsLiteralString>(
+                  context.position_));
+  return result.toAtom();
 }
 
 JS::Result<JSAtom*> BinASTTokenReaderContext::readMaybeIdentifierName(
     const FieldContext& context) {
   return readMaybeAtom(context);
 }
 
 JS::Result<JSAtom*> BinASTTokenReaderContext::readIdentifierName(
@@ -1452,30 +1441,32 @@ JS::Result<Ok> BinASTTokenReaderContext:
   return raiseError("readChars is not implemented in BinASTTokenReaderContext");
 }
 
 JS::Result<BinASTVariant> BinASTTokenReaderContext::readVariant(
     const ListContext& context) {
   BINJS_MOZ_TRY_DECL(result,
                      readFieldFromTable<HuffmanTableIndexedSymbolsStringEnum>(
                          context.position_));
-  return result;
+  return result.toVariant();
 }
 
 JS::Result<BinASTVariant> BinASTTokenReaderContext::readVariant(
     const FieldContext& context) {
   BINJS_MOZ_TRY_DECL(result,
                      readFieldFromTable<HuffmanTableIndexedSymbolsStringEnum>(
                          context.position_));
-  return result;
+  return result.toVariant();
 }
 
 JS::Result<uint32_t> BinASTTokenReaderContext::readUnsignedLong(
     const FieldContext& context) {
-  return readFieldFromTable<HuffmanTableExplicitSymbolsU32>(context.position_);
+  BINJS_MOZ_TRY_DECL(result, readFieldFromTable<HuffmanTableExplicitSymbolsU32>(
+                                 context.position_));
+  return result.toUnsignedLong();
 }
 
 JS::Result<BinASTTokenReaderBase::SkippableSubTree>
 BinASTTokenReaderContext::readSkippableSubTree(const FieldContext&) {
   return raiseError("Not Yet Implemented");
 }
 
 JS::Result<Ok> BinASTTokenReaderContext::enterSum(
@@ -1534,22 +1525,22 @@ JS::Result<Ok> BinASTTokenReaderContext:
 
 JS::Result<Ok> BinASTTokenReaderContext::enterList(uint32_t& items,
                                                    const ListContext& context) {
   const auto identity = context.content_;
   const auto& table = dictionary_.tableForListLength(identity);
   BINJS_MOZ_TRY_DECL(bits_, bitBuffer.getHuffmanLookup<Compression::No>(*this));
   const auto& tableForLookup =
       table.as<HuffmanTableExplicitSymbolsListLength>();
-  const auto lookup = tableForLookup.lookup(bits_);
-  bitBuffer.advanceBitBuffer<Compression::No>(lookup.key_.bitLength_);
-  if (MOZ_UNLIKELY(!lookup.value_)) {
+  const auto result = tableForLookup.lookup(bits_);
+  if (MOZ_UNLIKELY(!result.isFound())) {
     return raiseInvalidValue();
   }
-  items = *lookup.value_;
+  bitBuffer.advanceBitBuffer<Compression::No>(result.bitLength());
+  items = result.value().toListLength();
   return Ok();
 }
 
 // Internal uint32_t
 // Note that this is different than varnum in multipart.
 //
 // Encoded as variable length number.
 
@@ -1593,416 +1584,403 @@ JS::Result<uint32_t> BinASTTokenReaderCo
 HuffmanKey::HuffmanKey(const uint32_t bits, const uint8_t bitLength)
     : bits_(bits), bitLength_(bitLength) {
   MOZ_ASSERT(bitLength_ <= MAX_PREFIX_BIT_LENGTH);
   MOZ_ASSERT_IF(bitLength_ != 32 /* >> 32 is UB */, bits >> bitLength == 0);
 }
 
 // ---- Implementation of Huffman Tables
 
-template <typename T>
-GenericHuffmanTable<T>::Iterator::Iterator(
-    typename SingleEntryHuffmanTable<T>::Iterator&& iterator)
+GenericHuffmanTable::Iterator::Iterator(
+    typename SingleEntryHuffmanTable::Iterator&& iterator)
     : implementation_(std::move(iterator)) {}
 
-template <typename T>
-GenericHuffmanTable<T>::Iterator::Iterator(
-    typename SingleLookupHuffmanTable<T>::Iterator&& iterator)
+GenericHuffmanTable::Iterator::Iterator(
+    typename SingleLookupHuffmanTable::Iterator&& iterator)
     : implementation_(std::move(iterator)) {}
 
-template <typename T>
-GenericHuffmanTable<T>::Iterator::Iterator(
-    typename TwoLookupsHuffmanTable<T>::Iterator&& iterator)
+GenericHuffmanTable::Iterator::Iterator(
+    typename TwoLookupsHuffmanTable::Iterator&& iterator)
     : implementation_(std::move(iterator)) {}
 
-template <typename T>
-GenericHuffmanTable<T>::Iterator::Iterator(
-    typename ThreeLookupsHuffmanTable<T>::Iterator&& iterator)
+GenericHuffmanTable::Iterator::Iterator(
+    typename ThreeLookupsHuffmanTable::Iterator&& iterator)
     : implementation_(std::move(iterator)) {}
 
-template <typename T>
-void GenericHuffmanTable<T>::Iterator::operator++() {
+void GenericHuffmanTable::Iterator::operator++() {
   implementation_.match(
-      [](typename SingleEntryHuffmanTable<T>::Iterator& iterator) {
+      [](typename SingleEntryHuffmanTable::Iterator& iterator) {
         iterator.operator++();
       },
-      [](typename SingleLookupHuffmanTable<T>::Iterator& iterator) {
+      [](typename SingleLookupHuffmanTable::Iterator& iterator) {
         iterator.operator++();
       },
-      [](typename TwoLookupsHuffmanTable<T>::Iterator& iterator) {
+      [](typename TwoLookupsHuffmanTable::Iterator& iterator) {
         iterator.operator++();
       },
-      [](typename ThreeLookupsHuffmanTable<T>::Iterator& iterator) {
+      [](typename ThreeLookupsHuffmanTable::Iterator& iterator) {
         iterator.operator++();
       });
 }
 
-template <typename T>
-bool GenericHuffmanTable<T>::Iterator::operator==(
-    const GenericHuffmanTable<T>::Iterator& other) const {
+bool GenericHuffmanTable::Iterator::operator==(
+    const GenericHuffmanTable::Iterator& other) const {
   return implementation_.match(
-      [other](const typename SingleEntryHuffmanTable<T>::Iterator& iterator) {
-        return iterator == other.implementation_.template as<
-                               typename SingleEntryHuffmanTable<T>::Iterator>();
+      [other](const typename SingleEntryHuffmanTable::Iterator& iterator) {
+        return iterator ==
+               other.implementation_
+                   .template as<typename SingleEntryHuffmanTable::Iterator>();
       },
-      [other](const typename SingleLookupHuffmanTable<T>::Iterator& iterator) {
-        return iterator ==
-               other.implementation_.template as<
-                   typename SingleLookupHuffmanTable<T>::Iterator>();
-      },
-      [other](const typename TwoLookupsHuffmanTable<T>::Iterator& iterator) {
+      [other](const typename SingleLookupHuffmanTable::Iterator& iterator) {
         return iterator ==
                other.implementation_
-                   .template as<typename TwoLookupsHuffmanTable<T>::Iterator>();
+                   .template as<typename SingleLookupHuffmanTable::Iterator>();
       },
-      [other](const typename ThreeLookupsHuffmanTable<T>::Iterator& iterator) {
+      [other](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
         return iterator ==
-               other.implementation_.template as<
-                   typename ThreeLookupsHuffmanTable<T>::Iterator>();
+               other.implementation_
+                   .template as<typename TwoLookupsHuffmanTable::Iterator>();
+      },
+      [other](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
+        return iterator ==
+               other.implementation_
+                   .template as<typename ThreeLookupsHuffmanTable::Iterator>();
       });
 }
 
-template <typename T>
-bool GenericHuffmanTable<T>::Iterator::operator!=(
-    const GenericHuffmanTable<T>::Iterator& other) const {
+bool GenericHuffmanTable::Iterator::operator!=(
+    const GenericHuffmanTable::Iterator& other) const {
   return implementation_.match(
-      [other](const typename SingleEntryHuffmanTable<T>::Iterator& iterator) {
-        return iterator != other.implementation_.template as<
-                               typename SingleEntryHuffmanTable<T>::Iterator>();
+      [other](const typename SingleEntryHuffmanTable::Iterator& iterator) {
+        return iterator !=
+               other.implementation_
+                   .template as<typename SingleEntryHuffmanTable::Iterator>();
       },
-      [other](const typename SingleLookupHuffmanTable<T>::Iterator& iterator) {
+      [other](const typename SingleLookupHuffmanTable::Iterator& iterator) {
         return iterator !=
-               other.implementation_.template as<
-                   typename SingleLookupHuffmanTable<T>::Iterator>();
+               other.implementation_
+                   .template as<typename SingleLookupHuffmanTable::Iterator>();
       },
-      [other](const typename TwoLookupsHuffmanTable<T>::Iterator& iterator) {
+      [other](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
         return iterator !=
                other.implementation_
-                   .template as<typename TwoLookupsHuffmanTable<T>::Iterator>();
+                   .template as<typename TwoLookupsHuffmanTable::Iterator>();
       },
-      [other](const typename ThreeLookupsHuffmanTable<T>::Iterator& iterator) {
+      [other](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
         return iterator !=
-               other.implementation_.template as<
-                   typename ThreeLookupsHuffmanTable<T>::Iterator>();
+               other.implementation_
+                   .template as<typename ThreeLookupsHuffmanTable::Iterator>();
       });
 }
 
-template <typename T>
-const T* GenericHuffmanTable<T>::Iterator::operator*() const {
+const BinASTSymbol* GenericHuffmanTable::Iterator::operator*() const {
   return implementation_.match(
-      [](const typename SingleEntryHuffmanTable<T>::Iterator& iterator) {
+      [](const typename SingleEntryHuffmanTable::Iterator& iterator) {
         return iterator.operator*();
       },
-      [](const typename SingleLookupHuffmanTable<T>::Iterator& iterator) {
+      [](const typename SingleLookupHuffmanTable::Iterator& iterator) {
         return iterator.operator*();
       },
-      [](const typename TwoLookupsHuffmanTable<T>::Iterator& iterator) {
+      [](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
         return iterator.operator*();
       },
-      [](const typename ThreeLookupsHuffmanTable<T>::Iterator& iterator) {
+      [](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
         return iterator.operator*();
       });
 }
 
-template <typename T>
-GenericHuffmanTable<T>::GenericHuffmanTable(JSContext*)
+const BinASTSymbol* GenericHuffmanTable::Iterator::operator->() const {
+  return implementation_.match(
+      [](const typename SingleEntryHuffmanTable::Iterator& iterator) {
+        return iterator.operator->();
+      },
+      [](const typename SingleLookupHuffmanTable::Iterator& iterator) {
+        return iterator.operator->();
+      },
+      [](const typename TwoLookupsHuffmanTable::Iterator& iterator) {
+        return iterator.operator->();
+      },
+      [](const typename ThreeLookupsHuffmanTable::Iterator& iterator) {
+        return iterator.operator->();
+      });
+}
+
+GenericHuffmanTable::GenericHuffmanTable(JSContext*)
     : implementation_(HuffmanTableUnreachable{}) {}
 
-template <typename T>
-JS::Result<Ok> GenericHuffmanTable<T>::initComplete() {
+JS::Result<Ok> GenericHuffmanTable::initComplete() {
   return implementation_.match(
-      [](SingleEntryHuffmanTable<T>& implementation) -> JS::Result<Ok> {
+      [](SingleEntryHuffmanTable& implementation) -> JS::Result<Ok> {
         MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
       },
-      [](SingleLookupHuffmanTable<T>& implementation) -> JS::Result<Ok> {
+      [](SingleLookupHuffmanTable& implementation) -> JS::Result<Ok> {
         return implementation.initComplete();
       },
-      [](TwoLookupsHuffmanTable<T>& implementation) -> JS::Result<Ok> {
+      [](TwoLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
         return implementation.initComplete();
       },
-      [](ThreeLookupsHuffmanTable<T>& implementation) -> JS::Result<Ok> {
+      [](ThreeLookupsHuffmanTable& implementation) -> JS::Result<Ok> {
         return implementation.initComplete();
       },
       [](HuffmanTableUnreachable&) -> JS::Result<Ok> {
         MOZ_CRASH("GenericHuffmanTable is unitialized!");
       });
 }
 
-template <typename T>
-typename GenericHuffmanTable<T>::Iterator GenericHuffmanTable<T>::begin()
-    const {
+typename GenericHuffmanTable::Iterator GenericHuffmanTable::begin() const {
   return implementation_.match(
-      [](const SingleEntryHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const SingleEntryHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
+        return Iterator(implementation.begin());
+      },
+      [](const SingleLookupHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.begin());
       },
-      [](const SingleLookupHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const TwoLookupsHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.begin());
       },
-      [](const TwoLookupsHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const ThreeLookupsHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.begin());
       },
-      [](const ThreeLookupsHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
-        return Iterator(implementation.begin());
-      },
-      [](const HuffmanTableUnreachable&) -> GenericHuffmanTable<T>::Iterator {
+      [](const HuffmanTableUnreachable&) -> GenericHuffmanTable::Iterator {
         MOZ_CRASH("GenericHuffmanTable is unitialized!");
       });
 }
 
-template <typename T>
-typename GenericHuffmanTable<T>::Iterator GenericHuffmanTable<T>::end() const {
+typename GenericHuffmanTable::Iterator GenericHuffmanTable::end() const {
   return implementation_.match(
-      [](const SingleEntryHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const SingleEntryHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
+        return Iterator(implementation.end());
+      },
+      [](const SingleLookupHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.end());
       },
-      [](const SingleLookupHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const TwoLookupsHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.end());
       },
-      [](const TwoLookupsHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
+      [](const ThreeLookupsHuffmanTable& implementation)
+          -> GenericHuffmanTable::Iterator {
         return Iterator(implementation.end());
       },
-      [](const ThreeLookupsHuffmanTable<T>& implementation)
-          -> GenericHuffmanTable<T>::Iterator {
-        return Iterator(implementation.end());
-      },
-      [](const HuffmanTableUnreachable&) -> GenericHuffmanTable<T>::Iterator {
+      [](const HuffmanTableUnreachable&) -> GenericHuffmanTable::Iterator {
         MOZ_CRASH("GenericHuffmanTable is unitialized!");
       });
 }
 
-template <typename T>
-JS::Result<Ok> GenericHuffmanTable<T>::initWithSingleValue(JSContext* cx,
-                                                           T&& value) {
+JS::Result<Ok> GenericHuffmanTable::initWithSingleValue(
+    JSContext* cx, const BinASTSymbol& value) {
   // Only one value: use HuffmanImplementationSaturated
   MOZ_ASSERT(implementation_.template is<
              HuffmanTableUnreachable>());  // Make sure that we're initializing.
 
-  implementation_ = {mozilla::VariantType<SingleEntryHuffmanTable<T>>{},
-                     std::move(value)};
+  implementation_ = {mozilla::VariantType<SingleEntryHuffmanTable>{}, value};
   return Ok();
 }
 
-template <typename T>
-JS::Result<Ok> GenericHuffmanTable<T>::initStart(JSContext* cx,
-                                                 size_t numberOfSymbols,
-                                                 uint8_t largestBitLength) {
+JS::Result<Ok> GenericHuffmanTable::initStart(JSContext* cx,
+                                              size_t numberOfSymbols,
+                                              uint8_t largestBitLength) {
   // Make sure that we have a way to represent all legal bit lengths.
-  static_assert(
-      MAX_CODE_BIT_LENGTH <= ThreeLookupsHuffmanTable<T>::MAX_BIT_LENGTH,
-      "ThreeLookupsHuffmanTable cannot hold all bit lengths");
+  static_assert(MAX_CODE_BIT_LENGTH <= ThreeLookupsHuffmanTable::MAX_BIT_LENGTH,
+                "ThreeLookupsHuffmanTable cannot hold all bit lengths");
   // Make sure that we're initializing.
   MOZ_ASSERT(implementation_.template is<HuffmanTableUnreachable>());
 
   // Find the (hopefully) fastest implementation of HuffmanTable for
   // `largestBitLength`.
   // ...hopefully, only one lookup.
-  if (largestBitLength <= SingleLookupHuffmanTable<T>::MAX_BIT_LENGTH) {
-    implementation_ = {mozilla::VariantType<SingleLookupHuffmanTable<T>>{}, cx};
-    return implementation_.template as<SingleLookupHuffmanTable<T>>().initStart(
+  if (largestBitLength <= SingleLookupHuffmanTable::MAX_BIT_LENGTH) {
+    implementation_ = {mozilla::VariantType<SingleLookupHuffmanTable>{}, cx};
+    return implementation_.template as<SingleLookupHuffmanTable>().initStart(
         cx, numberOfSymbols, largestBitLength);
   }
 
   // ...if a single-lookup table would be too large, let's see if
   // we can fit in a two-lookup table.
-  if (largestBitLength <= TwoLookupsHuffmanTable<T>::MAX_BIT_LENGTH) {
-    implementation_ = {mozilla::VariantType<TwoLookupsHuffmanTable<T>>{}, cx};
-    return implementation_.template as<TwoLookupsHuffmanTable<T>>().initStart(
+  if (largestBitLength <= TwoLookupsHuffmanTable::MAX_BIT_LENGTH) {
+    implementation_ = {mozilla::VariantType<TwoLookupsHuffmanTable>{}, cx};
+    return implementation_.template as<TwoLookupsHuffmanTable>().initStart(
         cx, numberOfSymbols, largestBitLength);
   }
 
   // ...otherwise, we'll need three lookups.
-  implementation_ = {mozilla::VariantType<ThreeLookupsHuffmanTable<T>>{}, cx};
-  return implementation_.template as<ThreeLookupsHuffmanTable<T>>().initStart(
+  implementation_ = {mozilla::VariantType<ThreeLookupsHuffmanTable>{}, cx};
+  return implementation_.template as<ThreeLookupsHuffmanTable>().initStart(
       cx, numberOfSymbols, largestBitLength);
 }
 
-template <typename T>
-JS::Result<Ok> GenericHuffmanTable<T>::addSymbol(uint32_t bits,
-                                                 uint8_t bitLength, T&& value) {
+JS::Result<Ok> GenericHuffmanTable::addSymbol(uint32_t bits, uint8_t bitLength,
+                                              const BinASTSymbol& value) {
   return implementation_.match(
-      [](SingleEntryHuffmanTable<T>&) -> JS::Result<Ok> {
+      [](SingleEntryHuffmanTable&) -> JS::Result<Ok> {
         MOZ_CRASH("SingleEntryHuffmanTable shouldn't have multiple entries!");
         return Ok();
       },
-      [bits, bitLength, value = std::move(value)](
-          SingleLookupHuffmanTable<T>&
-              implementation) mutable /* discard implicit const */
+      [bits, bitLength,
+       value](SingleLookupHuffmanTable&
+                  implementation) mutable /* discard implicit const */
       -> JS::Result<Ok> {
-        return implementation.addSymbol(bits, bitLength, std::move(value));
+        return implementation.addSymbol(bits, bitLength, value);
       },
-      [bits, bitLength, value = std::move(value)](
-          TwoLookupsHuffmanTable<T>&
-              implementation) mutable /* discard implicit const */
+      [bits, bitLength,
+       value](TwoLookupsHuffmanTable&
+                  implementation) mutable /* discard implicit const */
       -> JS::Result<Ok> {
-        return implementation.addSymbol(bits, bitLength, std::move(value));
+        return implementation.addSymbol(bits, bitLength, value);
       },
-      [bits, bitLength, value = std::move(value)](
-          ThreeLookupsHuffmanTable<T>&
-              implementation) mutable /* discard implicit const */
+      [bits, bitLength,
+       value = value](ThreeLookupsHuffmanTable&
+                          implementation) mutable /* discard implicit const */
       -> JS::Result<Ok> {
-        return implementation.addSymbol(bits, bitLength, std::move(value));
+        return implementation.addSymbol(bits, bitLength, value);
       },
       [](HuffmanTableUnreachable&) -> JS::Result<Ok> {
         MOZ_CRASH("GenericHuffmanTable is unitialized!");
         return Ok();
       });
 }
 
-template <typename T>
-HuffmanEntry<const T*> GenericHuffmanTable<T>::lookup(
-    HuffmanLookup lookup) const {
+HuffmanLookupResult GenericHuffmanTable::lookup(HuffmanLookup key) const {
   return implementation_.match(
-      [lookup](const SingleEntryHuffmanTable<T>& implementation)
-          -> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
-      [lookup](const SingleLookupHuffmanTable<T>& implementation)
-          -> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
-      [lookup](const TwoLookupsHuffmanTable<T>& implementation)
-          -> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
-      [lookup](const ThreeLookupsHuffmanTable<T>& implementation)
-          -> HuffmanEntry<const T*> { return implementation.lookup(lookup); },
-      [](const HuffmanTableUnreachable&) -> HuffmanEntry<const T*> {
+      [key](const SingleEntryHuffmanTable& implementation)
+          -> HuffmanLookupResult { return implementation.lookup(key); },
+      [key](const SingleLookupHuffmanTable& implementation)
+          -> HuffmanLookupResult { return implementation.lookup(key); },
+      [key](const TwoLookupsHuffmanTable& implementation)
+          -> HuffmanLookupResult { return implementation.lookup(key); },
+      [key](const ThreeLookupsHuffmanTable& implementation)
+          -> HuffmanLookupResult { return implementation.lookup(key); },
+      [](const HuffmanTableUnreachable&) -> HuffmanLookupResult {
         MOZ_CRASH("GenericHuffmanTable is unitialized!");
       });
 }
 
-template <typename T, int N>
-JS::Result<Ok> NaiveHuffmanTable<T, N>::initWithSingleValue(JSContext* cx,
-                                                            T&& value) {
+template <int N>
+JS::Result<Ok> NaiveHuffmanTable<N>::initWithSingleValue(
+    JSContext* cx, const BinASTSymbol& value) {
   MOZ_ASSERT(values_.empty());  // Make sure that we're initializing.
-  if (MOZ_UNLIKELY(!values_.append(HuffmanEntry<T>(0, 0, std::move(value))))) {
+  if (MOZ_UNLIKELY(!values_.append(HuffmanEntry(0, 0, value)))) {
     return cx->alreadyReportedError();
   }
   return Ok();
 }
 
-template <typename T, int N>
-JS::Result<Ok> NaiveHuffmanTable<T, N>::initStart(JSContext* cx,
-                                                  size_t numberOfSymbols,
-                                                  uint8_t) {
+template <int N>
+JS::Result<Ok> NaiveHuffmanTable<N>::initStart(JSContext* cx,
+                                               size_t numberOfSymbols,
+                                               uint8_t) {
   MOZ_ASSERT(values_.empty());  // Make sure that we're initializing.
   if (MOZ_UNLIKELY(!values_.initCapacity(numberOfSymbols))) {
     return cx->alreadyReportedError();
   }
   return Ok();
 }
 
-template <typename T, int N>
-JS::Result<Ok> NaiveHuffmanTable<T, N>::initComplete() {
+template <int N>
+JS::Result<Ok> NaiveHuffmanTable<N>::initComplete() {
   MOZ_ASSERT(values_.length() <= N);
   return Ok();
 }
 
-template <typename T, int N>
-JS::Result<Ok> NaiveHuffmanTable<T, N>::addSymbol(uint32_t bits,
-                                                  uint8_t bitLength,
-                                                  T&& value) {
+template <int N>
+JS::Result<Ok> NaiveHuffmanTable<N>::addSymbol(uint32_t bits, uint8_t bitLength,
+                                               const BinASTSymbol& value) {
   MOZ_ASSERT(bitLength != 0,
              "Adding a symbol with a bitLength of 0 doesn't make sense.");
-  MOZ_ASSERT(values_.empty() || values_.back().key_.bitLength_ <= bitLength,
+  MOZ_ASSERT(values_.empty() || values_.back().key().bitLength_ <= bitLength,
              "Symbols must be ranked by increasing bits length");
   MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
   // Memory was reserved in `init()`.
-  MOZ_ALWAYS_TRUE(values_.emplaceBack(bits, bitLength, std::move(value)));
+  MOZ_ALWAYS_TRUE(values_.emplaceBack(bits, bitLength, value));
 
   return Ok();
 }
 
-template <typename T, int N>
-HuffmanEntry<const T*> NaiveHuffmanTable<T, N>::lookup(
-    HuffmanLookup key) const {
+template <int N>
+HuffmanLookupResult NaiveHuffmanTable<N>::lookup(HuffmanLookup key) const {
   // This current implementation is O(length) and designed mostly for testing.
   // Future versions will presumably adapt the underlying data structure to
   // provide bounded-time lookup.
   for (const auto& iter : values_) {
-    if (iter.key_.bitLength_ > key.bitLength_) {
+    if (iter.key().bitLength_ > key.bitLength_) {
       // We can't find the entry.
       break;
     }
 
-    const uint32_t keyBits = key.leadingBits(iter.key_.bitLength_);
-    if (keyBits == iter.key_.bits_) {
-      // Entry found.
-      return HuffmanEntry<const T*>(iter.key_.bits_, iter.key_.bitLength_,
-                                    &iter.value_);
+    const uint32_t keyBits = key.leadingBits(iter.key().bitLength_);
+    if (keyBits == iter.key().bits_) {
+      return HuffmanLookupResult::found(iter.key().bitLength_, &iter.value());
     }
   }
 
-  // Error: no entry found.
-  return HuffmanEntry<const T*>(0, 0, nullptr);
+  return HuffmanLookupResult::notFound();
 }
 
-template <typename T>
-SingleEntryHuffmanTable<T>::Iterator::Iterator(const T* position)
+SingleEntryHuffmanTable::Iterator::Iterator(const BinASTSymbol* position)
     : position_(position) {}
 
-template <typename T>
-void SingleEntryHuffmanTable<T>::Iterator::operator++() {
+void SingleEntryHuffmanTable::Iterator::operator++() {
   // There's only one entry, and `nullptr` means `end`.
   position_ = nullptr;
 }
 
-template <typename T>
-const T* SingleEntryHuffmanTable<T>::Iterator::operator*() const {
+const BinASTSymbol* SingleEntryHuffmanTable::Iterator::operator*() const {
   return position_;
 }
 
-template <typename T>
-bool SingleEntryHuffmanTable<T>::Iterator::operator==(
+const BinASTSymbol* SingleEntryHuffmanTable::Iterator::operator->() const {
+  return position_;
+}
+
+bool SingleEntryHuffmanTable::Iterator::operator==(
     const Iterator& other) const {
   return position_ == other.position_;
 }
 
-template <typename T>
-bool SingleEntryHuffmanTable<T>::Iterator::operator!=(
+bool SingleEntryHuffmanTable::Iterator::operator!=(
     const Iterator& other) const {
   return position_ != other.position_;
 }
 
-template <typename T>
-HuffmanEntry<const T*> SingleEntryHuffmanTable<T>::lookup(
-    HuffmanLookup key) const {
-  return HuffmanEntry<const T*>(0, 0, &value_);
+HuffmanLookupResult SingleEntryHuffmanTable::lookup(HuffmanLookup key) const {
+  return HuffmanLookupResult::found(0, &value_);
 }
 
-template <typename T>
-SingleLookupHuffmanTable<T>::Iterator::Iterator(const HuffmanEntry<T>* position)
+SingleLookupHuffmanTable::Iterator::Iterator(const HuffmanEntry* position)
     : position_(position) {}
 
-template <typename T>
-void SingleLookupHuffmanTable<T>::Iterator::operator++() {
-  position_++;
+void SingleLookupHuffmanTable::Iterator::operator++() { position_++; }
+
+const BinASTSymbol* SingleLookupHuffmanTable::Iterator::operator*() const {
+  return &position_->value();
 }
 
-template <typename T>
-const T* SingleLookupHuffmanTable<T>::Iterator::operator*() const {
-  return &position_->value_;
+const BinASTSymbol* SingleLookupHuffmanTable::Iterator::operator->() const {
+  return &position_->value();
 }
 
-template <typename T>
-bool SingleLookupHuffmanTable<T>::Iterator::operator==(
+bool SingleLookupHuffmanTable::Iterator::operator==(
     const Iterator& other) const {
   return position_ == other.position_;
 }
 
-template <typename T>
-bool SingleLookupHuffmanTable<T>::Iterator::operator!=(
+bool SingleLookupHuffmanTable::Iterator::operator!=(
     const Iterator& other) const {
   return position_ != other.position_;
 }
 
-template <typename T>
-JS::Result<Ok> SingleLookupHuffmanTable<T>::initStart(
-    JSContext* cx, size_t numberOfSymbols, uint8_t largestBitLength) {
+JS::Result<Ok> SingleLookupHuffmanTable::initStart(JSContext* cx,
+                                                   size_t numberOfSymbols,
+                                                   uint8_t largestBitLength) {
   MOZ_ASSERT_IF(largestBitLength != 32,
                 (uint32_t(1) << largestBitLength) - 1 <=
                     mozilla::MaxValue<InternalIndex>::value);
   MOZ_ASSERT(values_.empty());  // Make sure that we're initializing.
 
   largestBitLength_ = largestBitLength;
 
   if (MOZ_UNLIKELY(!values_.initCapacity(numberOfSymbols))) {
@@ -2015,55 +1993,53 @@ JS::Result<Ok> SingleLookupHuffmanTable<
   // Enlarge `saturated_`, as we're going to fill it in random order.
   for (size_t i = 0; i < saturatedLength; ++i) {
     // Capacity reserved in this method.
     saturated_.infallibleAppend(InternalIndex(-1));
   }
   return Ok();
 }
 
-template <typename T>
-JS::Result<Ok> SingleLookupHuffmanTable<T>::initComplete() {
+JS::Result<Ok> SingleLookupHuffmanTable::initComplete() {
   // Double-check that we've initialized properly.
   MOZ_ASSERT(largestBitLength_ <= MAX_CODE_BIT_LENGTH);
 
   // We can end up with empty tables, if this `SingleLookupHuffmanTable`
   // is used to store suffixes in a `MultiLookupHuffmanTable` and
   // the corresponding prefix is never used.
   if (values_.length() == 0) {
     MOZ_ASSERT(largestBitLength_ == 0);
     return Ok();
   }
 #ifdef DEBUG
   bool foundMaxBitLength = false;
   for (size_t i = 0; i < saturated_.length(); ++i) {
     const uint8_t index = saturated_[i];
-    MOZ_ASSERT(values_[index].key_.bitLength_ <= largestBitLength_);
-    if (values_[index].key_.bitLength_ == largestBitLength_) {
+    MOZ_ASSERT(values_[index].key().bitLength_ <= largestBitLength_);
+    if (values_[index].key().bitLength_ == largestBitLength_) {
       foundMaxBitLength = true;
     }
   }
   MOZ_ASSERT(foundMaxBitLength);
 #endif  // DEBUG
   return Ok();
 }
 
-template <typename T>
-JS::Result<Ok> SingleLookupHuffmanTable<T>::addSymbol(uint32_t bits,
-                                                      uint8_t bitLength,
-                                                      T&& value) {
+JS::Result<Ok> SingleLookupHuffmanTable::addSymbol(uint32_t bits,
+                                                   uint8_t bitLength,
+                                                   const BinASTSymbol& value) {
   MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
   MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
   MOZ_ASSERT(bitLength <= largestBitLength_);
 
   const size_t index = values_.length();
 
   // First add the value to `values_`.
   // Memory was reserved in `init()`.
-  values_.infallibleEmplaceBack(bits, bitLength, std::move(value));
+  values_.infallibleEmplaceBack(bits, bitLength, value);
 
   // Notation: in the following, unless otherwise specified, we consider
   // values with `largestBitLength_` bits exactly.
   //
   // When we perform lookup, we will extract `largestBitLength_` bits from the
   // key into a value `0bB...B`. We have a match for `value` if and only if
   // `0bB...B` may be decomposed into `0bC...CX...X` such that
   //    - `0bC...C` is `bitLength` bits long;
@@ -2075,114 +2051,117 @@ JS::Result<Ok> SingleLookupHuffmanTable<
   const HuffmanLookup base(bits, bitLength);
   for (size_t i : base.suffixes(largestBitLength_)) {
     saturated_[i] = index;
   }
 
   return Ok();
 }
 
-template <typename T>
-HuffmanEntry<const T*> SingleLookupHuffmanTable<T>::lookup(
-    HuffmanLookup key) const {
+HuffmanLookupResult SingleLookupHuffmanTable::lookup(HuffmanLookup key) const {
   if (values_.length() == 0) {
     // If the table is empty, any lookup fails.
-    return HuffmanEntry<const T*>(0, 0, nullptr);
+    return HuffmanLookupResult::notFound();
   }
   // ...otherwise, all lookups succeed.
 
   // Take the `largestBitLength_` highest weight bits of `key`.
   // In the documentation of `addSymbol`, this is
   // `0bB...B`.
   const uint32_t bits = key.leadingBits(largestBitLength_);
 
   // Invariants: `saturated_.length() == 1 << largestBitLength_`
   // and `bits <= 1 << largestBitLength_`.
   const size_t index = saturated_[bits];
 
   // Invariants: `saturated_[i] < values_.length()`.
   const auto& entry = values_[index];
-  return HuffmanEntry<const T*>(entry.key_.bits_, entry.key_.bitLength_,
-                                &entry.value_);
+  return HuffmanLookupResult::found(entry.key().bitLength_, &entry.value());
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::Iterator::Iterator(
-    const HuffmanEntry<T>* position)
+template <typename Subtable, uint8_t PrefixBitLength>
+MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::Iterator(
+    const HuffmanEntry* position)
     : position_(position) {}
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-void MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::Iterator::
-operator++() {
+template <typename Subtable, uint8_t PrefixBitLength>
+void MultiLookupHuffmanTable<Subtable,
+                             PrefixBitLength>::Iterator::operator++() {
   position_++;
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-const T* MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::Iterator::
-operator*() const {
-  return &position_->value_;
+template <typename Subtable, uint8_t PrefixBitLength>
+const BinASTSymbol* MultiLookupHuffmanTable<
+    Subtable, PrefixBitLength>::Iterator::operator*() const {
+  return &position_->value();
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-bool MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::Iterator::
-operator==(const Iterator& other) const {
+template <typename Subtable, uint8_t PrefixBitLength>
+const BinASTSymbol* MultiLookupHuffmanTable<
+    Subtable, PrefixBitLength>::Iterator::operator->() const {
+  return &position_->value();
+}
+
+template <typename Subtable, uint8_t PrefixBitLength>
+bool MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::operator==(
+    const Iterator& other) const {
   return position_ == other.position_;
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-bool MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::Iterator::
-operator!=(const Iterator& other) const {
+template <typename Subtable, uint8_t PrefixBitLength>
+bool MultiLookupHuffmanTable<Subtable, PrefixBitLength>::Iterator::operator!=(
+    const Iterator& other) const {
   return position_ != other.position_;
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-JS::Result<Ok> MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::initStart(
+template <typename Subtable, uint8_t PrefixBitLength>
+JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initStart(
     JSContext* cx, size_t numberOfSymbols, uint8_t largestBitLength) {
   static_assert(PrefixBitLength < MAX_CODE_BIT_LENGTH,
                 "Invalid PrefixBitLength");
   MOZ_ASSERT(values_.empty());  // Make sure that we're initializing.
   MOZ_ASSERT(subTables_.empty());
   largestBitLength_ = largestBitLength;
   if (MOZ_UNLIKELY(!values_.initCapacity(numberOfSymbols))) {
     return cx->alreadyReportedError();
   }
   if (MOZ_UNLIKELY(!subTables_.initCapacity(1 << PrefixBitLength))) {
     return cx->alreadyReportedError();
   }
   return Ok();
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-JS::Result<Ok> MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::addSymbol(
-    uint32_t bits, uint8_t bitLength, T&& value) {
+template <typename Subtable, uint8_t PrefixBitLength>
+JS::Result<Ok> MultiLookupHuffmanTable<Subtable, PrefixBitLength>::addSymbol(
+    uint32_t bits, uint8_t bitLength, const BinASTSymbol& value) {
   MOZ_ASSERT_IF(largestBitLength_ != 0, bitLength != 0);
-  MOZ_ASSERT(values_.empty() || values_.back().key_.bitLength_ <= bitLength,
+  MOZ_ASSERT(values_.empty() || values_.back().key().bitLength_ <= bitLength,
              "Symbols must be ranked by increasing bits length");
   MOZ_ASSERT_IF(bitLength != 32 /* >> 32 is UB */, bits >> bitLength == 0);
 
-  values_.infallibleEmplaceBack(bits, bitLength, std::move(value));
+  values_.infallibleEmplaceBack(bits, bitLength, value);
 
   return Ok();
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
+template <typename Subtable, uint8_t PrefixBitLength>
 JS::Result<Ok>
-MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::initComplete() {
+MultiLookupHuffmanTable<Subtable, PrefixBitLength>::initComplete() {
   // First, we need to collect the `largestBitLength_`
   // and `numberofSymbols` for each subtable.
   struct Bucket {
     Bucket() : largestBitLength_(0), numberOfSymbols_(0){};
     uint8_t largestBitLength_;
     uint32_t numberOfSymbols_;
   };
   Vector<Bucket> buckets{cx_};
   BINJS_TRY(buckets.resize(1 << PrefixBitLength));
 
   for (const auto& entry : values_) {
-    const HuffmanLookup lookup(entry.key_.bits_, entry.key_.bitLength_);
+    const HuffmanLookup lookup(entry.key().bits_, entry.key().bitLength_);
     const auto split = lookup.split(PrefixBitLength);
     MOZ_ASSERT_IF(split.suffix_.bitLength_ != 32,
                   split.suffix_.bits_ >> split.suffix_.bitLength_ == 0);
 
     // Entries that have a sufficient number of bits will be dispatched
     // to a single subtable (e.g. A, B, C, D, E, F in the documentation).
     // Other entries need to be dispatched to several subtables
     // (e.g. G, H in the documentation).
@@ -2205,60 +2184,56 @@ MultiLookupHuffmanTable<T, Subtable, Pre
   }
 
   // Now that the subtables are created, let's dispatch the values
   // among these tables.
   for (size_t i = 0; i < values_.length(); ++i) {
     const auto& entry = values_[i];
 
     // Find the relevant subtables.
-    const HuffmanLookup lookup(entry.key_.bits_, entry.key_.bitLength_);
+    const HuffmanLookup lookup(entry.key().bits_, entry.key().bitLength_);
     const auto split = lookup.split(PrefixBitLength);
     MOZ_ASSERT_IF(split.suffix_.bitLength_ != 32,
                   split.suffix_.bits_ >> split.suffix_.bitLength_ == 0);
     for (const auto index : lookup.suffixes(PrefixBitLength)) {
       auto& sub = subTables_[index];
 
       // We may now add a reference to `entry` into the sybtable.
       MOZ_TRY(sub.addSymbol(split.suffix_.bits_, split.suffix_.bitLength_,
-                            std::move(i)));
+                            BinASTSymbol::fromSubtableIndex(i)));
     }
   }
 
   // Finally, complete initialization of subtables.
   for (auto& sub : subTables_) {
     MOZ_TRY(sub.initComplete());
   }
 
   return Ok();
 }
 
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
-HuffmanEntry<const T*>
-MultiLookupHuffmanTable<T, Subtable, PrefixBitLength>::lookup(
+template <typename Subtable, uint8_t PrefixBitLength>
+HuffmanLookupResult MultiLookupHuffmanTable<Subtable, PrefixBitLength>::lookup(
     HuffmanLookup key) const {
   const auto split = key.split(PrefixBitLength);
   if (split.prefix_.bits_ >= subTables_.length()) {
-    return HuffmanEntry<const T*>(0, 0, nullptr);
+    return HuffmanLookupResult::notFound();
   }
   const Subtable& subtable = subTables_[split.prefix_.bits_];
 
-  auto found = subtable.lookup(split.suffix_);
-
-  if (found.value_ == nullptr) {
+  auto subResult = subtable.lookup(split.suffix_);
+  if (!subResult.isFound()) {
     // Propagate "not found".
-    return {0, 0, nullptr};
+    return HuffmanLookupResult::notFound();
   }
 
   // Otherwise, restore the entire `HuffmanEntry`.
-  const auto& result = values_[*found.value_];
-
-  return /* HuffmanEntry */ {/* bits */ result.key_.bits_,
-                             /* bitLength */ result.key_.bitLength_,
-                             /* value */ std::move(&result.value_)};
+  const auto& result = values_[subResult.value().toSubtableIndex()];
+
+  return HuffmanLookupResult::found(result.key().bitLength_, &result.value());
 }
 
 // -----
 
 // The number of possible interfaces in each sum, indexed by
 // `static_cast<size_t>(BinASTSum)`.
 const size_t SUM_LIMITS[]{
 #define WITH_SUM(_ENUM_NAME, _HUMAN_NAME, MACRO_NAME, _TYPE_NAME) \
@@ -2345,33 +2320,34 @@ template <>
 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
     const Boolean&) {
   // Sadly, there are only two booleans known to this date.
   return 2;
 }
 
 // Extract symbol from the grammar.
 template <>
-MOZ_MUST_USE JS::Result<bool> HuffmanPreludeReader::readSymbol(const Boolean&,
-                                                               size_t index) {
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
+    const Boolean&, size_t index) {
   MOZ_ASSERT(index < 2);
-  return index != 0;
+  return BinASTSymbol::fromBool(index != 0);
 }
 
 // Reading a single-value table of booleans
 template <>
 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Boolean>(
     Boolean::Table& table, const Boolean& entry) {
   uint8_t indexByte;
   MOZ_TRY_VAR(indexByte, reader_.readByte<Compression::No>());
   if (MOZ_UNLIKELY(indexByte >= 2)) {
     return raiseInvalidTableData(entry.identity_);
   }
 
-  MOZ_TRY(table.initWithSingleValue(cx_, indexByte != 0));
+  MOZ_TRY(
+      table.initWithSingleValue(cx_, BinASTSymbol::fromBool(indexByte != 0)));
   return Ok();
 }
 
 // ------ Optional interfaces.
 // 0 -> Null
 // 1 -> NonNull
 
 // Extract the number of symbols from the grammar.
@@ -2379,67 +2355,69 @@ template <>
 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
     const MaybeInterface&) {
   // Null, NonNull
   return 2;
 }
 
 // Extract symbol from the grammar.
 template <>
-MOZ_MUST_USE JS::Result<BinASTKind> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const MaybeInterface& entry, size_t index) {
   MOZ_ASSERT(index < 2);
-  return index == 0 ? BinASTKind::_Null : entry.kind_;
+  return BinASTSymbol::fromKind(index == 0 ? BinASTKind::_Null : entry.kind_);
 }
 
 // Reading a single-value table of optional interfaces
 template <>
 MOZ_MUST_USE JS::Result<Ok>
 HuffmanPreludeReader::readSingleValueTable<MaybeInterface>(
     MaybeInterface::Table& table, const MaybeInterface& entry) {
   uint8_t indexByte;
   MOZ_TRY_VAR(indexByte, reader_.readByte<Compression::No>());
   if (MOZ_UNLIKELY(indexByte >= 2)) {
     return raiseInvalidTableData(entry.identity_);
   }
 
   MOZ_TRY(table.initWithSingleValue(
-      cx_, indexByte == 0 ? BinASTKind::_Null : entry.kind_));
+      cx_, BinASTSymbol::fromKind(indexByte == 0 ? BinASTKind::_Null
+                                                 : entry.kind_)));
   return Ok();
 }
 
 // ------ Sums of interfaces
 // varnum i -> index `i` in the order defined by
 // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`
 
 // Extract the number of symbols from the grammar.
 template <>
 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
     const Sum& sum) {
   return sum.maxNumberOfSymbols();
 }
 
 // Extract symbol from the grammar.
 template <>
-MOZ_MUST_USE JS::Result<BinASTKind> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const Sum& entry, size_t index) {
   MOZ_ASSERT(index < entry.maxNumberOfSymbols());
-  return entry.interfaceAt(index);
+  return BinASTSymbol::fromKind(entry.interfaceAt(index));
 }
 
 // Reading a single-value table of sums of interfaces.
 template <>
 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Sum>(
     HuffmanTableIndexedSymbolsSum& table, const Sum& sum) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index >= sum.maxNumberOfSymbols())) {
     return raiseInvalidTableData(sum.identity_);
   }
 
-  MOZ_TRY(table.initWithSingleValue(cx_, sum.interfaceAt(index)));
+  MOZ_TRY(table.initWithSingleValue(
+      cx_, BinASTSymbol::fromKind(sum.interfaceAt(index))));
   return Ok();
 }
 
 // ------ Optional sums of interfaces
 // varnum 0 -> null
 // varnum i > 0 -> index `i - 1` in the order defined by
 // `FOR_EACH_BIN_INTERFACE_IN_SUM_*`
 
@@ -2447,33 +2425,34 @@ MOZ_MUST_USE JS::Result<Ok> HuffmanPrelu
 template <>
 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
     const MaybeSum& sum) {
   return sum.maxNumberOfSymbols();
 }
 
 // Extract symbol from the grammar.
 template <>
-MOZ_MUST_USE JS::Result<BinASTKind> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const MaybeSum& sum, size_t index) {
   MOZ_ASSERT(index < sum.maxNumberOfSymbols());
-  return sum.interfaceAt(index);
+  return BinASTSymbol::fromKind(sum.interfaceAt(index));
 }
 
 // Reading a single-value table of sums of interfaces.
 template <>
 MOZ_MUST_USE JS::Result<Ok>
 HuffmanPreludeReader::readSingleValueTable<MaybeSum>(
     HuffmanTableIndexedSymbolsSum& table, const MaybeSum& sum) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index >= sum.maxNumberOfSymbols())) {
     return raiseInvalidTableData(sum.identity_);
   }
 
-  MOZ_TRY(table.initWithSingleValue(cx_, sum.interfaceAt(index)));
+  MOZ_TRY(table.initWithSingleValue(
+      cx_, BinASTSymbol::fromKind(sum.interfaceAt(index))));
   return Ok();
 }
 
 // ------ Numbers
 // 64 bits, IEEE 754, big endian
 
 // Read the number of symbols from the stream.
 template <>
@@ -2483,43 +2462,40 @@ MOZ_MUST_USE JS::Result<uint32_t> Huffma
   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
     return raiseInvalidTableData(number.identity_);
   }
   return length;
 }
 
 // Read a single symbol from the stream.
 template <>
-MOZ_MUST_USE JS::Result<double> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const Number& number, size_t) {
   uint8_t bytes[8];
   MOZ_ASSERT(sizeof(bytes) == sizeof(double));
 
   uint32_t len = mozilla::ArrayLength(bytes);
   MOZ_TRY((reader_.readBuf<Compression::No, EndOfFilePolicy::RaiseError>(
       reinterpret_cast<uint8_t*>(bytes), len)));
 
   // Decode big-endian.
   const uint64_t asInt = mozilla::BigEndian::readUint64(bytes);
 
   // Canonicalize NaN, just to make sure another form of signalling NaN
   // doesn't slip past us.
-  return JS::CanonicalizeNaN(mozilla::BitwiseCast<double>(asInt));
+  return BinASTSymbol::fromDouble(
+      JS::CanonicalizeNaN(mozilla::BitwiseCast<double>(asInt)));
 }
 
 // Reading a single-value table of numbers.
 template <>
 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<Number>(
     HuffmanTableExplicitSymbolsF64& table, const Number& number) {
   BINJS_MOZ_TRY_DECL(value, readSymbol(number, 0 /* ignored */));
-  // Note: The `std::move` is useless for performance, but necessary to keep
-  // a consistent API.
-  MOZ_TRY(table.initWithSingleValue(
-      cx_,
-      /* NOLINT(performance-move-const-arg) */ std::move(value)));
+  MOZ_TRY(table.initWithSingleValue(cx_, value));
   return Ok();
 }
 
 // ------ List lengths
 // varnum
 
 // Read the number of symbols from the grammar.
 template <>
@@ -2529,34 +2505,34 @@ MOZ_MUST_USE JS::Result<uint32_t> Huffma
   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
     return raiseInvalidTableData(list.identity_);
   }
   return length;
 }
 
 // Read a single symbol from the stream.
 template <>
-MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const List& list, size_t) {
   BINJS_MOZ_TRY_DECL(length, reader_.readUnpackedLong());
   if (MOZ_UNLIKELY(length > MAX_LIST_LENGTH)) {
     return raiseInvalidTableData(list.identity_);
   }
-  return length;
+  return BinASTSymbol::fromListLength(length);
 }
 
 // Reading a single-value table of list lengths.
 template <>
 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<List>(
     HuffmanTableExplicitSymbolsListLength& table, const List& list) {
   BINJS_MOZ_TRY_DECL(length, reader_.readUnpackedLong());
   if (MOZ_UNLIKELY(length > MAX_LIST_LENGTH)) {
     return raiseInvalidTableData(list.identity_);
   }
-  MOZ_TRY(table.initWithSingleValue(cx_, std::move(length)));
+  MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromListLength(length)));
   return Ok();
 }
 
 // ------ Strings, non-nullable
 // varnum (index)
 
 // Read the number of symbols from the stream.
 template <>
@@ -2567,39 +2543,35 @@ MOZ_MUST_USE JS::Result<uint32_t> Huffma
                    length > reader_.metadata_->numStrings())) {
     return raiseInvalidTableData(string.identity_);
   }
   return length;
 }
 
 // Read a single symbol from the stream.
 template <>
-MOZ_MUST_USE JS::Result<JSAtom*> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const String& entry, size_t) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings())) {
     return raiseInvalidTableData(entry.identity_);
   }
-  return reader_.metadata_->getAtom(index);
+  return BinASTSymbol::fromAtom(reader_.metadata_->getAtom(index));
 }
 
 // Reading a single-value table of string indices.
 template <>
 MOZ_MUST_USE JS::Result<Ok> HuffmanPreludeReader::readSingleValueTable<String>(
     HuffmanTableIndexedSymbolsLiteralString& table, const String& entry) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings())) {
     return raiseInvalidTableData(entry.identity_);
   }
-  // Note: The `std::move` is useless for performance, but necessary to keep
-  // a consistent API.
   JSAtom* value = reader_.metadata_->getAtom(index);
-  MOZ_TRY(table.initWithSingleValue(
-      cx_,
-      /* NOLINT(performance-move-const-arg) */ std::move(value)));
+  MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromAtom(value)));
   return Ok();
 }
 
 // ------ Optional strings
 // varnum 0 -> null
 // varnum i > 0 -> string at index i - 1
 
 // Read the number of symbols from the metadata.
@@ -2611,80 +2583,72 @@ MOZ_MUST_USE JS::Result<uint32_t> Huffma
                    length > reader_.metadata_->numStrings() + 1)) {
     return raiseInvalidTableData(entry.identity_);
   }
   return length;
 }
 
 // Read a single symbol from the stream.
 template <>
-MOZ_MUST_USE JS::Result<JSAtom*> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const MaybeString& entry, size_t) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (index == 0) {
-    return nullptr;
+    return BinASTSymbol::fromAtom(nullptr);
   }
   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings() + 1)) {
     return raiseInvalidTableData(entry.identity_);
   }
-  return reader_.metadata_->getAtom(index - 1);
+  return BinASTSymbol::fromAtom(reader_.metadata_->getAtom(index - 1));
 }
 
 // Reading a single-value table of string indices.
 template <>
 MOZ_MUST_USE JS::Result<Ok>
 HuffmanPreludeReader::readSingleValueTable<MaybeString>(
     HuffmanTableIndexedSymbolsOptionalLiteralString& table,
     const MaybeString& entry) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index > reader_.metadata_->numStrings() + 1)) {
     return raiseInvalidTableData(entry.identity_);
   }
   JSAtom* symbol = index == 0 ? nullptr : reader_.metadata_->getAtom(index - 1);
-  // Note: The `std::move` is useless for performance, but necessary to keep
-  // a consistent API.
-  MOZ_TRY(table.initWithSingleValue(
-      cx_,
-      /* NOLINT(performance-move-const-arg) */ std::move(symbol)));
+  MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromAtom(symbol)));
   return Ok();
 }
 
 // ------ String Enums
 // varnum index in the enum
 
 // Read the number of symbols from the grammar.
 template <>
 MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readNumberOfSymbols(
     const StringEnum& entry) {
   return entry.maxNumberOfSymbols();
 }
 
 // Read a single symbol from the grammar.
 template <>
-MOZ_MUST_USE JS::Result<BinASTVariant> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const StringEnum& entry, size_t index) {
-  return entry.variantAt(index);
+  return BinASTSymbol::fromVariant(entry.variantAt(index));
 }
 
 // Reading a single-value table of string indices.
 template <>
 MOZ_MUST_USE JS::Result<Ok>
 HuffmanPreludeReader::readSingleValueTable<StringEnum>(
     HuffmanTableIndexedSymbolsStringEnum& table, const StringEnum& entry) {
   BINJS_MOZ_TRY_DECL(index, reader_.readVarU32<Compression::No>());
   if (MOZ_UNLIKELY(index > entry.maxNumberOfSymbols())) {
     return raiseInvalidTableData(entry.identity_);
   }
 
   BinASTVariant symbol = entry.variantAt(index);
-  // Note: The `std::move` is useless for performance, but necessary to keep
-  // a consistent API.
-  MOZ_TRY(table.initWithSingleValue(
-      cx_,
-      /* NOLINT(performance-move-const-arg) */ std::move(symbol)));
+  MOZ_TRY(table.initWithSingleValue(cx_, BinASTSymbol::fromVariant(symbol)));
   return Ok();
 }
 
 // ------ Unsigned Longs
 // Unpacked 32-bit
 
 // Read the number of symbols from the stream.
 template <>
@@ -2694,32 +2658,30 @@ MOZ_MUST_USE JS::Result<uint32_t> Huffma
   if (MOZ_UNLIKELY(length > MAX_NUMBER_OF_SYMBOLS)) {
     return raiseInvalidTableData(entry.identity_);
   }
   return length;
 }
 
 // Read a single symbol from the stream.
 template <>
-MOZ_MUST_USE JS::Result<uint32_t> HuffmanPreludeReader::readSymbol(
+MOZ_MUST_USE JS::Result<BinASTSymbol> HuffmanPreludeReader::readSymbol(
     const UnsignedLong& entry, size_t) {
-  return reader_.readUnpackedLong();
+  BINJS_MOZ_TRY_DECL(result, reader_.readUnpackedLong());
+  return BinASTSymbol::fromUnsignedLong(result);
 }
 
 // Reading a single-value table of string indices.
 template <>
 MOZ_MUST_USE JS::Result<Ok>
 HuffmanPreludeReader::readSingleValueTable<UnsignedLong>(
     HuffmanTableExplicitSymbolsU32& table, const UnsignedLong& entry) {
   BINJS_MOZ_TRY_DECL(index, reader_.readUnpackedLong());
-  // Note: The `std::move` is useless for performance, but necessary to keep
-  // a consistent API.
-  MOZ_TRY(table.initWithSingleValue(
-      cx_,
-      /* NOLINT(performance-move-const-arg) */ std::move(index)));
+  MOZ_TRY(
+      table.initWithSingleValue(cx_, BinASTSymbol::fromUnsignedLong(index)));
   return Ok();
 }
 
 HuffmanDictionary::HuffmanDictionary(JSContext* cx)
     : fields_(BINAST_PARAM_NUMBER_OF_INTERFACE_AND_FIELD(
           mozilla::AsVariant(HuffmanTableUnreachable()))),
       listLengths_(BINAST_PARAM_NUMBER_OF_LIST_TYPES(
           mozilla::AsVariant(HuffmanTableUnreachable()))) {}
--- a/js/src/frontend/BinASTTokenReaderContext.h
+++ b/js/src/frontend/BinASTTokenReaderContext.h
@@ -151,25 +151,106 @@ struct HuffmanKey {
   // The actual length of buffer `bits_`.
   //
   // MUST be within `[0, 32]`.
   //
   // If `bitLength_ < 32`, it means that some of the highest bits are unused.
   const uint8_t bitLength_;
 };
 
+// Symbol appears in the table.
+// This class is used to store symbols in `*HuffmanTable` classes without having
+// multiple implementation or different generated code for each type.
+//
+// This class doesn't store any tag to determine which kind of symbol it is.
+// The consumer MUST use the correct `from*`/`to*` pair.
+class alignas(8) BinASTSymbol {
+ private:
+  uint64_t asBits_;
+
+  explicit BinASTSymbol(uint64_t asBits) : asBits_(asBits) {}
+
+  static BinASTSymbol fromRawBits(uint64_t asBits) {
+    return BinASTSymbol(asBits);
+  }
+
+ public:
+  static BinASTSymbol fromUnsignedLong(uint32_t i) { return fromRawBits(i); }
+  static BinASTSymbol fromListLength(uint32_t i) { return fromRawBits(i); }
+  static BinASTSymbol fromSubtableIndex(size_t i) { return fromRawBits(i); }
+  static BinASTSymbol fromBool(bool b) { return fromRawBits(b); }
+  static BinASTSymbol fromDouble(double d) {
+    return fromRawBits(mozilla::BitwiseCast<uint64_t>(d));
+  }
+  static BinASTSymbol fromKind(BinASTKind k) {
+    return fromRawBits(uint64_t(k));
+  }
+  static BinASTSymbol fromVariant(BinASTVariant v) {
+    return fromRawBits(uint64_t(v));
+  }
+  static BinASTSymbol fromAtom(JSAtom* v) {
+    return fromRawBits(reinterpret_cast<uint64_t>(v));
+  }
+
+  uint32_t toUnsignedLong() const { return uint32_t(asBits_); }
+  uint32_t toListLength() const { return uint32_t(asBits_); }
+  size_t toSubtableIndex() const { return size_t(asBits_); }
+  bool toBool() const { return bool(asBits_); }
+  double toDouble() const { return mozilla::BitwiseCast<double>(asBits_); }
+  BinASTKind toKind() const { return BinASTKind(asBits_); }
+  BinASTVariant toVariant() const { return BinASTVariant(asBits_); }
+  JSAtom* toAtom() const { return reinterpret_cast<JSAtom*>(asBits_); }
+};
+
 // An entry in a Huffman table.
-template <typename T>
-struct HuffmanEntry {
-  HuffmanEntry(HuffmanKey key, T&& value) : key_(key), value_(value) {}
-  HuffmanEntry(uint32_t bits, uint8_t bitLength, T&& value)
+class HuffmanEntry {
+  const HuffmanKey key_;
+  const BinASTSymbol value_;
+
+ public:
+  HuffmanEntry(HuffmanKey key, const BinASTSymbol& value)
+      : key_(key), value_(value) {}
+
+  HuffmanEntry(uint32_t bits, uint8_t bitLength, const BinASTSymbol& value)
       : key_(bits, bitLength), value_(value) {}
 
-  const HuffmanKey key_;
-  const T value_;
+  const HuffmanKey& key() const { return key_; };
+  const BinASTSymbol& value() const { return value_; };
+};
+
+// The result of lookup in Huffman table.
+class HuffmanLookupResult {
+  uint8_t bitLength_;
+  const BinASTSymbol* value_;
+
+  HuffmanLookupResult(uint8_t bitLength, const BinASTSymbol* value)
+      : bitLength_(bitLength), value_(value) {}
+
+ public:
+  static HuffmanLookupResult found(uint8_t bitLength,
+                                   const BinASTSymbol* value) {
+    MOZ_ASSERT(value);
+    return HuffmanLookupResult(bitLength, value);
+  }
+
+  static HuffmanLookupResult notFound() {
+    return HuffmanLookupResult(0, nullptr);
+  }
+
+  bool isFound() const { return !!value_; };
+
+  uint8_t bitLength() const {
+    MOZ_ASSERT(isFound());
+    return bitLength_;
+  }
+
+  const BinASTSymbol& value() const {
+    MOZ_ASSERT(isFound());
+    return *value_;
+  }
 };
 
 // The default inline buffer length for instances of HuffmanTableValue.
 // Specific type (e.g. booleans) will override this to provide something
 // more suited to their type.
 const size_t HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH = 8;
 
 // A flag that determines only whether a value is `null`.
@@ -177,102 +258,103 @@ const size_t HUFFMAN_TABLE_DEFAULT_INLIN
 enum class Nullable {
   Null,
   NonNull,
 };
 
 // An implementation of Huffman Tables as a vector, with `O(entries)`
 // lookup. Performance-wise, this implementation only makes sense for
 // very short tables.
-template <typename T, int N = HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH>
+template <int N = HUFFMAN_TABLE_DEFAULT_INLINE_BUFFER_LENGTH>
 class NaiveHuffmanTable {
  public:
   explicit NaiveHuffmanTable(JSContext* cx) : values_(cx) {}
   NaiveHuffmanTable(NaiveHuffmanTable&& other) noexcept
       : values_(std::move(other.values_)) {}
 
   // Initialize a Huffman table containing a single value.
-  JS::Result<Ok> initWithSingleValue(JSContext* cx, T&& value);
+  JS::Result<Ok> initWithSingleValue(JSContext* cx, const BinASTSymbol& value);
 
   // Initialize a Huffman table containing `numberOfSymbols`.
   // Symbols must be added with `addSymbol`.
   // If you initialize with `initStart`, you MUST call `initComplete()`
   // at the end of initialization.
   JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
                            uint8_t maxBitLength);
 
   JS::Result<Ok> initComplete();
 
   // Add a symbol to a value.
-  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength, T&& value);
+  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
+                           const BinASTSymbol& value);
 
   NaiveHuffmanTable() = delete;
   NaiveHuffmanTable(NaiveHuffmanTable&) = delete;
 
   // Lookup a value in the table.
   //
   // The return of this method contains:
   //
   // - the resulting value (`nullptr` if the value is not in the table);
   // - the number of bits in the entry associated to this value.
   //
   // Note that entries inside a single table are typically associated to
   // distinct bit lengths. The caller is responsible for checking
   // the result of this method and advancing the bitstream by
-  // `result.key_.bitLength_` bits.
-  HuffmanEntry<const T*> lookup(HuffmanLookup lookup) const;
+  // `result.key().bitLength_` bits.
+  HuffmanLookupResult lookup(HuffmanLookup key) const;
 
   // The number of values in the table.
   size_t length() const { return values_.length(); }
-  const HuffmanEntry<T>* begin() const { return values_.begin(); }
-  const HuffmanEntry<T>* end() const { return values_.end(); }
+  const HuffmanEntry* begin() const { return values_.begin(); }
+  const HuffmanEntry* end() const { return values_.end(); }
 
  private:
   // The entries in this Huffman table.
   // Entries are always ranked by increasing bit_length, and within
   // a bitlength by increasing value of `bits`. This representation
   // is good for small tables, but in the future, we may adopt a
   // representation more optimized for larger tables.
-  Vector<HuffmanEntry<T>, N> values_;
+  Vector<HuffmanEntry, N> values_;
   friend class HuffmanPreludeReader;
 };
 
 // An implementation of Huffman Tables for single-entry table.
-template <typename T>
 class SingleEntryHuffmanTable {
  public:
-  explicit SingleEntryHuffmanTable(T&& value) : value_(std::move(value)) {}
+  explicit SingleEntryHuffmanTable(const BinASTSymbol& value) : value_(value) {}
   SingleEntryHuffmanTable(SingleEntryHuffmanTable&& other) = default;
 
   SingleEntryHuffmanTable() = delete;
   SingleEntryHuffmanTable(SingleEntryHuffmanTable&) = delete;
 
   // Lookup a value in the table.
   // The key is 0-bit length and this always suceeds.
-  HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
+  HuffmanLookupResult lookup(HuffmanLookup key) const;
 
   // The number of values in the table.
   size_t length() const { return 1; }
 
   // Iterating in the order of insertion.
   struct Iterator {
-    explicit Iterator(const T* position);
+    explicit Iterator(const BinASTSymbol* position);
     void operator++();
-    const T* operator*() const;
+    const BinASTSymbol* operator*() const;
+    const BinASTSymbol* operator->() const;
     bool operator==(const Iterator& other) const;
     bool operator!=(const Iterator& other) const;
 
    private:
-    const T* position_;
+    const BinASTSymbol* position_;
   };
   Iterator begin() const { return Iterator(&value_); }
   Iterator end() const { return Iterator(nullptr); }
 
  private:
-  T value_;
+  BinASTSymbol value_;
 
   friend class HuffmanPreludeReader;
 };
 
 // An implementation of Huffman Tables as a vector designed to allow
 // constant-time lookups at the expense of high space complexity.
 //
 // # Time complexity
@@ -339,17 +421,16 @@ class SingleEntryHuffmanTable {
 //
 // By using the next 5 bits in the bit buffer, we may, in
 // a single lookup, determine the symbol and the bit length.
 //
 // In the current implementation, to save some space, we have
 // two distinct arrays, one (`values_`) with a single instance of each
 // symbols bit length, and one (`saturated_`) with indices into that
 // array.
-template <typename T>
 class SingleLookupHuffmanTable {
  public:
   // An index into table `values_`.
   // We use `uint8_t` instead of `size_t` to limit the space
   // used by the table.
   using InternalIndex = uint8_t;
 
   // The largest bit length that may be represented by this table.
@@ -364,58 +445,60 @@ class SingleLookupHuffmanTable {
   // If you initialize with `initStart`, you MUST call `initComplete()`
   // at the end of initialization.
   JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
                            uint8_t maxBitLength);
 
   JS::Result<Ok> initComplete();
 
   // Add a `(bit, bitLength) => value` mapping.
-  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength, T&& value);
+  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
+                           const BinASTSymbol& value);
 
   SingleLookupHuffmanTable() = delete;
   SingleLookupHuffmanTable(SingleLookupHuffmanTable&) = delete;
 
   // Lookup a value in the table.
   //
   // The return of this method contains:
   //
   // - the resulting value (`nullptr` if the value is not in the table);
   // - the number of bits in the entry associated to this value.
   //
   // Note that entries inside a single table are typically associated to
   // distinct bit lengths. The caller is responsible for checking
   // the result of this method and advancing the bitstream by
-  // `result.key_.bitLength_` bits.
-  HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
+  // `result.key().bitLength_` bits.
+  HuffmanLookupResult lookup(HuffmanLookup key) const;
 
   // The number of values in the table.
   size_t length() const { return values_.length(); }
 
   // Iterating in the order of insertion.
   struct Iterator {
-    explicit Iterator(const HuffmanEntry<T>* position);
+    explicit Iterator(const HuffmanEntry* position);
     void operator++();
-    const T* operator*() const;
+    const BinASTSymbol* operator*() const;
+    const BinASTSymbol* operator->() const;
     bool operator==(const Iterator& other) const;
     bool operator!=(const Iterator& other) const;
 
    private:
-    const HuffmanEntry<T>* position_;
+    const HuffmanEntry* position_;
   };
   Iterator begin() const { return Iterator(values_.begin()); }
   Iterator end() const { return Iterator(values_.end()); }
 
  private:
   // The entries in this Huffman Table, sorted in the order of insertion.
   //
   // Invariant (once `init*` has been called):
   // - Length is the number of values inserted in the table.
   // - for all i, `values_[i].bitLength_ <= largestBitLength_`.
-  Vector<HuffmanEntry<T>> values_;
+  Vector<HuffmanEntry> values_;
 
   // The entries in this Huffman table, prepared for lookup.
   //
   // Invariant (once `init*` has been called):
   // - Length is `1 << largestBitLength_`.
   // - for all i, `saturated_[i] < values_.length()`
   Vector<InternalIndex> saturated_;
 
@@ -541,17 +624,17 @@ class SingleLookupHuffmanTable {
 /// - 7 tables with a max bit length of 0.
 ///
 /// Consequently, instead of storing 2^5 = 32 internal references,
 /// as we would have done with a SingleLookupHuffmanTable, we only
 /// need to store:
 ///
 /// - 7 subtables with 1 reference each;
 /// - 1 subtable with 2^2 = 4 references.
-template <typename T, typename Subtable, uint8_t PrefixBitLength>
+template <typename Subtable, uint8_t PrefixBitLength>
 class MultiLookupHuffmanTable {
  public:
   // The largest bit length that may be represented by this table.
   static const uint8_t MAX_BIT_LENGTH =
       PrefixBitLength + Subtable::MAX_BIT_LENGTH;
 
   explicit MultiLookupHuffmanTable(JSContext* cx)
       : cx_(cx), values_(cx), subTables_(cx), largestBitLength_(-1) {}
@@ -562,47 +645,49 @@ class MultiLookupHuffmanTable {
   // If you initialize with `initStart`, you MUST call `initComplete()`
   // at the end of initialization.
   JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
                            uint8_t largestBitLength);
 
   JS::Result<Ok> initComplete();
 
   // Add a `(bit, bitLength) => value` mapping.
-  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength, T&& value);
+  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
+                           const BinASTSymbol& value);
 
   MultiLookupHuffmanTable() = delete;
   MultiLookupHuffmanTable(MultiLookupHuffmanTable&) = delete;
 
   // Lookup a value in the table.
   //
   // The return of this method contains:
   //
   // - the resulting value (`nullptr` if the value is not in the table);
   // - the number of bits in the entry associated to this value.
   //
   // Note that entries inside a single table are typically associated to
   // distinct bit lengths. The caller is responsible for checking
   // the result of this method and advancing the bitstream by
-  // `result.key_.bitLength_` bits.
-  HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
+  // `result.key().bitLength_` bits.
+  HuffmanLookupResult lookup(HuffmanLookup key) const;
 
   // The number of values in the table.
   size_t length() const { return values_.length(); }
 
   // Iterating in the order of insertion.
   struct Iterator {
-    explicit Iterator(const HuffmanEntry<T>* position);
+    explicit Iterator(const HuffmanEntry* position);
     void operator++();
-    const T* operator*() const;
+    const BinASTSymbol* operator*() const;
+    const BinASTSymbol* operator->() const;
     bool operator==(const Iterator& other) const;
     bool operator!=(const Iterator& other) const;
 
    private:
-    const HuffmanEntry<T>* position_;
+    const HuffmanEntry* position_;
   };
   Iterator begin() const { return Iterator(values_.begin()); }
   Iterator end() const { return Iterator(values_.end()); }
 
  public:
   // An index into table `values_`.
   // We use `uint8_t` instead of `size_t` to limit the space
   // used by the table.
@@ -614,17 +699,17 @@ class MultiLookupHuffmanTable {
   // The entries in this Huffman Table, sorted in the order of insertion.
   //
   // Invariant (once `init*` has been called):
   // - Length is the number of values inserted in the table.
   // - for all i, `values_[i].bitLength_ <= largestBitLength_`.
   //
   // FIXME: In a ThreeLookupsHuffmanTable, we currently store each value
   // three times. We could at least get down to twice.
-  Vector<HuffmanEntry<T>> values_;
+  Vector<HuffmanEntry> values_;
 
   // A mapping from 0..2^prefixBitLen such that index `i`
   // maps to a subtable that holds all values associated
   // with a key that starts with `HuffmanKey(i, prefixBitLen)`.
   //
   // Note that, to allow the use of smaller tables, keys
   // inside the subtables have been stripped
   // from the prefix `HuffmanKey(i, prefixBitLen)`.
@@ -635,76 +720,75 @@ class MultiLookupHuffmanTable {
   // Invariant (once `init*` has been called):
   // - `largestBitLength_ <= MAX_CODE_BIT_LENGTH`
   uint8_t largestBitLength_;
 
   friend class HuffmanPreludeReader;
 };
 
 /// A Huffman table suitable for max bit lengths in [8, 14]
-template <typename T>
-using TwoLookupsHuffmanTable = MultiLookupHuffmanTable<
-    T, SingleLookupHuffmanTable</* external index */ size_t>, 6>;
+using TwoLookupsHuffmanTable =
+    MultiLookupHuffmanTable<SingleLookupHuffmanTable, 6>;
 
 /// A Huffman table suitable for max bit lengths in [15, 20]
-template <typename T>
-using ThreeLookupsHuffmanTable = MultiLookupHuffmanTable<
-    T, TwoLookupsHuffmanTable</* external index */ size_t>, 6>;
+using ThreeLookupsHuffmanTable =
+    MultiLookupHuffmanTable<TwoLookupsHuffmanTable, 6>;
 
 // An empty Huffman table. Attempting to get a value from this table is a syntax
 // error. This is the default value for `HuffmanTableValue` and represents all
 // states that may not be reached.
 //
 // Part of variants `HuffmanTableValue`, `HuffmanTableListLength` and
 // `GenericHuffmanTable::implementation`.
 struct HuffmanTableUnreachable {};
 
 // Generic implementation of Huffman tables.
 //
 //
-template <typename T>
 struct GenericHuffmanTable {
   explicit GenericHuffmanTable(JSContext* cx);
   explicit GenericHuffmanTable() = delete;
 
   // Initialize a Huffman table containing a single value.
-  JS::Result<Ok> initWithSingleValue(JSContext* cx, T&& value);
+  JS::Result<Ok> initWithSingleValue(JSContext* cx, const BinASTSymbol& value);
 
   // Initialize a Huffman table containing `numberOfSymbols`.
   // Symbols must be added with `addSymbol`.
   // If you initialize with `initStart`, you MUST call `initComplete()`
   // at the end of initialization.
   JS::Result<Ok> initStart(JSContext* cx, size_t numberOfSymbols,
                            uint8_t maxBitLength);
 
   // Add a `(bit, bitLength) => value` mapping.
-  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength, T&& value);
+  JS::Result<Ok> addSymbol(uint32_t bits, uint8_t bitLength,
+                           const BinASTSymbol& value);
 
   JS::Result<Ok> initComplete();
 
   // The number of values in the table.
   size_t length() const;
 
   struct Iterator {
-    explicit Iterator(typename SingleEntryHuffmanTable<T>::Iterator&&);
-    explicit Iterator(typename SingleLookupHuffmanTable<T>::Iterator&&);
-    explicit Iterator(typename TwoLookupsHuffmanTable<T>::Iterator&&);
-    explicit Iterator(typename ThreeLookupsHuffmanTable<T>::Iterator&&);
+    explicit Iterator(typename SingleEntryHuffmanTable::Iterator&&);
+    explicit Iterator(typename SingleLookupHuffmanTable::Iterator&&);
+    explicit Iterator(typename TwoLookupsHuffmanTable::Iterator&&);
+    explicit Iterator(typename ThreeLookupsHuffmanTable::Iterator&&);
     Iterator(Iterator&&) = default;
     Iterator(const Iterator&) = default;
     void operator++();
-    const T* operator*() const;
+    const BinASTSymbol* operator*() const;
+    const BinASTSymbol* operator->() const;
     bool operator==(const Iterator& other) const;
     bool operator!=(const Iterator& other) const;
 
    private:
-    mozilla::Variant<typename SingleEntryHuffmanTable<T>::Iterator,
-                     typename SingleLookupHuffmanTable<T>::Iterator,
-                     typename TwoLookupsHuffmanTable<T>::Iterator,
-                     typename ThreeLookupsHuffmanTable<T>::Iterator>
+    mozilla::Variant<typename SingleEntryHuffmanTable::Iterator,
+                     typename SingleLookupHuffmanTable::Iterator,
+                     typename TwoLookupsHuffmanTable::Iterator,
+                     typename ThreeLookupsHuffmanTable::Iterator>
         implementation_;
   };
 
   // Iterating in the order of insertion.
   Iterator begin() const;
   Iterator end() const;
 
   // Lookup a value in the table.
@@ -712,115 +796,103 @@ struct GenericHuffmanTable {
   // The return of this method contains:
   //
   // - the resulting value (`nullptr` if the value is not in the table);
   // - the number of bits in the entry associated to this value.
   //
   // Note that entries inside a single table are typically associated to
   // distinct bit lengths. The caller is responsible for checking
   // the result of this method and advancing the bitstream by
-  // `result.key_.bitLength_` bits.
-  HuffmanEntry<const T*> lookup(HuffmanLookup key) const;
+  // `result.key().bitLength_` bits.
+  HuffmanLookupResult lookup(HuffmanLookup key) const;
 
  private:
-  mozilla::Variant<SingleEntryHuffmanTable<T>, SingleLookupHuffmanTable<T>,
-                   TwoLookupsHuffmanTable<T>, ThreeLookupsHuffmanTable<T>,
+  mozilla::Variant<SingleEntryHuffmanTable, SingleLookupHuffmanTable,
+                   TwoLookupsHuffmanTable, ThreeLookupsHuffmanTable,
                    HuffmanTableUnreachable>
       implementation_;
 };
 
 // While reading the Huffman prelude, whenever we first encounter a
 // `HuffmanTableUnreachable`, we replace it with a `HuffmanTableInitializing`
 // to mark that we should not attempt to read/initialize it again.
 //
 // Attempting to get a value from this table is an internal error.
 //
 // Part of variants `HuffmanTableValue` and `HuffmanTableListLength`.
 struct HuffmanTableInitializing {};
 
 // These classes are all parts of variant `HuffmanTableValue`.
 
-struct HuffmanTableExplicitSymbolsF64 : GenericHuffmanTable<double> {
-  using Contents = double;
+struct HuffmanTableExplicitSymbolsF64 : GenericHuffmanTable {
   explicit HuffmanTableExplicitSymbolsF64(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
-struct HuffmanTableExplicitSymbolsU32 : GenericHuffmanTable<uint32_t> {
-  using Contents = uint32_t;
+struct HuffmanTableExplicitSymbolsU32 : GenericHuffmanTable {
   explicit HuffmanTableExplicitSymbolsU32(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
-struct HuffmanTableIndexedSymbolsSum : GenericHuffmanTable<BinASTKind> {
-  using Contents = BinASTKind;
+struct HuffmanTableIndexedSymbolsSum : GenericHuffmanTable {
   explicit HuffmanTableIndexedSymbolsSum(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
-struct HuffmanTableIndexedSymbolsBool : NaiveHuffmanTable<bool, 2> {
-  using Contents = bool;
+struct HuffmanTableIndexedSymbolsBool : NaiveHuffmanTable<2> {
   explicit HuffmanTableIndexedSymbolsBool(JSContext* cx)
       : NaiveHuffmanTable(cx) {}
 };
 
 // A Huffman table that may only ever contain two values:
 // `BinASTKind::_Null` and another `BinASTKind`.
-struct HuffmanTableIndexedSymbolsMaybeInterface
-    : NaiveHuffmanTable<BinASTKind, 2> {
-  using Contents = BinASTKind;
+struct HuffmanTableIndexedSymbolsMaybeInterface : NaiveHuffmanTable<2> {
   explicit HuffmanTableIndexedSymbolsMaybeInterface(JSContext* cx)
       : NaiveHuffmanTable(cx) {}
 
   // `true` if this table only contains values for `null`.
   bool isAlwaysNull() const {
     MOZ_ASSERT(length() > 0);
 
     // By definition, we have either 1 or 2 values.
     // By definition, if we have 2 values, one of them is not null.
     if (length() != 1) {
       return false;
     }
     // Otherwise, check the single value.
-    return begin()->value_ == BinASTKind::_Null;
+    return begin()->value().toKind() == BinASTKind::_Null;
   }
 };
 
-struct HuffmanTableIndexedSymbolsStringEnum
-    : GenericHuffmanTable<BinASTVariant> {
-  using Contents = BinASTVariant;
+struct HuffmanTableIndexedSymbolsStringEnum : GenericHuffmanTable {
   explicit HuffmanTableIndexedSymbolsStringEnum(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
-struct HuffmanTableIndexedSymbolsLiteralString : GenericHuffmanTable<JSAtom*> {
-  using Contents = JSAtom*;
+struct HuffmanTableIndexedSymbolsLiteralString : GenericHuffmanTable {
   explicit HuffmanTableIndexedSymbolsLiteralString(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
-struct HuffmanTableIndexedSymbolsOptionalLiteralString
-    : GenericHuffmanTable<JSAtom*> {
-  using Contents = JSAtom*;
+struct HuffmanTableIndexedSymbolsOptionalLiteralString : GenericHuffmanTable {
   explicit HuffmanTableIndexedSymbolsOptionalLiteralString(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
 // A single Huffman table, used for values.
 using HuffmanTableValue = mozilla::Variant<
     HuffmanTableUnreachable,  // Default value.
     HuffmanTableInitializing, HuffmanTableExplicitSymbolsF64,
     HuffmanTableExplicitSymbolsU32, HuffmanTableIndexedSymbolsSum,
     HuffmanTableIndexedSymbolsMaybeInterface, HuffmanTableIndexedSymbolsBool,
     HuffmanTableIndexedSymbolsStringEnum,
     HuffmanTableIndexedSymbolsLiteralString,
     HuffmanTableIndexedSymbolsOptionalLiteralString>;
 
-struct HuffmanTableExplicitSymbolsListLength : GenericHuffmanTable<uint32_t> {
-  using Contents = uint32_t;
+struct HuffmanTableExplicitSymbolsListLength : GenericHuffmanTable {
   explicit HuffmanTableExplicitSymbolsListLength(JSContext* cx)
       : GenericHuffmanTable(cx) {}
 };
 
 // A single Huffman table, specialized for list lengths.
 using HuffmanTableListLength =
     mozilla::Variant<HuffmanTableUnreachable,  // Default value.
                      HuffmanTableInitializing,
@@ -1169,17 +1241,17 @@ class MOZ_STACK_CLASS BinASTTokenReaderC
   MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong(const FieldContext&);
   MOZ_MUST_USE JS::Result<uint32_t> readUnpackedLong();
 
  private:
   MOZ_MUST_USE JS::Result<BinASTKind> readTagFromTable(
       const BinASTInterfaceAndField&);
 
   template <typename Table>
-  MOZ_MUST_USE JS::Result<typename Table::Contents> readFieldFromTable(
+  MOZ_MUST_USE JS::Result<BinASTSymbol> readFieldFromTable(
       const BinASTInterfaceAndField&);
 
   /**
    * Report an "invalid value error".
    */
   MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidValue();
 
   /**