tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StencilXdr.cpp (52237B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "frontend/StencilXdr.h"  // StencilXDR
      8 
      9 #include "mozilla/ArrayUtils.h"             // mozilla::ArrayEqual
     10 #include "mozilla/OperatorNewExtensions.h"  // mozilla::KnownNotNull
     11 #include "mozilla/RefPtr.h"                 // RefPtr
     12 #include "mozilla/ScopeExit.h"              // mozilla::MakeScopeExit
     13 #include "mozilla/Try.h"                    // MOZ_TRY
     14 
     15 #include <stddef.h>     // size_t
     16 #include <stdint.h>     // uint8_t, uint16_t, uint32_t
     17 #include <type_traits>  // std::has_unique_object_representations
     18 #include <utility>      // std::forward
     19 
     20 #include "ds/LifoAlloc.h"                 // LifoAlloc
     21 #include "frontend/CompilationStencil.h"  // CompilationStencil, ExtensibleCompilationStencil
     22 #include "frontend/FrontendContext.h"  // FrontendContext, AutoReportFrontendContext
     23 #include "frontend/ScriptIndex.h"      // ScriptIndex
     24 #include "js/CompileOptions.h"         // JS::ReadOnlyDecodeOptions
     25 #include "js/experimental/JSStencil.h"  // ScriptIndex
     26 #include "js/Transcoding.h"  // JS::TranscodeBuffer, JS::TranscodeRange, JS::TranscodeResult
     27 #include "vm/JSScript.h"      // ScriptSource
     28 #include "vm/Scope.h"         // SizeOfParserScopeData
     29 #include "vm/StencilEnums.h"  // js::ImmutableScriptFlagsEnum
     30 
     31 using namespace js;
     32 using namespace js::frontend;
     33 
     34 using mozilla::Utf8Unit;
     35 
     36 template <typename NameType>
     37 struct CanEncodeNameType {
     38  static constexpr bool value = false;
     39 };
     40 
     41 template <>
     42 struct CanEncodeNameType<TaggedParserAtomIndex> {
     43  static constexpr bool value = true;
     44 };
     45 
     46 template <XDRMode mode, typename T, size_t N, class AP>
     47 static XDRResult XDRVectorUninitialized(XDRState<mode>* xdr,
     48                                        Vector<T, N, AP>& vec,
     49                                        uint32_t& length) {
     50  if (mode == XDR_ENCODE) {
     51    MOZ_ASSERT(vec.length() <= UINT32_MAX);
     52    length = vec.length();
     53  }
     54 
     55  MOZ_TRY(xdr->codeUint32(&length));
     56 
     57  if (mode == XDR_DECODE) {
     58    MOZ_ASSERT(vec.empty());
     59    if (!vec.resizeUninitialized(length)) {
     60      js::ReportOutOfMemory(xdr->fc());
     61      return xdr->fail(JS::TranscodeResult::Throw);
     62    }
     63  }
     64 
     65  return Ok();
     66 }
     67 
     68 template <XDRMode mode, typename T, size_t N, class AP>
     69 static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
     70                                      Vector<T, N, AP>& vec, uint32_t length) {
     71  MOZ_ASSERT_IF(mode == XDR_ENCODE, length == vec.length());
     72 
     73  if (mode == XDR_DECODE) {
     74    MOZ_ASSERT(vec.empty());
     75    if (!vec.resize(length)) {
     76      js::ReportOutOfMemory(xdr->fc());
     77      return xdr->fail(JS::TranscodeResult::Throw);
     78    }
     79  }
     80 
     81  return Ok();
     82 }
     83 
     84 template <XDRMode mode, typename T, size_t N, class AP>
     85 static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
     86                                      Vector<T, N, AP>& vec) {
     87  uint32_t length;
     88  if (mode == XDR_ENCODE) {
     89    MOZ_ASSERT(vec.length() <= UINT32_MAX);
     90    length = vec.length();
     91  }
     92 
     93  MOZ_TRY(xdr->codeUint32(&length));
     94 
     95  return XDRVectorInitialized(xdr, vec, length);
     96 }
     97 
     98 template <XDRMode mode, typename T, size_t N, class AP>
     99 static XDRResult XDRVectorContent(XDRState<mode>* xdr, Vector<T, N, AP>& vec) {
    100  static_assert(CanCopyDataToDisk<T>::value,
    101                "Vector content cannot be bulk-copied to disk.");
    102 
    103  uint32_t length;
    104  MOZ_TRY(XDRVectorUninitialized(xdr, vec, length));
    105  MOZ_TRY(xdr->codeBytes(vec.begin(), sizeof(T) * length));
    106 
    107  return Ok();
    108 }
    109 
    110 template <XDRMode mode, typename T>
    111 static XDRResult XDRSpanInitialized(XDRState<mode>* xdr, LifoAlloc& alloc,
    112                                    mozilla::Span<T>& span, uint32_t size) {
    113  MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());
    114 
    115  if (mode == XDR_DECODE) {
    116    MOZ_ASSERT(span.empty());
    117    if (size > 0) {
    118      auto* p = alloc.template newArrayUninitialized<T>(size);
    119      if (!p) {
    120        js::ReportOutOfMemory(xdr->fc());
    121        return xdr->fail(JS::TranscodeResult::Throw);
    122      }
    123      span = mozilla::Span(p, size);
    124 
    125      for (size_t i = 0; i < size; i++) {
    126        new (mozilla::KnownNotNull, &span[i]) T();
    127      }
    128    }
    129  }
    130 
    131  return Ok();
    132 }
    133 
    134 template <XDRMode mode, typename T>
    135 static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
    136                                mozilla::Span<T>& span, uint32_t size) {
    137  static_assert(CanCopyDataToDisk<T>::value,
    138                "Span cannot be bulk-copied to disk.");
    139  MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());
    140 
    141  if (size) {
    142    MOZ_TRY(xdr->align32());
    143 
    144    T* data;
    145    if constexpr (mode == XDR_ENCODE) {
    146      data = span.data();
    147      MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
    148    } else {
    149      const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    150      if (options.borrowBuffer) {
    151        MOZ_TRY(xdr->borrowedData(&data, sizeof(T) * size));
    152      } else {
    153        data = alloc.template newArrayUninitialized<T>(size);
    154        if (!data) {
    155          js::ReportOutOfMemory(xdr->fc());
    156          return xdr->fail(JS::TranscodeResult::Throw);
    157        }
    158        MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
    159      }
    160    }
    161    if (mode == XDR_DECODE) {
    162      span = mozilla::Span(data, size);
    163    }
    164  }
    165 
    166  return Ok();
    167 }
    168 
    169 template <XDRMode mode, typename T>
    170 static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
    171                                mozilla::Span<T>& span) {
    172  uint32_t size;
    173  if (mode == XDR_ENCODE) {
    174    MOZ_ASSERT(span.size() <= UINT32_MAX);
    175    size = span.size();
    176  }
    177 
    178  MOZ_TRY(xdr->codeUint32(&size));
    179 
    180  return XDRSpanContent(xdr, alloc, span, size);
    181 }
    182 
    183 template <XDRMode mode>
    184 /* static */ XDRResult StencilXDR::codeBigInt(XDRState<mode>* xdr,
    185                                              LifoAlloc& alloc,
    186                                              BigIntStencil& stencil) {
    187  uint32_t size;
    188  if (mode == XDR_ENCODE) {
    189    size = stencil.bigInt_.match(
    190        [](mozilla::Span<char16_t> source) { return source.size(); },
    191        [](int64_t) { return size_t(0); });
    192  }
    193  MOZ_TRY(xdr->codeUint32(&size));
    194 
    195  // Zero-length size indicates inline storage for int64-sized BigInts.
    196  if (size == 0) {
    197    uint64_t num;
    198    if (mode == XDR_ENCODE) {
    199      num = static_cast<uint64_t>(stencil.bigInt_.as<int64_t>());
    200    }
    201    MOZ_TRY(xdr->codeUint64(&num));
    202    if (mode == XDR_DECODE) {
    203      stencil.bigInt_.as<int64_t>() = static_cast<int64_t>(num);
    204    }
    205    return Ok();
    206  }
    207 
    208  return XDRSpanContent(xdr, alloc, stencil.source(), size);
    209 }
    210 
    211 template <XDRMode mode>
    212 /* static */ XDRResult StencilXDR::codeObjLiteral(XDRState<mode>* xdr,
    213                                                  LifoAlloc& alloc,
    214                                                  ObjLiteralStencil& stencil) {
    215  uint8_t kindAndFlags = 0;
    216 
    217  if (mode == XDR_ENCODE) {
    218    static_assert(sizeof(ObjLiteralKindAndFlags) == sizeof(uint8_t));
    219    kindAndFlags = stencil.kindAndFlags_.toRaw();
    220  }
    221  MOZ_TRY(xdr->codeUint8(&kindAndFlags));
    222  if (mode == XDR_DECODE) {
    223    stencil.kindAndFlags_.setRaw(kindAndFlags);
    224  }
    225 
    226  MOZ_TRY(xdr->codeUint32(&stencil.propertyCount_));
    227 
    228  MOZ_TRY(XDRSpanContent(xdr, alloc, stencil.code_));
    229 
    230  return Ok();
    231 }
    232 
    233 template <typename ScopeT>
    234 /* static */ void AssertScopeSpecificDataIsEncodable() {
    235  using ScopeDataT = typename ScopeT::ParserData;
    236 
    237  static_assert(CanEncodeNameType<typename ScopeDataT::NameType>::value);
    238  static_assert(CanCopyDataToDisk<ScopeDataT>::value,
    239                "ScopeData cannot be bulk-copied to disk");
    240 }
    241 
    242 template <XDRMode mode>
    243 /* static */ XDRResult StencilXDR::codeScopeData(
    244    XDRState<mode>* xdr, LifoAlloc& alloc, ScopeStencil& stencil,
    245    BaseParserScopeData*& baseScopeData) {
    246  // WasmInstanceScope & WasmFunctionScope should not appear in stencils.
    247  MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmInstance);
    248  MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmFunction);
    249  if (stencil.kind_ == ScopeKind::With) {
    250    return Ok();
    251  }
    252 
    253  MOZ_TRY(xdr->align32());
    254 
    255  static_assert(offsetof(BaseParserScopeData, length) == 0,
    256                "length should be the first field");
    257  uint32_t length;
    258  if (mode == XDR_ENCODE) {
    259    length = baseScopeData->length;
    260  } else {
    261    MOZ_TRY(xdr->peekUint32(&length));
    262  }
    263 
    264  AssertScopeSpecificDataIsEncodable<FunctionScope>();
    265  AssertScopeSpecificDataIsEncodable<VarScope>();
    266  AssertScopeSpecificDataIsEncodable<LexicalScope>();
    267  AssertScopeSpecificDataIsEncodable<ClassBodyScope>();
    268  AssertScopeSpecificDataIsEncodable<EvalScope>();
    269  AssertScopeSpecificDataIsEncodable<GlobalScope>();
    270  AssertScopeSpecificDataIsEncodable<ModuleScope>();
    271 
    272  // In both decoding and encoding, stencil.kind_ is now known, and
    273  // can be assumed.  This allows the encoding to write out the bytes
    274  // for the specialized scope-data type without needing to encode
    275  // a distinguishing prefix.
    276  uint32_t totalLength = SizeOfParserScopeData(stencil.kind_, length);
    277  if constexpr (mode == XDR_ENCODE) {
    278    MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
    279  } else {
    280    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    281    if (options.borrowBuffer) {
    282      MOZ_TRY(xdr->borrowedData(&baseScopeData, totalLength));
    283    } else {
    284      baseScopeData =
    285          reinterpret_cast<BaseParserScopeData*>(alloc.alloc(totalLength));
    286      if (!baseScopeData) {
    287        js::ReportOutOfMemory(xdr->fc());
    288        return xdr->fail(JS::TranscodeResult::Throw);
    289      }
    290      MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
    291    }
    292  }
    293 
    294  return Ok();
    295 }
    296 
    297 template <XDRMode mode>
    298 /* static */
    299 XDRResult StencilXDR::codeSharedData(XDRState<mode>* xdr,
    300                                     RefPtr<SharedImmutableScriptData>& sisd) {
    301  static_assert(frontend::CanCopyDataToDisk<ImmutableScriptData>::value,
    302                "ImmutableScriptData cannot be bulk-copied to disk");
    303  static_assert(frontend::CanCopyDataToDisk<jsbytecode>::value,
    304                "jsbytecode cannot be bulk-copied to disk");
    305  static_assert(frontend::CanCopyDataToDisk<SrcNote>::value,
    306                "SrcNote cannot be bulk-copied to disk");
    307  static_assert(frontend::CanCopyDataToDisk<ScopeNote>::value,
    308                "ScopeNote cannot be bulk-copied to disk");
    309  static_assert(frontend::CanCopyDataToDisk<TryNote>::value,
    310                "TryNote cannot be bulk-copied to disk");
    311 
    312  uint32_t size;
    313  uint32_t hash;
    314  if (mode == XDR_ENCODE) {
    315    if (sisd) {
    316      size = sisd->immutableDataLength();
    317      hash = sisd->hash();
    318    } else {
    319      size = 0;
    320      hash = 0;
    321    }
    322  }
    323  MOZ_TRY(xdr->codeUint32(&size));
    324 
    325  // A size of zero is used when the `sisd` is nullptr. This can occur for
    326  // certain outer container modes. In this case, there is no further
    327  // transcoding to do.
    328  if (!size) {
    329    MOZ_ASSERT(!sisd);
    330    return Ok();
    331  }
    332 
    333  MOZ_TRY(xdr->align32());
    334  static_assert(alignof(ImmutableScriptData) <= alignof(uint32_t));
    335 
    336  MOZ_TRY(xdr->codeUint32(&hash));
    337 
    338  if constexpr (mode == XDR_ENCODE) {
    339    uint8_t* data = const_cast<uint8_t*>(sisd->get()->immutableData().data());
    340    MOZ_ASSERT(data == reinterpret_cast<const uint8_t*>(sisd->get()),
    341               "Decode below relies on the data placement");
    342    MOZ_TRY(xdr->codeBytes(data, size));
    343  } else {
    344    sisd = SharedImmutableScriptData::create(xdr->fc());
    345    if (!sisd) {
    346      return xdr->fail(JS::TranscodeResult::Throw);
    347    }
    348 
    349    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    350    if (options.usePinnedBytecode) {
    351      MOZ_ASSERT(options.borrowBuffer);
    352      ImmutableScriptData* isd;
    353      MOZ_TRY(xdr->borrowedData(&isd, size));
    354      sisd->setExternal(isd, hash);
    355    } else {
    356      auto isd = ImmutableScriptData::new_(xdr->fc(), size);
    357      if (!isd) {
    358        return xdr->fail(JS::TranscodeResult::Throw);
    359      }
    360      uint8_t* data = reinterpret_cast<uint8_t*>(isd.get());
    361      MOZ_TRY(xdr->codeBytes(data, size));
    362      sisd->setOwn(std::move(isd), hash);
    363    }
    364 
    365    if (!sisd->get()->validateLayout(size)) {
    366      MOZ_ASSERT(false, "Bad ImmutableScriptData");
    367      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
    368    }
    369  }
    370 
    371  if (mode == XDR_DECODE) {
    372    if (!SharedImmutableScriptData::shareScriptData(xdr->fc(), sisd)) {
    373      return xdr->fail(JS::TranscodeResult::Throw);
    374    }
    375  }
    376 
    377  return Ok();
    378 }
    379 
    380 // Called from js::XDRScript.
    381 template /* static */ XDRResult StencilXDR::codeSharedData(
    382    XDRState<XDR_ENCODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);
    383 template /* static */ XDRResult StencilXDR::codeSharedData(
    384    XDRState<XDR_DECODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);
    385 
    386 template <XDRMode mode>
    387 /* static */ XDRResult StencilXDR::codeSharedDataContainer(
    388    XDRState<mode>* xdr, SharedDataContainer& sharedData) {
    389  if (mode == XDR_ENCODE) {
    390    if (sharedData.isBorrow()) {
    391      return codeSharedDataContainer(xdr, *sharedData.asBorrow());
    392    }
    393  }
    394 
    395  enum Kind {
    396    Single,
    397    Vector,
    398    Map,
    399  };
    400 
    401  Kind kind;
    402  if (mode == XDR_ENCODE) {
    403    if (sharedData.isSingle()) {
    404      kind = Kind::Single;
    405    } else if (sharedData.isVector()) {
    406      kind = Kind::Vector;
    407    } else {
    408      MOZ_ASSERT(sharedData.isMap());
    409      kind = Kind::Map;
    410    }
    411  }
    412  MOZ_TRY(xdr->codeEnum32(&kind));
    413 
    414  switch (kind) {
    415    case Kind::Single: {
    416      RefPtr<SharedImmutableScriptData> ref;
    417      if (mode == XDR_ENCODE) {
    418        ref = sharedData.asSingle();
    419      }
    420      MOZ_TRY(codeSharedData<mode>(xdr, ref));
    421      if (mode == XDR_DECODE) {
    422        sharedData.setSingle(ref.forget());
    423      }
    424      break;
    425    }
    426 
    427    case Kind::Vector: {
    428      if (mode == XDR_DECODE) {
    429        if (!sharedData.initVector(xdr->fc())) {
    430          return xdr->fail(JS::TranscodeResult::Throw);
    431        }
    432      }
    433      auto& vec = *sharedData.asVector();
    434      MOZ_TRY(XDRVectorInitialized(xdr, vec));
    435      for (auto& entry : vec) {
    436        // NOTE: There can be nullptr, even if we don't perform syntax parsing,
    437        //       because of constant folding.
    438        MOZ_TRY(codeSharedData<mode>(xdr, entry));
    439      }
    440      break;
    441    }
    442 
    443    case Kind::Map: {
    444      if (mode == XDR_DECODE) {
    445        if (!sharedData.initMap(xdr->fc())) {
    446          return xdr->fail(JS::TranscodeResult::Throw);
    447        }
    448      }
    449      auto& map = *sharedData.asMap();
    450      uint32_t count;
    451      if (mode == XDR_ENCODE) {
    452        count = map.count();
    453      }
    454      MOZ_TRY(xdr->codeUint32(&count));
    455      if (mode == XDR_DECODE) {
    456        if (!map.reserve(count)) {
    457          js::ReportOutOfMemory(xdr->fc());
    458          return xdr->fail(JS::TranscodeResult::Throw);
    459        }
    460      }
    461 
    462      if (mode == XDR_ENCODE) {
    463        for (auto iter = map.iter(); !iter.done(); iter.next()) {
    464          uint32_t index = iter.get().key().index;
    465          auto& data = iter.get().value();
    466          MOZ_TRY(xdr->codeUint32(&index));
    467          MOZ_TRY(codeSharedData<mode>(xdr, data));
    468        }
    469      } else {
    470        for (uint32_t i = 0; i < count; i++) {
    471          ScriptIndex index;
    472          MOZ_TRY(xdr->codeUint32(&index.index));
    473 
    474          RefPtr<SharedImmutableScriptData> data;
    475          MOZ_TRY(codeSharedData<mode>(xdr, data));
    476 
    477          if (!map.putNew(index, data)) {
    478            js::ReportOutOfMemory(xdr->fc());
    479            return xdr->fail(JS::TranscodeResult::Throw);
    480          }
    481        }
    482      }
    483 
    484      break;
    485    }
    486 
    487    default:
    488      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
    489  }
    490 
    491  return Ok();
    492 }
    493 
    494 template <XDRMode mode>
    495 /* static */ XDRResult StencilXDR::codeParserAtom(XDRState<mode>* xdr,
    496                                                  LifoAlloc& alloc,
    497                                                  ParserAtom** atomp) {
    498  static_assert(CanCopyDataToDisk<ParserAtom>::value,
    499                "ParserAtom cannot be bulk-copied to disk.");
    500 
    501  MOZ_TRY(xdr->align32());
    502 
    503  const ParserAtom* header;
    504  if (mode == XDR_ENCODE) {
    505    header = *atomp;
    506  } else {
    507    MOZ_TRY(xdr->peekData(&header));
    508  }
    509 
    510  const uint32_t CharSize =
    511      header->hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
    512  uint32_t totalLength = sizeof(ParserAtom) + (CharSize * header->length());
    513 
    514  if constexpr (mode == XDR_ENCODE) {
    515    MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
    516  } else {
    517    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    518    if (options.borrowBuffer) {
    519      MOZ_TRY(xdr->borrowedData(atomp, totalLength));
    520    } else {
    521      *atomp = reinterpret_cast<ParserAtom*>(alloc.alloc(totalLength));
    522      if (!*atomp) {
    523        js::ReportOutOfMemory(xdr->fc());
    524        return xdr->fail(JS::TranscodeResult::Throw);
    525      }
    526      MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
    527    }
    528  }
    529 
    530  return Ok();
    531 }
    532 
    533 template <XDRMode mode>
    534 static XDRResult XDRAtomCount(XDRState<mode>* xdr, uint32_t* atomCount) {
    535  return xdr->codeUint32(atomCount);
    536 }
    537 
    538 template <XDRMode mode>
    539 /* static */ XDRResult StencilXDR::codeParserAtomSpan(
    540    XDRState<mode>* xdr, LifoAlloc& alloc, ParserAtomSpan& parserAtomData) {
    541  if (mode == XDR_ENCODE) {
    542    uint32_t atomVectorLength = parserAtomData.size();
    543    MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
    544 
    545    uint32_t atomCount = 0;
    546    for (const auto& entry : parserAtomData) {
    547      if (!entry) {
    548        continue;
    549      }
    550      if (entry->isUsedByStencil()) {
    551        atomCount++;
    552      }
    553    }
    554    MOZ_TRY(XDRAtomCount(xdr, &atomCount));
    555 
    556    for (uint32_t i = 0; i < atomVectorLength; i++) {
    557      auto& entry = parserAtomData[i];
    558      if (!entry) {
    559        continue;
    560      }
    561      if (entry->isUsedByStencil()) {
    562        MOZ_TRY(xdr->codeUint32(&i));
    563        MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
    564      }
    565    }
    566 
    567    return Ok();
    568  }
    569 
    570  uint32_t atomVectorLength;
    571  MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
    572 
    573  frontend::ParserAtomSpanBuilder builder(parserAtomData);
    574  if (!builder.allocate(xdr->fc(), alloc, atomVectorLength)) {
    575    return xdr->fail(JS::TranscodeResult::Throw);
    576  }
    577 
    578  uint32_t atomCount;
    579  MOZ_TRY(XDRAtomCount(xdr, &atomCount));
    580 
    581  for (uint32_t i = 0; i < atomCount; i++) {
    582    frontend::ParserAtom* entry = nullptr;
    583    uint32_t index;
    584    MOZ_TRY(xdr->codeUint32(&index));
    585    MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
    586    if (mode == XDR_DECODE) {
    587      if (index >= atomVectorLength) {
    588        return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
    589      }
    590    }
    591    builder.set(frontend::ParserAtomIndex(index), entry);
    592  }
    593 
    594  return Ok();
    595 }
    596 
    597 template <XDRMode mode>
    598 /* static */ XDRResult StencilXDR::codeModuleRequest(
    599    XDRState<mode>* xdr, StencilModuleRequest& stencil) {
    600  MOZ_TRY(xdr->codeUint32(stencil.specifier.rawDataRef()));
    601  MOZ_TRY(xdr->codeUint32(stencil.firstUnsupportedAttributeKey.rawDataRef()));
    602  MOZ_TRY(XDRVectorContent(xdr, stencil.attributes));
    603 
    604  return Ok();
    605 }
    606 
    607 template <XDRMode mode>
    608 /* static */ XDRResult StencilXDR::codeModuleRequestVector(
    609    XDRState<mode>* xdr, StencilModuleMetadata::RequestVector& vector) {
    610  MOZ_TRY(XDRVectorInitialized(xdr, vector));
    611 
    612  for (auto& entry : vector) {
    613    MOZ_TRY(codeModuleRequest<mode>(xdr, entry));
    614  }
    615 
    616  return Ok();
    617 }
    618 
    619 template <XDRMode mode>
    620 /* static */ XDRResult StencilXDR::codeModuleEntry(
    621    XDRState<mode>* xdr, StencilModuleEntry& stencil) {
    622  MOZ_TRY(xdr->codeUint32(&stencil.moduleRequest));
    623  MOZ_TRY(xdr->codeUint32(stencil.localName.rawDataRef()));
    624  MOZ_TRY(xdr->codeUint32(stencil.importName.rawDataRef()));
    625  MOZ_TRY(xdr->codeUint32(stencil.exportName.rawDataRef()));
    626  MOZ_TRY(xdr->codeUint32(&stencil.lineno));
    627  MOZ_TRY(xdr->codeUint32(stencil.column.addressOfValueForTranscode()));
    628 
    629  return Ok();
    630 }
    631 
    632 template <XDRMode mode>
    633 /* static */ XDRResult StencilXDR::codeModuleEntryVector(
    634    XDRState<mode>* xdr, StencilModuleMetadata::EntryVector& vector) {
    635  MOZ_TRY(XDRVectorInitialized(xdr, vector));
    636 
    637  for (auto& entry : vector) {
    638    MOZ_TRY(codeModuleEntry<mode>(xdr, entry));
    639  }
    640 
    641  return Ok();
    642 }
    643 
    644 template <XDRMode mode>
    645 /* static */ XDRResult StencilXDR::codeModuleMetadata(
    646    XDRState<mode>* xdr, StencilModuleMetadata& stencil) {
    647  MOZ_TRY(codeModuleRequestVector(xdr, stencil.moduleRequests));
    648  MOZ_TRY(codeModuleEntryVector(xdr, stencil.requestedModules));
    649  MOZ_TRY(codeModuleEntryVector(xdr, stencil.importEntries));
    650  MOZ_TRY(codeModuleEntryVector(xdr, stencil.localExportEntries));
    651  MOZ_TRY(codeModuleEntryVector(xdr, stencil.indirectExportEntries));
    652  MOZ_TRY(codeModuleEntryVector(xdr, stencil.starExportEntries));
    653  MOZ_TRY(XDRVectorContent(xdr, stencil.functionDecls));
    654 
    655  uint8_t isAsync = 0;
    656  if (mode == XDR_ENCODE) {
    657    if (stencil.isAsync) {
    658      isAsync = stencil.isAsync ? 1 : 0;
    659    }
    660  }
    661 
    662  MOZ_TRY(xdr->codeUint8(&isAsync));
    663 
    664  if (mode == XDR_DECODE) {
    665    stencil.isAsync = isAsync == 1;
    666  }
    667 
    668  return Ok();
    669 }
    670 
    671 template <XDRMode mode>
    672 XDRResult XDRCompilationStencilSpanSize(
    673    XDRState<mode>* xdr, uint32_t* scriptSize, uint32_t* gcThingSize,
    674    uint32_t* scopeSize, uint32_t* scriptExtraSize, uint32_t* regExpSize,
    675    uint32_t* bigIntSize, uint32_t* objLiteralSize) {
    676  // Compress the series of span sizes, to avoid consuming extra space for
    677  // unused/small span sizes.
    678  // There will be align32 shortly after this section, so try to make the
    679  // padding smaller.
    680 
    681  enum XDRSpanSizeKind {
    682    // All of the size values fit in 1 byte each. The entire section takes 7
    683    // bytes, and expect no padding.
    684    All8Kind,
    685 
    686    // Other cases. All of the size values fit in 4 bytes each. Expect 3 bytes
    687    // padding for `sizeKind`.
    688    All32Kind,
    689  };
    690 
    691  uint8_t sizeKind = All32Kind;
    692  if (mode == XDR_ENCODE) {
    693    uint32_t mask = (*scriptSize) | (*gcThingSize) | (*scopeSize) |
    694                    (*scriptExtraSize) | (*regExpSize) | (*bigIntSize) |
    695                    (*objLiteralSize);
    696 
    697    if (mask <= 0xff) {
    698      sizeKind = All8Kind;
    699    }
    700  }
    701  MOZ_TRY(xdr->codeUint8(&sizeKind));
    702 
    703  if (sizeKind == All32Kind) {
    704    MOZ_TRY(xdr->codeUint32(scriptSize));
    705    MOZ_TRY(xdr->codeUint32(gcThingSize));
    706    MOZ_TRY(xdr->codeUint32(scopeSize));
    707    MOZ_TRY(xdr->codeUint32(scriptExtraSize));
    708    MOZ_TRY(xdr->codeUint32(regExpSize));
    709    MOZ_TRY(xdr->codeUint32(bigIntSize));
    710    MOZ_TRY(xdr->codeUint32(objLiteralSize));
    711  } else {
    712    uint8_t scriptSize8 = 0;
    713    uint8_t gcThingSize8 = 0;
    714    uint8_t scopeSize8 = 0;
    715    uint8_t scriptExtraSize8 = 0;
    716    uint8_t regExpSize8 = 0;
    717    uint8_t bigIntSize8 = 0;
    718    uint8_t objLiteralSize8 = 0;
    719 
    720    if (mode == XDR_ENCODE) {
    721      scriptSize8 = uint8_t(*scriptSize);
    722      gcThingSize8 = uint8_t(*gcThingSize);
    723      scopeSize8 = uint8_t(*scopeSize);
    724      scriptExtraSize8 = uint8_t(*scriptExtraSize);
    725      regExpSize8 = uint8_t(*regExpSize);
    726      bigIntSize8 = uint8_t(*bigIntSize);
    727      objLiteralSize8 = uint8_t(*objLiteralSize);
    728    }
    729 
    730    MOZ_TRY(xdr->codeUint8(&scriptSize8));
    731    MOZ_TRY(xdr->codeUint8(&gcThingSize8));
    732    MOZ_TRY(xdr->codeUint8(&scopeSize8));
    733    MOZ_TRY(xdr->codeUint8(&scriptExtraSize8));
    734    MOZ_TRY(xdr->codeUint8(&regExpSize8));
    735    MOZ_TRY(xdr->codeUint8(&bigIntSize8));
    736    MOZ_TRY(xdr->codeUint8(&objLiteralSize8));
    737 
    738    if (mode == XDR_DECODE) {
    739      *scriptSize = scriptSize8;
    740      *gcThingSize = gcThingSize8;
    741      *scopeSize = scopeSize8;
    742      *scriptExtraSize = scriptExtraSize8;
    743      *regExpSize = regExpSize8;
    744      *bigIntSize = bigIntSize8;
    745      *objLiteralSize = objLiteralSize8;
    746    }
    747  }
    748 
    749  return Ok();
    750 }
    751 
    752 // Marker between each section inside CompilationStencil.
    753 //
    754 // These values should meet the following requirement:
    755 //   * No same value (differ more than single bit flip)
    756 //   * Bit pattern that won't frequently appear inside other XDR data
    757 //
    758 // Currently they're randomly chosen prime numbers that doesn't have same
    759 // byte pattern.
    760 enum class SectionMarker : uint32_t {
    761  ParserAtomData = 0xD9C098D3,
    762  ScopeData = 0x892C25EF,
    763  ScopeNames = 0x638C4FB3,
    764  RegExpData = 0xB030C2AF,
    765  BigIntData = 0x4B24F449,
    766  ObjLiteralData = 0x9AFAAE45,
    767  SharedData = 0xAAD52687,
    768  GCThingData = 0x1BD8F533,
    769  ScriptData = 0x840458FF,
    770  ScriptExtra = 0xA90E489D,
    771  ModuleMetadata = 0x94FDCE6D,
    772  End = 0x16DDA135,
    773 };
    774 
    775 template <XDRMode mode>
    776 static XDRResult CodeMarker(XDRState<mode>* xdr, SectionMarker marker) {
    777  return xdr->codeMarker(uint32_t(marker));
    778 }
    779 
    780 template <XDRMode mode>
    781 /* static */ XDRResult StencilXDR::codeCompilationStencil(
    782    XDRState<mode>* xdr, CompilationStencil& stencil) {
    783  MOZ_ASSERT(!stencil.hasAsmJS());
    784 
    785  if constexpr (mode == XDR_DECODE) {
    786    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    787    if (options.borrowBuffer) {
    788      stencil.storageType = CompilationStencil::StorageType::Borrowed;
    789    } else {
    790      stencil.storageType = CompilationStencil::StorageType::Owned;
    791    }
    792  }
    793 
    794  MOZ_TRY(CodeMarker(xdr, SectionMarker::ParserAtomData));
    795  MOZ_TRY(codeParserAtomSpan(xdr, stencil.alloc, stencil.parserAtomData));
    796 
    797  uint8_t canLazilyParse = 0;
    798 
    799  if (mode == XDR_ENCODE) {
    800    canLazilyParse = stencil.canLazilyParse;
    801  }
    802  MOZ_TRY(xdr->codeUint8(&canLazilyParse));
    803  if (mode == XDR_DECODE) {
    804    stencil.canLazilyParse = canLazilyParse;
    805  }
    806 
    807  MOZ_TRY(xdr->codeUint32(&stencil.functionKey));
    808 
    809  uint32_t scriptSize, gcThingSize, scopeSize, scriptExtraSize;
    810  uint32_t regExpSize, bigIntSize, objLiteralSize;
    811  if (mode == XDR_ENCODE) {
    812    scriptSize = stencil.scriptData.size();
    813    gcThingSize = stencil.gcThingData.size();
    814    scopeSize = stencil.scopeData.size();
    815    MOZ_ASSERT(scopeSize == stencil.scopeNames.size());
    816 
    817    scriptExtraSize = stencil.scriptExtra.size();
    818 
    819    regExpSize = stencil.regExpData.size();
    820    bigIntSize = stencil.bigIntData.size();
    821    objLiteralSize = stencil.objLiteralData.size();
    822  }
    823  MOZ_TRY(XDRCompilationStencilSpanSize(
    824      xdr, &scriptSize, &gcThingSize, &scopeSize, &scriptExtraSize, &regExpSize,
    825      &bigIntSize, &objLiteralSize));
    826 
    827  // All of the vector-indexed data elements referenced by the
    828  // main script tree must be materialized first.
    829 
    830  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeData));
    831  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scopeData, scopeSize));
    832 
    833  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeNames));
    834  MOZ_TRY(
    835      XDRSpanInitialized(xdr, stencil.alloc, stencil.scopeNames, scopeSize));
    836  MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
    837  for (uint32_t i = 0; i < scopeSize; i++) {
    838    MOZ_TRY(codeScopeData(xdr, stencil.alloc, stencil.scopeData[i],
    839                          stencil.scopeNames[i]));
    840  }
    841 
    842  MOZ_TRY(CodeMarker(xdr, SectionMarker::RegExpData));
    843  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.regExpData, regExpSize));
    844 
    845  MOZ_TRY(CodeMarker(xdr, SectionMarker::BigIntData));
    846  MOZ_TRY(
    847      XDRSpanInitialized(xdr, stencil.alloc, stencil.bigIntData, bigIntSize));
    848  for (auto& entry : stencil.bigIntData) {
    849    MOZ_TRY(codeBigInt(xdr, stencil.alloc, entry));
    850  }
    851 
    852  MOZ_TRY(CodeMarker(xdr, SectionMarker::ObjLiteralData));
    853  MOZ_TRY(XDRSpanInitialized(xdr, stencil.alloc, stencil.objLiteralData,
    854                             objLiteralSize));
    855  for (auto& entry : stencil.objLiteralData) {
    856    MOZ_TRY(codeObjLiteral(xdr, stencil.alloc, entry));
    857  }
    858 
    859  MOZ_TRY(CodeMarker(xdr, SectionMarker::SharedData));
    860  MOZ_TRY(codeSharedDataContainer(xdr, stencil.sharedData));
    861 
    862  MOZ_TRY(CodeMarker(xdr, SectionMarker::GCThingData));
    863  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.gcThingData, gcThingSize));
    864 
    865  // Now serialize the vector of ScriptStencils.
    866  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptData));
    867  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scriptData, scriptSize));
    868 
    869  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptExtra));
    870  MOZ_TRY(
    871      XDRSpanContent(xdr, stencil.alloc, stencil.scriptExtra, scriptExtraSize));
    872 
    873  // We don't support coding non-initial CompilationStencil.
    874  MOZ_ASSERT(stencil.isInitialStencil());
    875 
    876  if (stencil.scriptExtra[CompilationStencil::TopLevelIndex].isModule()) {
    877    if (mode == XDR_DECODE) {
    878      stencil.moduleMetadata =
    879          xdr->fc()->getAllocator()->template new_<StencilModuleMetadata>();
    880      if (!stencil.moduleMetadata) {
    881        return xdr->fail(JS::TranscodeResult::Throw);
    882      }
    883    }
    884 
    885    MOZ_TRY(CodeMarker(xdr, SectionMarker::ModuleMetadata));
    886    MOZ_TRY(codeModuleMetadata(xdr, *stencil.moduleMetadata));
    887 
    888    // codeModuleMetadata doesn't guarantee alignment.
    889    MOZ_TRY(xdr->align32());
    890  }
    891 
    892  MOZ_TRY(CodeMarker(xdr, SectionMarker::End));
    893 
    894  // The result should be aligned.
    895  //
    896  // NOTE:
    897  // If the top-level isn't a module, ScriptData/ScriptExtra sections
    898  // guarantee the alignment because there should be at least 1 item,
    899  // and XDRSpanContent adds alignment before span content, and the struct size
    900  // should also be aligned.
    901  static_assert(sizeof(ScriptStencil) % 4 == 0,
    902                "size of ScriptStencil should be aligned");
    903  static_assert(sizeof(ScriptStencilExtra) % 4 == 0,
    904                "size of ScriptStencilExtra should be aligned");
    905  MOZ_RELEASE_ASSERT(xdr->isAligned32());
    906 
    907  return Ok();
    908 }
    909 
    910 template <typename Unit>
    911 struct UnretrievableSourceDecoder {
    912  XDRState<XDR_DECODE>* const xdr_;
    913  ScriptSource* const scriptSource_;
    914  const uint32_t uncompressedLength_;
    915 
    916 public:
    917  UnretrievableSourceDecoder(XDRState<XDR_DECODE>* xdr,
    918                             ScriptSource* scriptSource,
    919                             uint32_t uncompressedLength)
    920      : xdr_(xdr),
    921        scriptSource_(scriptSource),
    922        uncompressedLength_(uncompressedLength) {}
    923 
    924  XDRResult decode() {
    925    auto sourceUnits = xdr_->fc()->getAllocator()->make_pod_array<Unit>(
    926        std::max<size_t>(uncompressedLength_, 1));
    927    if (!sourceUnits) {
    928      return xdr_->fail(JS::TranscodeResult::Throw);
    929    }
    930 
    931    MOZ_TRY(xdr_->codeChars(sourceUnits.get(), uncompressedLength_));
    932 
    933    if (!scriptSource_->initializeUnretrievableUncompressedSource(
    934            xdr_->fc(), std::move(sourceUnits), uncompressedLength_)) {
    935      return xdr_->fail(JS::TranscodeResult::Throw);
    936    }
    937 
    938    return Ok();
    939  }
    940 };
    941 
    942 template <>
    943 XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_DECODE>(
    944    XDRState<XDR_DECODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
    945    uint32_t uncompressedLength) {
    946  MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
    947 
    948  if (sourceCharSize == 1) {
    949    UnretrievableSourceDecoder<Utf8Unit> decoder(xdr, ss, uncompressedLength);
    950    return decoder.decode();
    951  }
    952 
    953  UnretrievableSourceDecoder<char16_t> decoder(xdr, ss, uncompressedLength);
    954  return decoder.decode();
    955 }
    956 
    957 template <typename Unit>
    958 struct UnretrievableSourceEncoder {
    959  XDRState<XDR_ENCODE>* const xdr_;
    960  ScriptSource* const source_;
    961  const uint32_t uncompressedLength_;
    962 
    963  UnretrievableSourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source,
    964                             uint32_t uncompressedLength)
    965      : xdr_(xdr), source_(source), uncompressedLength_(uncompressedLength) {}
    966 
    967  XDRResult encode() {
    968    Unit* sourceUnits =
    969        const_cast<Unit*>(source_->uncompressedData<Unit>()->units());
    970 
    971    return xdr_->codeChars(sourceUnits, uncompressedLength_);
    972  }
    973 };
    974 
    975 template <>
    976 /* static */
    977 XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_ENCODE>(
    978    XDRState<XDR_ENCODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
    979    uint32_t uncompressedLength) {
    980  MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
    981 
    982  if (sourceCharSize == 1) {
    983    UnretrievableSourceEncoder<Utf8Unit> encoder(xdr, ss, uncompressedLength);
    984    return encoder.encode();
    985  }
    986 
    987  UnretrievableSourceEncoder<char16_t> encoder(xdr, ss, uncompressedLength);
    988  return encoder.encode();
    989 }
    990 
    991 template <typename Unit, XDRMode mode>
    992 /* static */
    993 XDRResult StencilXDR::codeSourceUncompressedData(XDRState<mode>* const xdr,
    994                                                 ScriptSource* const ss) {
    995  static_assert(
    996      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
    997      "should handle UTF-8 and UTF-16");
    998 
    999  if (mode == XDR_ENCODE) {
   1000    MOZ_ASSERT(ss->isUncompressed<Unit>());
   1001  } else {
   1002    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
   1003  }
   1004 
   1005  uint32_t uncompressedLength;
   1006  if (mode == XDR_ENCODE) {
   1007    uncompressedLength = ss->uncompressedData<Unit>()->length();
   1008  }
   1009  MOZ_TRY(xdr->codeUint32(&uncompressedLength));
   1010 
   1011  return codeSourceUnretrievableUncompressed(xdr, ss, sizeof(Unit),
   1012                                             uncompressedLength);
   1013 }
   1014 
   1015 template <typename Unit, XDRMode mode>
   1016 /* static */
   1017 XDRResult StencilXDR::codeSourceCompressedData(XDRState<mode>* const xdr,
   1018                                               ScriptSource* const ss) {
   1019  static_assert(
   1020      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
   1021      "should handle UTF-8 and UTF-16");
   1022 
   1023  if (mode == XDR_ENCODE) {
   1024    MOZ_ASSERT(ss->isCompressed<Unit>());
   1025  } else {
   1026    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
   1027  }
   1028 
   1029  uint32_t uncompressedLength;
   1030  if (mode == XDR_ENCODE) {
   1031    uncompressedLength =
   1032        ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
   1033            .uncompressedLength;
   1034  }
   1035  MOZ_TRY(xdr->codeUint32(&uncompressedLength));
   1036 
   1037  uint32_t compressedLength;
   1038  if (mode == XDR_ENCODE) {
   1039    compressedLength =
   1040        ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
   1041            .raw.length();
   1042  }
   1043  MOZ_TRY(xdr->codeUint32(&compressedLength));
   1044 
   1045  if (mode == XDR_DECODE) {
   1046    // Compressed data is always single-byte chars.
   1047    auto bytes = xdr->fc()->getAllocator()->template make_pod_array<char>(
   1048        compressedLength);
   1049    if (!bytes) {
   1050      return xdr->fail(JS::TranscodeResult::Throw);
   1051    }
   1052    MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
   1053 
   1054    if (!ss->initializeWithUnretrievableCompressedSource<Unit>(
   1055            xdr->fc(), std::move(bytes), compressedLength,
   1056            uncompressedLength)) {
   1057      return xdr->fail(JS::TranscodeResult::Throw);
   1058    }
   1059  } else {
   1060    void* bytes = const_cast<char*>(ss->compressedData<Unit>()->raw.chars());
   1061    MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
   1062  }
   1063 
   1064  return Ok();
   1065 }
   1066 
   1067 template <typename Unit,
   1068          template <typename U, SourceRetrievable CanRetrieve> class Data,
   1069          XDRMode mode>
   1070 /* static */
   1071 void StencilXDR::codeSourceRetrievable(ScriptSource* const ss) {
   1072  static_assert(
   1073      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
   1074      "should handle UTF-8 and UTF-16");
   1075 
   1076  if (mode == XDR_ENCODE) {
   1077    MOZ_ASSERT((ss->data.is<Data<Unit, SourceRetrievable::Yes>>()));
   1078  } else {
   1079    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
   1080    ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
   1081  }
   1082 }
   1083 
   1084 template <typename Unit, XDRMode mode>
   1085 /* static */
   1086 void StencilXDR::codeSourceRetrievableData(ScriptSource* ss) {
   1087  // There's nothing to code for retrievable data.  Just be sure to set
   1088  // retrievable data when decoding.
   1089  if (mode == XDR_ENCODE) {
   1090    MOZ_ASSERT(ss->data.is<ScriptSource::Retrievable<Unit>>());
   1091  } else {
   1092    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
   1093    ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
   1094  }
   1095 }
   1096 
   1097 template <XDRMode mode>
   1098 /* static */
   1099 XDRResult StencilXDR::codeSourceData(XDRState<mode>* const xdr,
   1100                                     ScriptSource* const ss) {
   1101  // The order here corresponds to the type order in |ScriptSource::SourceType|
   1102  // so number->internal Variant tag is a no-op.
   1103  enum class DataType {
   1104    CompressedUtf8Retrievable,
   1105    UncompressedUtf8Retrievable,
   1106    CompressedUtf8NotRetrievable,
   1107    UncompressedUtf8NotRetrievable,
   1108    CompressedUtf16Retrievable,
   1109    UncompressedUtf16Retrievable,
   1110    CompressedUtf16NotRetrievable,
   1111    UncompressedUtf16NotRetrievable,
   1112    RetrievableUtf8,
   1113    RetrievableUtf16,
   1114    Missing,
   1115  };
   1116 
   1117  DataType tag;
   1118  {
   1119    // This is terrible, but we can't do better.  When |mode == XDR_DECODE| we
   1120    // don't have a |ScriptSource::data| |Variant| to match -- the entire XDR
   1121    // idiom for tagged unions depends on coding a tag-number, then the
   1122    // corresponding tagged data.  So we must manually define a tag-enum, code
   1123    // it, then switch on it (and ignore the |Variant::match| API).
   1124    class XDRDataTag {
   1125     public:
   1126      DataType operator()(
   1127          const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::Yes>&) {
   1128        return DataType::CompressedUtf8Retrievable;
   1129      }
   1130      DataType operator()(
   1131          const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::Yes>&) {
   1132        return DataType::UncompressedUtf8Retrievable;
   1133      }
   1134      DataType operator()(
   1135          const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::No>&) {
   1136        return DataType::CompressedUtf8NotRetrievable;
   1137      }
   1138      DataType operator()(
   1139          const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::No>&) {
   1140        return DataType::UncompressedUtf8NotRetrievable;
   1141      }
   1142      DataType operator()(
   1143          const ScriptSource::Compressed<char16_t, SourceRetrievable::Yes>&) {
   1144        return DataType::CompressedUtf16Retrievable;
   1145      }
   1146      DataType operator()(
   1147          const ScriptSource::Uncompressed<char16_t, SourceRetrievable::Yes>&) {
   1148        return DataType::UncompressedUtf16Retrievable;
   1149      }
   1150      DataType operator()(
   1151          const ScriptSource::Compressed<char16_t, SourceRetrievable::No>&) {
   1152        return DataType::CompressedUtf16NotRetrievable;
   1153      }
   1154      DataType operator()(
   1155          const ScriptSource::Uncompressed<char16_t, SourceRetrievable::No>&) {
   1156        return DataType::UncompressedUtf16NotRetrievable;
   1157      }
   1158      DataType operator()(const ScriptSource::Retrievable<Utf8Unit>&) {
   1159        return DataType::RetrievableUtf8;
   1160      }
   1161      DataType operator()(const ScriptSource::Retrievable<char16_t>&) {
   1162        return DataType::RetrievableUtf16;
   1163      }
   1164      DataType operator()(const ScriptSource::Missing&) {
   1165        return DataType::Missing;
   1166      }
   1167    };
   1168 
   1169    uint8_t type;
   1170    if (mode == XDR_ENCODE) {
   1171      type = static_cast<uint8_t>(ss->data.match(XDRDataTag()));
   1172    }
   1173    MOZ_TRY(xdr->codeUint8(&type));
   1174 
   1175    if (type > static_cast<uint8_t>(DataType::Missing)) {
   1176      // Fail in debug, but only soft-fail in release, if the type is invalid.
   1177      MOZ_ASSERT_UNREACHABLE("bad tag");
   1178      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
   1179    }
   1180 
   1181    tag = static_cast<DataType>(type);
   1182  }
   1183 
   1184  switch (tag) {
   1185    case DataType::CompressedUtf8Retrievable:
   1186      codeSourceRetrievable<Utf8Unit, ScriptSource::Compressed, mode>(ss);
   1187      return Ok();
   1188 
   1189    case DataType::CompressedUtf8NotRetrievable:
   1190      return codeSourceCompressedData<Utf8Unit>(xdr, ss);
   1191 
   1192    case DataType::UncompressedUtf8Retrievable:
   1193      codeSourceRetrievable<Utf8Unit, ScriptSource::Uncompressed, mode>(ss);
   1194      return Ok();
   1195 
   1196    case DataType::UncompressedUtf8NotRetrievable:
   1197      return codeSourceUncompressedData<Utf8Unit>(xdr, ss);
   1198 
   1199    case DataType::CompressedUtf16Retrievable:
   1200      codeSourceRetrievable<char16_t, ScriptSource::Compressed, mode>(ss);
   1201      return Ok();
   1202 
   1203    case DataType::CompressedUtf16NotRetrievable:
   1204      return codeSourceCompressedData<char16_t>(xdr, ss);
   1205 
   1206    case DataType::UncompressedUtf16Retrievable:
   1207      codeSourceRetrievable<char16_t, ScriptSource::Uncompressed, mode>(ss);
   1208      return Ok();
   1209 
   1210    case DataType::UncompressedUtf16NotRetrievable:
   1211      return codeSourceUncompressedData<char16_t>(xdr, ss);
   1212 
   1213    case DataType::Missing: {
   1214      MOZ_ASSERT(ss->data.is<ScriptSource::Missing>(),
   1215                 "ScriptSource::data is initialized as missing, so neither "
   1216                 "encoding nor decoding has to change anything");
   1217 
   1218      // There's no data to XDR for missing source.
   1219      break;
   1220    }
   1221 
   1222    case DataType::RetrievableUtf8:
   1223      codeSourceRetrievableData<Utf8Unit, mode>(ss);
   1224      return Ok();
   1225 
   1226    case DataType::RetrievableUtf16:
   1227      codeSourceRetrievableData<char16_t, mode>(ss);
   1228      return Ok();
   1229  }
   1230 
   1231  // The range-check on |type| far above ought ensure the above |switch| is
   1232  // exhaustive and all cases will return, but not all compilers understand
   1233  // this.  Make the Missing case break to here so control obviously never flows
   1234  // off the end.
   1235  MOZ_ASSERT(tag == DataType::Missing);
   1236  return Ok();
   1237 }
   1238 
   1239 template <XDRMode mode>
   1240 /* static */
   1241 XDRResult StencilXDR::codeSource(XDRState<mode>* xdr,
   1242                                 const JS::ReadOnlyDecodeOptions* maybeOptions,
   1243                                 RefPtr<ScriptSource>& source) {
   1244  FrontendContext* fc = xdr->fc();
   1245 
   1246  if (mode == XDR_DECODE) {
   1247    // Allocate a new ScriptSource and root it with the holder.
   1248    source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
   1249    if (!source) {
   1250      return xdr->fail(JS::TranscodeResult::Throw);
   1251    }
   1252  }
   1253 
   1254  static constexpr uint8_t HasFilename = 1 << 0;
   1255  static constexpr uint8_t HasDisplayURL = 1 << 1;
   1256  static constexpr uint8_t HasSourceMapURL = 1 << 2;
   1257  static constexpr uint8_t MutedErrors = 1 << 3;
   1258 
   1259  uint8_t flags = 0;
   1260  if (mode == XDR_ENCODE) {
   1261    if (source->filename_) {
   1262      flags |= HasFilename;
   1263    }
   1264    if (source->hasDisplayURL()) {
   1265      flags |= HasDisplayURL;
   1266    }
   1267    if (source->hasSourceMapURL()) {
   1268      flags |= HasSourceMapURL;
   1269    }
   1270    if (source->mutedErrors()) {
   1271      flags |= MutedErrors;
   1272    }
   1273  }
   1274 
   1275  MOZ_TRY(xdr->codeUint8(&flags));
   1276 
   1277  if (flags & HasFilename) {
   1278    XDRTranscodeString<char> chars;
   1279 
   1280    if (mode == XDR_ENCODE) {
   1281      chars.construct<const char*>(source->filename());
   1282    }
   1283    MOZ_TRY(xdr->codeCharsZ(chars));
   1284    if (mode == XDR_DECODE) {
   1285      if (!source->setFilename(fc, std::move(chars.ref<UniqueChars>()))) {
   1286        return xdr->fail(JS::TranscodeResult::Throw);
   1287      }
   1288    }
   1289  }
   1290 
   1291  if (flags & HasDisplayURL) {
   1292    XDRTranscodeString<char16_t> chars;
   1293 
   1294    if (mode == XDR_ENCODE) {
   1295      chars.construct<const char16_t*>(source->displayURL());
   1296    }
   1297    MOZ_TRY(xdr->codeCharsZ(chars));
   1298    if (mode == XDR_DECODE) {
   1299      if (!source->setDisplayURL(fc,
   1300                                 std::move(chars.ref<UniqueTwoByteChars>()))) {
   1301        return xdr->fail(JS::TranscodeResult::Throw);
   1302      }
   1303    }
   1304  }
   1305 
   1306  if (flags & HasSourceMapURL) {
   1307    XDRTranscodeString<char16_t> chars;
   1308 
   1309    if (mode == XDR_ENCODE) {
   1310      chars.construct<const char16_t*>(source->sourceMapURL());
   1311    }
   1312    MOZ_TRY(xdr->codeCharsZ(chars));
   1313    if (mode == XDR_DECODE) {
   1314      if (!source->setSourceMapURL(
   1315              fc, std::move(chars.ref<UniqueTwoByteChars>()))) {
   1316        return xdr->fail(JS::TranscodeResult::Throw);
   1317      }
   1318    }
   1319  }
   1320 
   1321  MOZ_ASSERT(source->parameterListEnd_ == 0);
   1322 
   1323  if (flags & MutedErrors) {
   1324    if (mode == XDR_DECODE) {
   1325      source->mutedErrors_ = true;
   1326    }
   1327  }
   1328 
   1329  MOZ_TRY(xdr->codeUint32(&source->startLine_));
   1330  MOZ_TRY(xdr->codeUint32(source->startColumn_.addressOfValueForTranscode()));
   1331 
   1332  // The introduction info doesn't persist across encode/decode.
   1333  if (mode == XDR_DECODE) {
   1334    source->introductionType_ = maybeOptions->introductionType;
   1335    source->setIntroductionOffset(maybeOptions->introductionOffset);
   1336    if (maybeOptions->introducerFilename()) {
   1337      if (!source->setIntroducerFilename(
   1338              fc, maybeOptions->introducerFilename().c_str())) {
   1339        return xdr->fail(JS::TranscodeResult::Throw);
   1340      }
   1341    }
   1342  }
   1343 
   1344  MOZ_TRY(codeSourceData(xdr, source.get()));
   1345 
   1346  return Ok();
   1347 }
   1348 
   1349 template /* static */
   1350    XDRResult
   1351    StencilXDR::codeSource(XDRState<XDR_ENCODE>* xdr,
   1352                           const JS::ReadOnlyDecodeOptions* maybeOptions,
   1353                           RefPtr<ScriptSource>& holder);
   1354 template /* static */
   1355    XDRResult
   1356    StencilXDR::codeSource(XDRState<XDR_DECODE>* xdr,
   1357                           const JS::ReadOnlyDecodeOptions* maybeOptions,
   1358                           RefPtr<ScriptSource>& holder);
   1359 
   1360 JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId(
   1361    JS::BuildIdCharVector* buildId) {
   1362  MOZ_ASSERT(buildId->empty());
   1363  MOZ_ASSERT(GetBuildId);
   1364 
   1365  if (!GetBuildId(buildId)) {
   1366    return false;
   1367  }
   1368 
   1369  // Note: the buildId returned here is also used for the bytecode cache MIME
   1370  // type so use plain ASCII characters.
   1371 
   1372  if (!buildId->reserve(buildId->length() + 4)) {
   1373    return false;
   1374  }
   1375 
   1376  buildId->infallibleAppend('-');
   1377 
   1378  // XDR depends on pointer size and endianness.
   1379  static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8);
   1380  buildId->infallibleAppend(sizeof(uintptr_t) == 4 ? '4' : '8');
   1381  buildId->infallibleAppend(MOZ_LITTLE_ENDIAN() ? 'l' : 'b');
   1382 
   1383  return true;
   1384 }
   1385 
   1386 template <XDRMode mode>
   1387 static XDRResult VersionCheck(XDRState<mode>* xdr) {
   1388  JS::BuildIdCharVector buildId;
   1389  if (!JS::GetScriptTranscodingBuildId(&buildId)) {
   1390    ReportOutOfMemory(xdr->fc());
   1391    return xdr->fail(JS::TranscodeResult::Throw);
   1392  }
   1393  MOZ_ASSERT(!buildId.empty());
   1394 
   1395  uint32_t buildIdLength;
   1396  if (mode == XDR_ENCODE) {
   1397    buildIdLength = buildId.length();
   1398  }
   1399 
   1400  MOZ_TRY(xdr->codeUint32(&buildIdLength));
   1401 
   1402  if (mode == XDR_DECODE && buildIdLength != buildId.length()) {
   1403    return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
   1404  }
   1405 
   1406  if (mode == XDR_ENCODE) {
   1407    MOZ_TRY(xdr->codeBytes(buildId.begin(), buildIdLength));
   1408  } else {
   1409    JS::BuildIdCharVector decodedBuildId;
   1410 
   1411    // buildIdLength is already checked against the length of current
   1412    // buildId.
   1413    if (!decodedBuildId.resize(buildIdLength)) {
   1414      ReportOutOfMemory(xdr->fc());
   1415      return xdr->fail(JS::TranscodeResult::Throw);
   1416    }
   1417 
   1418    MOZ_TRY(xdr->codeBytes(decodedBuildId.begin(), buildIdLength));
   1419 
   1420    // We do not provide binary compatibility with older scripts.
   1421    if (!mozilla::ArrayEqual(decodedBuildId.begin(), buildId.begin(),
   1422                             buildIdLength)) {
   1423      return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
   1424    }
   1425  }
   1426 
   1427  return Ok();
   1428 }
   1429 
   1430 XDRResult XDRStencilEncoder::codeStencil(
   1431    const RefPtr<ScriptSource>& source,
   1432    const frontend::CompilationStencil& stencil) {
   1433 #ifdef DEBUG
   1434  auto sanityCheck = mozilla::MakeScopeExit(
   1435      [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
   1436 #endif
   1437 
   1438  MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(this, stencil));
   1439 
   1440  MOZ_TRY(VersionCheck(this));
   1441 
   1442  uint32_t dummy = 0;
   1443  size_t lengthOffset = buf->cursor();
   1444  MOZ_TRY(codeUint32(&dummy));
   1445  size_t hashOffset = buf->cursor();
   1446  MOZ_TRY(codeUint32(&dummy));
   1447 
   1448  size_t contentOffset = buf->cursor();
   1449  MOZ_TRY(frontend::StencilXDR::codeSource(
   1450      this, nullptr, const_cast<RefPtr<ScriptSource>&>(source)));
   1451  MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(
   1452      this, const_cast<frontend::CompilationStencil&>(stencil)));
   1453  size_t endOffset = buf->cursor();
   1454 
   1455  if (endOffset > UINT32_MAX) {
   1456    ReportOutOfMemory(fc());
   1457    return fail(JS::TranscodeResult::Throw);
   1458  }
   1459 
   1460  uint32_t length = endOffset - contentOffset;
   1461  codeUint32At(&length, lengthOffset);
   1462 
   1463  const uint8_t* contentBegin = buf->bufferAt(contentOffset);
   1464  uint32_t hash = mozilla::HashBytes(contentBegin, length);
   1465  codeUint32At(&hash, hashOffset);
   1466 
   1467  return Ok();
   1468 }
   1469 
   1470 XDRResult XDRStencilEncoder::codeStencil(
   1471    const frontend::CompilationStencil& stencil) {
   1472  return codeStencil(stencil.source, stencil);
   1473 }
   1474 
   1475 static JS::TranscodeResult EncodeStencilImpl(
   1476    JS::FrontendContext* fc, const frontend::CompilationStencil* initial,
   1477    JS::TranscodeBuffer& buffer) {
   1478  XDRStencilEncoder encoder(fc, buffer);
   1479  XDRResult res = encoder.codeStencil(*initial);
   1480  if (res.isErr()) {
   1481    return res.unwrapErr();
   1482  }
   1483  return JS::TranscodeResult::Ok;
   1484 }
   1485 
   1486 JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
   1487                                      JS::TranscodeBuffer& buffer) {
   1488  AutoReportFrontendContext fc(cx);
   1489  return JS::EncodeStencil(&fc, stencil, buffer);
   1490 }
   1491 
   1492 JS::TranscodeResult JS::EncodeStencil(FrontendContext* fc, JS::Stencil* stencil,
   1493                                      JS::TranscodeBuffer& buffer) {
   1494  const CompilationStencil* initial;
   1495  UniquePtr<CompilationStencil> merged;
   1496  if (stencil->canLazilyParse()) {
   1497    merged.reset(stencil->getMerged(fc));
   1498    if (!merged) {
   1499      return TranscodeResult::Throw;
   1500    }
   1501    initial = merged.get();
   1502  } else {
   1503    initial = stencil->getInitial();
   1504  }
   1505 
   1506  return EncodeStencilImpl(fc, initial, buffer);
   1507 }
   1508 
   1509 JS::TranscodeResult js::EncodeStencil(JSContext* cx,
   1510                                      frontend::CompilationStencil* stencil,
   1511                                      JS::TranscodeBuffer& buffer) {
   1512  AutoReportFrontendContext fc(cx);
   1513  return EncodeStencilImpl(&fc, stencil, buffer);
   1514 }
   1515 
   1516 JS::TranscodeResult js::EncodeStencil(FrontendContext* fc,
   1517                                      frontend::CompilationStencil* stencil,
   1518                                      JS::TranscodeBuffer& buffer) {
   1519  return EncodeStencilImpl(fc, stencil, buffer);
   1520 }
   1521 
   1522 XDRResult XDRStencilDecoder::codeStencil(
   1523    const JS::ReadOnlyDecodeOptions& options,
   1524    frontend::CompilationStencil& stencil) {
   1525 #ifdef DEBUG
   1526  auto sanityCheck = mozilla::MakeScopeExit(
   1527      [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
   1528 #endif
   1529 
   1530  auto resetOptions = mozilla::MakeScopeExit([&] { options_ = nullptr; });
   1531  options_ = &options;
   1532 
   1533  MOZ_TRY(VersionCheck(this));
   1534 
   1535  uint32_t length;
   1536  MOZ_TRY(codeUint32(&length));
   1537 
   1538  uint32_t hash;
   1539  MOZ_TRY(codeUint32(&hash));
   1540 
   1541  const uint8_t* contentBegin;
   1542  MOZ_TRY(peekArray(length, &contentBegin));
   1543  uint32_t actualHash = mozilla::HashBytes(contentBegin, length);
   1544 
   1545  if (MOZ_UNLIKELY(actualHash != hash)) {
   1546    return fail(JS::TranscodeResult::Failure_BadDecode);
   1547  }
   1548 
   1549  MOZ_TRY(frontend::StencilXDR::codeSource(this, &options, stencil.source));
   1550  MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(this, stencil));
   1551 
   1552  return Ok();
   1553 }
   1554 
   1555 JS::TranscodeResult JS::DecodeStencil(JSContext* cx,
   1556                                      const JS::ReadOnlyDecodeOptions& options,
   1557                                      const JS::TranscodeRange& range,
   1558                                      JS::Stencil** stencilOut) {
   1559  AutoReportFrontendContext fc(cx);
   1560  return JS::DecodeStencil(&fc, options, range, stencilOut);
   1561 }
   1562 
   1563 JS::TranscodeResult JS::DecodeStencil(JS::FrontendContext* fc,
   1564                                      const JS::ReadOnlyDecodeOptions& options,
   1565                                      const JS::TranscodeRange& range,
   1566                                      JS::Stencil** stencilOut) {
   1567  RefPtr<CompilationStencil> stencil;
   1568  JS::TranscodeResult result =
   1569      js::DecodeStencil(fc, options, range, getter_AddRefs(stencil));
   1570  if (result != TranscodeResult::Ok) {
   1571    return result;
   1572  }
   1573 
   1574  RefPtr stencils =
   1575      fc->getAllocator()->new_<frontend::InitialStencilAndDelazifications>();
   1576  if (!stencils) {
   1577    return TranscodeResult::Throw;
   1578  }
   1579  if (!stencils->init(fc, stencil.get())) {
   1580    return TranscodeResult::Throw;
   1581  }
   1582  stencils.forget(stencilOut);
   1583  return TranscodeResult::Ok;
   1584 }
   1585 
   1586 JS::TranscodeResult js::DecodeStencil(
   1587    JS::FrontendContext* fc, const JS::ReadOnlyDecodeOptions& options,
   1588    const JS::TranscodeRange& range,
   1589    frontend::CompilationStencil** stencilOut) {
   1590  RefPtr<ScriptSource> source = fc->getAllocator()->new_<ScriptSource>();
   1591  if (!source) {
   1592    return JS::TranscodeResult::Throw;
   1593  }
   1594  RefPtr<CompilationStencil> stencil =
   1595      fc->getAllocator()->new_<CompilationStencil>(source);
   1596  if (!stencil) {
   1597    return JS::TranscodeResult::Throw;
   1598  }
   1599  XDRStencilDecoder decoder(fc, range);
   1600  XDRResult res = decoder.codeStencil(options, *stencil);
   1601  if (res.isErr()) {
   1602    return res.unwrapErr();
   1603  }
   1604  stencil.forget(stencilOut);
   1605  return JS::TranscodeResult::Ok;
   1606 }
   1607 
   1608 template /* static */ XDRResult StencilXDR::codeCompilationStencil(
   1609    XDRState<XDR_ENCODE>* xdr, CompilationStencil& stencil);
   1610 
   1611 template /* static */ XDRResult StencilXDR::codeCompilationStencil(
   1612    XDRState<XDR_DECODE>* xdr, CompilationStencil& stencil);
   1613 
   1614 /* static */ XDRResult StencilXDR::checkCompilationStencil(
   1615    XDRStencilEncoder* encoder, const CompilationStencil& stencil) {
   1616  if (stencil.hasAsmJS()) {
   1617    return encoder->fail(JS::TranscodeResult::Failure_AsmJSNotSupported);
   1618  }
   1619 
   1620  return Ok();
   1621 }
   1622 
   1623 /* static */ XDRResult StencilXDR::checkCompilationStencil(
   1624    const ExtensibleCompilationStencil& stencil) {
   1625  if (stencil.hasAsmJS()) {
   1626    return mozilla::Err(JS::TranscodeResult::Failure_AsmJSNotSupported);
   1627  }
   1628 
   1629  return Ok();
   1630 }