Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 50 kB image not shown  

Quelle  StencilXdr.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "frontend/StencilXdr.h"  // StencilXDR

#include "mozilla/ArrayUtils.h"             // mozilla::ArrayEqual
#include "mozilla/OperatorNewExtensions.h"  // mozilla::KnownNotNull
#include "mozilla/RefPtr.h"                 // RefPtr
#include "mozilla/ScopeExit.h"              // mozilla::MakeScopeExit
#include "mozilla/Try.h"                    // MOZ_TRY

#include <stddef.h>     // size_t
#include <stdint.h>     // uint8_t, uint16_t, uint32_t
#include <type_traits>  // std::has_unique_object_representations
#include <utility>      // std::forward

#include "ds/LifoAlloc.h"                 // LifoAlloc
#include "frontend/CompilationStencil.h"  // CompilationStencil, ExtensibleCompilationStencil
#include "frontend/FrontendContext.h"  // FrontendContext, AutoReportFrontendContext
#include "frontend/ScriptIndex.h"      // ScriptIndex
#include "js/CompileOptions.h"         // JS::ReadOnlyDecodeOptions
#include "js/experimental/JSStencil.h"  // ScriptIndex
#include "js/Transcoding.h"  // JS::TranscodeBuffer, JS::TranscodeRange, JS::TranscodeResult
#include "vm/JSScript.h"      // ScriptSource
#include "vm/Scope.h"         // SizeOfParserScopeData
#include "vm/StencilEnums.h"  // js::ImmutableScriptFlagsEnum

using namespace js;
using namespace js::frontend;

using mozilla::Utf8Unit;

template <typename NameType>
struct CanEncodeNameType {
  static constexpr bool value = false;
};

template <>
struct CanEncodeNameType<TaggedParserAtomIndex> {
  static constexpr bool value = true;
};

template <XDRMode mode, typename T, size_t N, class AP>
static XDRResult XDRVectorUninitialized(XDRState<mode>* xdr,
                                        Vector<T, N, AP>& vec,
                                        uint32_t& length) {
  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(vec.length() <= UINT32_MAX);
    length = vec.length();
  }

  MOZ_TRY(xdr->codeUint32(&length));

  if (mode == XDR_DECODE) {
    MOZ_ASSERT(vec.empty());
    if (!vec.resizeUninitialized(length)) {
      js::ReportOutOfMemory(xdr->fc());
      return xdr->fail(JS::TranscodeResult::Throw);
    }
  }

  return Ok();
}

template <XDRMode mode, typename T, size_t N, class AP>
static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
                                      Vector<T, N, AP>& vec, uint32_t length) {
  MOZ_ASSERT_IF(mode == XDR_ENCODE, length == vec.length());

  if (mode == XDR_DECODE) {
    MOZ_ASSERT(vec.empty());
    if (!vec.resize(length)) {
      js::ReportOutOfMemory(xdr->fc());
      return xdr->fail(JS::TranscodeResult::Throw);
    }
  }

  return Ok();
}

template <XDRMode mode, typename T, size_t N, class AP>
static XDRResult XDRVectorInitialized(XDRState<mode>* xdr,
                                      Vector<T, N, AP>& vec) {
  uint32_t length;
  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(vec.length() <= UINT32_MAX);
    length = vec.length();
  }

  MOZ_TRY(xdr->codeUint32(&length));

  return XDRVectorInitialized(xdr, vec, length);
}

template <XDRMode mode, typename T, size_t N, class AP>
static XDRResult XDRVectorContent(XDRState<mode>* xdr, Vector<T, N, AP>& vec) {
  static_assert(CanCopyDataToDisk<T>::value,
                "Vector content cannot be bulk-copied to disk.");

  uint32_t length;
  MOZ_TRY(XDRVectorUninitialized(xdr, vec, length));
  MOZ_TRY(xdr->codeBytes(vec.begin(), sizeof(T) * length));

  return Ok();
}

template <XDRMode mode, typename T>
static XDRResult XDRSpanInitialized(XDRState<mode>* xdr, LifoAlloc& alloc,
                                    mozilla::Span<T>& span, uint32_t size) {
  MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());

  if (mode == XDR_DECODE) {
    MOZ_ASSERT(span.empty());
    if (size > 0) {
      auto* p = alloc.template newArrayUninitialized<T>(size);
      if (!p) {
        js::ReportOutOfMemory(xdr->fc());
        return xdr->fail(JS::TranscodeResult::Throw);
      }
      span = mozilla::Span(p, size);

      for (size_t i = 0; i < size; i++) {
        new (mozilla::KnownNotNull, &span[i]) T();
      }
    }
  }

  return Ok();
}

template <XDRMode mode, typename T>
static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
                                mozilla::Span<T>& span, uint32_t size) {
  static_assert(CanCopyDataToDisk<T>::value,
                "Span cannot be bulk-copied to disk.");
  MOZ_ASSERT_IF(mode == XDR_ENCODE, size == span.size());

  if (size) {
    MOZ_TRY(xdr->align32());

    T* data;
    if constexpr (mode == XDR_ENCODE) {
      data = span.data();
      MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
    } else {
      const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
      if (options.borrowBuffer) {
        MOZ_TRY(xdr->borrowedData(&data, sizeof(T) * size));
      } else {
        data = alloc.template newArrayUninitialized<T>(size);
        if (!data) {
          js::ReportOutOfMemory(xdr->fc());
          return xdr->fail(JS::TranscodeResult::Throw);
        }
        MOZ_TRY(xdr->codeBytes(data, sizeof(T) * size));
      }
    }
    if (mode == XDR_DECODE) {
      span = mozilla::Span(data, size);
    }
  }

  return Ok();
}

template <XDRMode mode, typename T>
static XDRResult XDRSpanContent(XDRState<mode>* xdr, LifoAlloc& alloc,
                                mozilla::Span<T>& span) {
  uint32_t size;
  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(span.size() <= UINT32_MAX);
    size = span.size();
  }

  MOZ_TRY(xdr->codeUint32(&size));

  return XDRSpanContent(xdr, alloc, span, size);
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeBigInt(XDRState<mode>* xdr,
                                              LifoAlloc& alloc,
                                              BigIntStencil& stencil) {
  uint32_t size;
  if (mode == XDR_ENCODE) {
    size = stencil.bigInt_.match(
        [](mozilla::Span<char16_t> source) { return source.size(); },
        [](int64_t) { return size_t(0); });
  }
  MOZ_TRY(xdr->codeUint32(&size));

  // Zero-length size indicates inline storage for int64-sized BigInts.
  if (size == 0) {
    uint64_t num;
    if (mode == XDR_ENCODE) {
      num = static_cast<uint64_t>(stencil.bigInt_.as<int64_t>());
    }
    MOZ_TRY(xdr->codeUint64(&num));
    if (mode == XDR_DECODE) {
      stencil.bigInt_.as<int64_t>() = static_cast<int64_t>(num);
    }
    return Ok();
  }

  return XDRSpanContent(xdr, alloc, stencil.source(), size);
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeObjLiteral(XDRState<mode>* xdr,
                                                  LifoAlloc& alloc,
                                                  ObjLiteralStencil& stencil) {
  uint8_t kindAndFlags = 0;

  if (mode == XDR_ENCODE) {
    static_assert(sizeof(ObjLiteralKindAndFlags) == sizeof(uint8_t));
    kindAndFlags = stencil.kindAndFlags_.toRaw();
  }
  MOZ_TRY(xdr->codeUint8(&kindAndFlags));
  if (mode == XDR_DECODE) {
    stencil.kindAndFlags_.setRaw(kindAndFlags);
  }

  MOZ_TRY(xdr->codeUint32(&stencil.propertyCount_));

  MOZ_TRY(XDRSpanContent(xdr, alloc, stencil.code_));

  return Ok();
}

template <typename ScopeT>
/* static */ void AssertScopeSpecificDataIsEncodable() {
  using ScopeDataT = typename ScopeT::ParserData;

  static_assert(CanEncodeNameType<typename ScopeDataT::NameType>::value);
  static_assert(CanCopyDataToDisk<ScopeDataT>::value,
                "ScopeData cannot be bulk-copied to disk");
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeScopeData(
    XDRState<mode>* xdr, LifoAlloc& alloc, ScopeStencil& stencil,
    BaseParserScopeData*& baseScopeData) {
  // WasmInstanceScope & WasmFunctionScope should not appear in stencils.
  MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmInstance);
  MOZ_ASSERT(stencil.kind_ != ScopeKind::WasmFunction);
  if (stencil.kind_ == ScopeKind::With) {
    return Ok();
  }

  MOZ_TRY(xdr->align32());

  static_assert(offsetof(BaseParserScopeData, length) == 0,
                "length should be the first field");
  uint32_t length;
  if (mode == XDR_ENCODE) {
    length = baseScopeData->length;
  } else {
    MOZ_TRY(xdr->peekUint32(&length));
  }

  AssertScopeSpecificDataIsEncodable<FunctionScope>();
  AssertScopeSpecificDataIsEncodable<VarScope>();
  AssertScopeSpecificDataIsEncodable<LexicalScope>();
  AssertScopeSpecificDataIsEncodable<ClassBodyScope>();
  AssertScopeSpecificDataIsEncodable<EvalScope>();
  AssertScopeSpecificDataIsEncodable<GlobalScope>();
  AssertScopeSpecificDataIsEncodable<ModuleScope>();

  // In both decoding and encoding, stencil.kind_ is now known, and
  // can be assumed.  This allows the encoding to write out the bytes
  // for the specialized scope-data type without needing to encode
  // a distinguishing prefix.
  uint32_t totalLength = SizeOfParserScopeData(stencil.kind_, length);
  if constexpr (mode == XDR_ENCODE) {
    MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
  } else {
    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    if (options.borrowBuffer) {
      MOZ_TRY(xdr->borrowedData(&baseScopeData, totalLength));
    } else {
      baseScopeData =
          reinterpret_cast<BaseParserScopeData*>(alloc.alloc(totalLength));
      if (!baseScopeData) {
        js::ReportOutOfMemory(xdr->fc());
        return xdr->fail(JS::TranscodeResult::Throw);
      }
      MOZ_TRY(xdr->codeBytes(baseScopeData, totalLength));
    }
  }

  return Ok();
}

template <XDRMode mode>
/* static */
XDRResult StencilXDR::codeSharedData(XDRState<mode>* xdr,
                                     RefPtr<SharedImmutableScriptData>& sisd) {
  static_assert(frontend::CanCopyDataToDisk<ImmutableScriptData>::value,
                "ImmutableScriptData cannot be bulk-copied to disk");
  static_assert(frontend::CanCopyDataToDisk<jsbytecode>::value,
                "jsbytecode cannot be bulk-copied to disk");
  static_assert(frontend::CanCopyDataToDisk<SrcNote>::value,
                "SrcNote cannot be bulk-copied to disk");
  static_assert(frontend::CanCopyDataToDisk<ScopeNote>::value,
                "ScopeNote cannot be bulk-copied to disk");
  static_assert(frontend::CanCopyDataToDisk<TryNote>::value,
                "TryNote cannot be bulk-copied to disk");

  uint32_t size;
  uint32_t hash;
  if (mode == XDR_ENCODE) {
    if (sisd) {
      size = sisd->immutableDataLength();
      hash = sisd->hash();
    } else {
      size = 0;
      hash = 0;
    }
  }
  MOZ_TRY(xdr->codeUint32(&size));

  // A size of zero is used when the `sisd` is nullptr. This can occur for
  // certain outer container modes. In this case, there is no further
  // transcoding to do.
  if (!size) {
    MOZ_ASSERT(!sisd);
    return Ok();
  }

  MOZ_TRY(xdr->align32());
  static_assert(alignof(ImmutableScriptData) <= alignof(uint32_t));

  MOZ_TRY(xdr->codeUint32(&hash));

  if constexpr (mode == XDR_ENCODE) {
    uint8_t* data = const_cast<uint8_t*>(sisd->get()->immutableData().data());
    MOZ_ASSERT(data == reinterpret_cast<const uint8_t*>(sisd->get()),
               "Decode below relies on the data placement");
    MOZ_TRY(xdr->codeBytes(data, size));
  } else {
    sisd = SharedImmutableScriptData::create(xdr->fc());
    if (!sisd) {
      return xdr->fail(JS::TranscodeResult::Throw);
    }

    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    if (options.usePinnedBytecode) {
      MOZ_ASSERT(options.borrowBuffer);
      ImmutableScriptData* isd;
      MOZ_TRY(xdr->borrowedData(&isd, size));
      sisd->setExternal(isd, hash);
    } else {
      auto isd = ImmutableScriptData::new_(xdr->fc(), size);
      if (!isd) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
      uint8_t* data = reinterpret_cast<uint8_t*>(isd.get());
      MOZ_TRY(xdr->codeBytes(data, size));
      sisd->setOwn(std::move(isd), hash);
    }

    if (!sisd->get()->validateLayout(size)) {
      MOZ_ASSERT(false"Bad ImmutableScriptData");
      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
    }
  }

  if (mode == XDR_DECODE) {
    if (!SharedImmutableScriptData::shareScriptData(xdr->fc(), sisd)) {
      return xdr->fail(JS::TranscodeResult::Throw);
    }
  }

  return Ok();
}

// Called from js::XDRScript.
template /* static */ XDRResult StencilXDR::codeSharedData(
    XDRState<XDR_ENCODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);
template /* static */ XDRResult StencilXDR::codeSharedData(
    XDRState<XDR_DECODE>* xdr, RefPtr<SharedImmutableScriptData>& sisd);

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeSharedDataContainer(
    XDRState<mode>* xdr, SharedDataContainer& sharedData) {
  if (mode == XDR_ENCODE) {
    if (sharedData.isBorrow()) {
      return codeSharedDataContainer(xdr, *sharedData.asBorrow());
    }
  }

  enum Kind {
    Single,
    Vector,
    Map,
  };

  Kind kind;
  if (mode == XDR_ENCODE) {
    if (sharedData.isSingle()) {
      kind = Kind::Single;
    } else if (sharedData.isVector()) {
      kind = Kind::Vector;
    } else {
      MOZ_ASSERT(sharedData.isMap());
      kind = Kind::Map;
    }
  }
  MOZ_TRY(xdr->codeEnum32(&kind));

  switch (kind) {
    case Kind::Single: {
      RefPtr<SharedImmutableScriptData> ref;
      if (mode == XDR_ENCODE) {
        ref = sharedData.asSingle();
      }
      MOZ_TRY(codeSharedData<mode>(xdr, ref));
      if (mode == XDR_DECODE) {
        sharedData.setSingle(ref.forget());
      }
      break;
    }

    case Kind::Vector: {
      if (mode == XDR_DECODE) {
        if (!sharedData.initVector(xdr->fc())) {
          return xdr->fail(JS::TranscodeResult::Throw);
        }
      }
      auto& vec = *sharedData.asVector();
      MOZ_TRY(XDRVectorInitialized(xdr, vec));
      for (auto& entry : vec) {
        // NOTE: There can be nullptr, even if we don't perform syntax parsing,
        //       because of constant folding.
        MOZ_TRY(codeSharedData<mode>(xdr, entry));
      }
      break;
    }

    case Kind::Map: {
      if (mode == XDR_DECODE) {
        if (!sharedData.initMap(xdr->fc())) {
          return xdr->fail(JS::TranscodeResult::Throw);
        }
      }
      auto& map = *sharedData.asMap();
      uint32_t count;
      if (mode == XDR_ENCODE) {
        count = map.count();
      }
      MOZ_TRY(xdr->codeUint32(&count));
      if (mode == XDR_DECODE) {
        if (!map.reserve(count)) {
          js::ReportOutOfMemory(xdr->fc());
          return xdr->fail(JS::TranscodeResult::Throw);
        }
      }

      if (mode == XDR_ENCODE) {
        for (auto iter = map.iter(); !iter.done(); iter.next()) {
          uint32_t index = iter.get().key().index;
          auto& data = iter.get().value();
          MOZ_TRY(xdr->codeUint32(&index));
          MOZ_TRY(codeSharedData<mode>(xdr, data));
        }
      } else {
        for (uint32_t i = 0; i < count; i++) {
          ScriptIndex index;
          MOZ_TRY(xdr->codeUint32(&index.index));

          RefPtr<SharedImmutableScriptData> data;
          MOZ_TRY(codeSharedData<mode>(xdr, data));

          if (!map.putNew(index, data)) {
            js::ReportOutOfMemory(xdr->fc());
            return xdr->fail(JS::TranscodeResult::Throw);
          }
        }
      }

      break;
    }

    default:
      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
  }

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeParserAtom(XDRState<mode>* xdr,
                                                  LifoAlloc& alloc,
                                                  ParserAtom** atomp) {
  static_assert(CanCopyDataToDisk<ParserAtom>::value,
                "ParserAtom cannot be bulk-copied to disk.");

  MOZ_TRY(xdr->align32());

  const ParserAtom* header;
  if (mode == XDR_ENCODE) {
    header = *atomp;
  } else {
    MOZ_TRY(xdr->peekData(&header));
  }

  const uint32_t CharSize =
      header->hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
  uint32_t totalLength = sizeof(ParserAtom) + (CharSize * header->length());

  if constexpr (mode == XDR_ENCODE) {
    MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
  } else {
    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    if (options.borrowBuffer) {
      MOZ_TRY(xdr->borrowedData(atomp, totalLength));
    } else {
      *atomp = reinterpret_cast<ParserAtom*>(alloc.alloc(totalLength));
      if (!*atomp) {
        js::ReportOutOfMemory(xdr->fc());
        return xdr->fail(JS::TranscodeResult::Throw);
      }
      MOZ_TRY(xdr->codeBytes(*atomp, totalLength));
    }
  }

  return Ok();
}

template <XDRMode mode>
static XDRResult XDRAtomCount(XDRState<mode>* xdr, uint32_t* atomCount) {
  return xdr->codeUint32(atomCount);
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeParserAtomSpan(
    XDRState<mode>* xdr, LifoAlloc& alloc, ParserAtomSpan& parserAtomData) {
  if (mode == XDR_ENCODE) {
    uint32_t atomVectorLength = parserAtomData.size();
    MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));

    uint32_t atomCount = 0;
    for (const auto& entry : parserAtomData) {
      if (!entry) {
        continue;
      }
      if (entry->isUsedByStencil()) {
        atomCount++;
      }
    }
    MOZ_TRY(XDRAtomCount(xdr, &atomCount));

    for (uint32_t i = 0; i < atomVectorLength; i++) {
      auto& entry = parserAtomData[i];
      if (!entry) {
        continue;
      }
      if (entry->isUsedByStencil()) {
        MOZ_TRY(xdr->codeUint32(&i));
        MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
      }
    }

    return Ok();
  }

  uint32_t atomVectorLength;
  MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));

  frontend::ParserAtomSpanBuilder builder(parserAtomData);
  if (!builder.allocate(xdr->fc(), alloc, atomVectorLength)) {
    return xdr->fail(JS::TranscodeResult::Throw);
  }

  uint32_t atomCount;
  MOZ_TRY(XDRAtomCount(xdr, &atomCount));

  for (uint32_t i = 0; i < atomCount; i++) {
    frontend::ParserAtom* entry = nullptr;
    uint32_t index;
    MOZ_TRY(xdr->codeUint32(&index));
    MOZ_TRY(codeParserAtom(xdr, alloc, &entry));
    if (mode == XDR_DECODE) {
      if (index >= atomVectorLength) {
        return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
      }
    }
    builder.set(frontend::ParserAtomIndex(index), entry);
  }

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeModuleRequest(
    XDRState<mode>* xdr, StencilModuleRequest& stencil) {
  MOZ_TRY(xdr->codeUint32(stencil.specifier.rawDataRef()));
  MOZ_TRY(xdr->codeUint32(stencil.firstUnsupportedAttributeKey.rawDataRef()));
  MOZ_TRY(XDRVectorContent(xdr, stencil.attributes));

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeModuleRequestVector(
    XDRState<mode>* xdr, StencilModuleMetadata::RequestVector& vector) {
  MOZ_TRY(XDRVectorInitialized(xdr, vector));

  for (auto& entry : vector) {
    MOZ_TRY(codeModuleRequest<mode>(xdr, entry));
  }

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeModuleEntry(
    XDRState<mode>* xdr, StencilModuleEntry& stencil) {
  MOZ_TRY(xdr->codeUint32(&stencil.moduleRequest));
  MOZ_TRY(xdr->codeUint32(stencil.localName.rawDataRef()));
  MOZ_TRY(xdr->codeUint32(stencil.importName.rawDataRef()));
  MOZ_TRY(xdr->codeUint32(stencil.exportName.rawDataRef()));
  MOZ_TRY(xdr->codeUint32(&stencil.lineno));
  MOZ_TRY(xdr->codeUint32(stencil.column.addressOfValueForTranscode()));

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeModuleEntryVector(
    XDRState<mode>* xdr, StencilModuleMetadata::EntryVector& vector) {
  MOZ_TRY(XDRVectorInitialized(xdr, vector));

  for (auto& entry : vector) {
    MOZ_TRY(codeModuleEntry<mode>(xdr, entry));
  }

  return Ok();
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeModuleMetadata(
    XDRState<mode>* xdr, StencilModuleMetadata& stencil) {
  MOZ_TRY(codeModuleRequestVector(xdr, stencil.moduleRequests));
  MOZ_TRY(codeModuleEntryVector(xdr, stencil.requestedModules));
  MOZ_TRY(codeModuleEntryVector(xdr, stencil.importEntries));
  MOZ_TRY(codeModuleEntryVector(xdr, stencil.localExportEntries));
  MOZ_TRY(codeModuleEntryVector(xdr, stencil.indirectExportEntries));
  MOZ_TRY(codeModuleEntryVector(xdr, stencil.starExportEntries));
  MOZ_TRY(XDRVectorContent(xdr, stencil.functionDecls));

  uint8_t isAsync = 0;
  if (mode == XDR_ENCODE) {
    if (stencil.isAsync) {
      isAsync = stencil.isAsync ? 1 : 0;
    }
  }

  MOZ_TRY(xdr->codeUint8(&isAsync));

  if (mode == XDR_DECODE) {
    stencil.isAsync = isAsync == 1;
  }

  return Ok();
}

template <XDRMode mode>
XDRResult XDRCompilationStencilSpanSize(
    XDRState<mode>* xdr, uint32_t* scriptSize, uint32_t* gcThingSize,
    uint32_t* scopeSize, uint32_t* scriptExtraSize, uint32_t* regExpSize,
    uint32_t* bigIntSize, uint32_t* objLiteralSize) {
  // Compress the series of span sizes, to avoid consuming extra space for
  // unused/small span sizes.
  // There will be align32 shortly after this section, so try to make the
  // padding smaller.

  enum XDRSpanSizeKind {
    // All of the size values fit in 1 byte each. The entire section takes 7
    // bytes, and expect no padding.
    All8Kind,

    // Other cases. All of the size values fit in 4 bytes each. Expect 3 bytes
    // padding for `sizeKind`.
    All32Kind,
  };

  uint8_t sizeKind = All32Kind;
  if (mode == XDR_ENCODE) {
    uint32_t mask = (*scriptSize) | (*gcThingSize) | (*scopeSize) |
                    (*scriptExtraSize) | (*regExpSize) | (*bigIntSize) |
                    (*objLiteralSize);

    if (mask <= 0xff) {
      sizeKind = All8Kind;
    }
  }
  MOZ_TRY(xdr->codeUint8(&sizeKind));

  if (sizeKind == All32Kind) {
    MOZ_TRY(xdr->codeUint32(scriptSize));
    MOZ_TRY(xdr->codeUint32(gcThingSize));
    MOZ_TRY(xdr->codeUint32(scopeSize));
    MOZ_TRY(xdr->codeUint32(scriptExtraSize));
    MOZ_TRY(xdr->codeUint32(regExpSize));
    MOZ_TRY(xdr->codeUint32(bigIntSize));
    MOZ_TRY(xdr->codeUint32(objLiteralSize));
  } else {
    uint8_t scriptSize8 = 0;
    uint8_t gcThingSize8 = 0;
    uint8_t scopeSize8 = 0;
    uint8_t scriptExtraSize8 = 0;
    uint8_t regExpSize8 = 0;
    uint8_t bigIntSize8 = 0;
    uint8_t objLiteralSize8 = 0;

    if (mode == XDR_ENCODE) {
      scriptSize8 = uint8_t(*scriptSize);
      gcThingSize8 = uint8_t(*gcThingSize);
      scopeSize8 = uint8_t(*scopeSize);
      scriptExtraSize8 = uint8_t(*scriptExtraSize);
      regExpSize8 = uint8_t(*regExpSize);
      bigIntSize8 = uint8_t(*bigIntSize);
      objLiteralSize8 = uint8_t(*objLiteralSize);
    }

    MOZ_TRY(xdr->codeUint8(&scriptSize8));
    MOZ_TRY(xdr->codeUint8(&gcThingSize8));
    MOZ_TRY(xdr->codeUint8(&scopeSize8));
    MOZ_TRY(xdr->codeUint8(&scriptExtraSize8));
    MOZ_TRY(xdr->codeUint8(®ExpSize8));
    MOZ_TRY(xdr->codeUint8(&bigIntSize8));
    MOZ_TRY(xdr->codeUint8(&objLiteralSize8));

    if (mode == XDR_DECODE) {
      *scriptSize = scriptSize8;
      *gcThingSize = gcThingSize8;
      *scopeSize = scopeSize8;
      *scriptExtraSize = scriptExtraSize8;
      *regExpSize = regExpSize8;
      *bigIntSize = bigIntSize8;
      *objLiteralSize = objLiteralSize8;
    }
  }

  return Ok();
}

// Marker between each section inside CompilationStencil.
//
// These values should meet the following requirement:
//   * No same value (differ more than single bit flip)
//   * Bit pattern that won't frequently appear inside other XDR data
//
// Currently they're randomly chosen prime numbers that doesn't have same
// byte pattern.
enum class SectionMarker : uint32_t {
  ParserAtomData = 0xD9C098D3,
  ScopeData = 0x892C25EF,
  ScopeNames = 0x638C4FB3,
  RegExpData = 0xB030C2AF,
  BigIntData = 0x4B24F449,
  ObjLiteralData = 0x9AFAAE45,
  SharedData = 0xAAD52687,
  GCThingData = 0x1BD8F533,
  ScriptData = 0x840458FF,
  ScriptExtra = 0xA90E489D,
  ModuleMetadata = 0x94FDCE6D,
  End = 0x16DDA135,
};

template <XDRMode mode>
static XDRResult CodeMarker(XDRState<mode>* xdr, SectionMarker marker) {
  return xdr->codeMarker(uint32_t(marker));
}

template <XDRMode mode>
/* static */ XDRResult StencilXDR::codeCompilationStencil(
    XDRState<mode>* xdr, CompilationStencil& stencil) {
  MOZ_ASSERT(!stencil.asmJS);

  if constexpr (mode == XDR_DECODE) {
    const auto& options = static_cast<XDRStencilDecoder*>(xdr)->options();
    if (options.borrowBuffer) {
      stencil.storageType = CompilationStencil::StorageType::Borrowed;
    } else {
      stencil.storageType = CompilationStencil::StorageType::Owned;
    }
  }

  MOZ_TRY(CodeMarker(xdr, SectionMarker::ParserAtomData));
  MOZ_TRY(codeParserAtomSpan(xdr, stencil.alloc, stencil.parserAtomData));

  uint8_t canLazilyParse = 0;

  if (mode == XDR_ENCODE) {
    canLazilyParse = stencil.canLazilyParse;
  }
  MOZ_TRY(xdr->codeUint8(&canLazilyParse));
  if (mode == XDR_DECODE) {
    stencil.canLazilyParse = canLazilyParse;
  }

  MOZ_TRY(xdr->codeUint32(&stencil.functionKey));

  uint32_t scriptSize, gcThingSize, scopeSize, scriptExtraSize;
  uint32_t regExpSize, bigIntSize, objLiteralSize;
  if (mode == XDR_ENCODE) {
    scriptSize = stencil.scriptData.size();
    gcThingSize = stencil.gcThingData.size();
    scopeSize = stencil.scopeData.size();
    MOZ_ASSERT(scopeSize == stencil.scopeNames.size());

    scriptExtraSize = stencil.scriptExtra.size();

    regExpSize = stencil.regExpData.size();
    bigIntSize = stencil.bigIntData.size();
    objLiteralSize = stencil.objLiteralData.size();
  }
  MOZ_TRY(XDRCompilationStencilSpanSize(
      xdr, &scriptSize, &gcThingSize, &scopeSize, &scriptExtraSize, ®ExpSize,
      &bigIntSize, &objLiteralSize));

  // All of the vector-indexed data elements referenced by the
  // main script tree must be materialized first.

  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeData));
  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scopeData, scopeSize));

  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScopeNames));
  MOZ_TRY(
      XDRSpanInitialized(xdr, stencil.alloc, stencil.scopeNames, scopeSize));
  MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
  for (uint32_t i = 0; i < scopeSize; i++) {
    MOZ_TRY(codeScopeData(xdr, stencil.alloc, stencil.scopeData[i],
                          stencil.scopeNames[i]));
  }

  MOZ_TRY(CodeMarker(xdr, SectionMarker::RegExpData));
  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.regExpData, regExpSize));

  MOZ_TRY(CodeMarker(xdr, SectionMarker::BigIntData));
  MOZ_TRY(
      XDRSpanInitialized(xdr, stencil.alloc, stencil.bigIntData, bigIntSize));
  for (auto& entry : stencil.bigIntData) {
    MOZ_TRY(codeBigInt(xdr, stencil.alloc, entry));
  }

  MOZ_TRY(CodeMarker(xdr, SectionMarker::ObjLiteralData));
  MOZ_TRY(XDRSpanInitialized(xdr, stencil.alloc, stencil.objLiteralData,
                             objLiteralSize));
  for (auto& entry : stencil.objLiteralData) {
    MOZ_TRY(codeObjLiteral(xdr, stencil.alloc, entry));
  }

  MOZ_TRY(CodeMarker(xdr, SectionMarker::SharedData));
  MOZ_TRY(codeSharedDataContainer(xdr, stencil.sharedData));

  MOZ_TRY(CodeMarker(xdr, SectionMarker::GCThingData));
  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.gcThingData, gcThingSize));

  // Now serialize the vector of ScriptStencils.
  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptData));
  MOZ_TRY(XDRSpanContent(xdr, stencil.alloc, stencil.scriptData, scriptSize));

  MOZ_TRY(CodeMarker(xdr, SectionMarker::ScriptExtra));
  MOZ_TRY(
      XDRSpanContent(xdr, stencil.alloc, stencil.scriptExtra, scriptExtraSize));

  // We don't support coding non-initial CompilationStencil.
  MOZ_ASSERT(stencil.isInitialStencil());

  if (stencil.scriptExtra[CompilationStencil::TopLevelIndex].isModule()) {
    if (mode == XDR_DECODE) {
      stencil.moduleMetadata =
          xdr->fc()->getAllocator()->template new_<StencilModuleMetadata>();
      if (!stencil.moduleMetadata) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
    }

    MOZ_TRY(CodeMarker(xdr, SectionMarker::ModuleMetadata));
    MOZ_TRY(codeModuleMetadata(xdr, *stencil.moduleMetadata));

    // codeModuleMetadata doesn't guarantee alignment.
    MOZ_TRY(xdr->align32());
  }

  MOZ_TRY(CodeMarker(xdr, SectionMarker::End));

  // The result should be aligned.
  //
  // NOTE:
  // If the top-level isn't a module, ScriptData/ScriptExtra sections
  // guarantee the alignment because there should be at least 1 item,
  // and XDRSpanContent adds alignment before span content, and the struct size
  // should also be aligned.
  static_assert(sizeof(ScriptStencil) % 4 == 0,
                "size of ScriptStencil should be aligned");
  static_assert(sizeof(ScriptStencilExtra) % 4 == 0,
                "size of ScriptStencilExtra should be aligned");
  MOZ_RELEASE_ASSERT(xdr->isAligned32());

  return Ok();
}

template <typename Unit>
struct UnretrievableSourceDecoder {
  XDRState<XDR_DECODE>* const xdr_;
  ScriptSource* const scriptSource_;
  const uint32_t uncompressedLength_;

 public:
  UnretrievableSourceDecoder(XDRState<XDR_DECODE>* xdr,
                             ScriptSource* scriptSource,
                             uint32_t uncompressedLength)
      : xdr_(xdr),
        scriptSource_(scriptSource),
        uncompressedLength_(uncompressedLength) {}

  XDRResult decode() {
    auto sourceUnits = xdr_->fc()->getAllocator()->make_pod_array<Unit>(
        std::max<size_t>(uncompressedLength_, 1));
    if (!sourceUnits) {
      return xdr_->fail(JS::TranscodeResult::Throw);
    }

    MOZ_TRY(xdr_->codeChars(sourceUnits.get(), uncompressedLength_));

    if (!scriptSource_->initializeUnretrievableUncompressedSource(
            xdr_->fc(), std::move(sourceUnits), uncompressedLength_)) {
      return xdr_->fail(JS::TranscodeResult::Throw);
    }

    return Ok();
  }
};

template <>
XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_DECODE>(
    XDRState<XDR_DECODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
    uint32_t uncompressedLength) {
  MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);

  if (sourceCharSize == 1) {
    UnretrievableSourceDecoder<Utf8Unit> decoder(xdr, ss, uncompressedLength);
    return decoder.decode();
  }

  UnretrievableSourceDecoder<char16_t> decoder(xdr, ss, uncompressedLength);
  return decoder.decode();
}

template <typename Unit>
struct UnretrievableSourceEncoder {
  XDRState<XDR_ENCODE>* const xdr_;
  ScriptSource* const source_;
  const uint32_t uncompressedLength_;

  UnretrievableSourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source,
                             uint32_t uncompressedLength)
      : xdr_(xdr), source_(source), uncompressedLength_(uncompressedLength) {}

  XDRResult encode() {
    Unit* sourceUnits =
        const_cast<Unit*>(source_->uncompressedData<Unit>()->units());

    return xdr_->codeChars(sourceUnits, uncompressedLength_);
  }
};

template <>
/* static */
XDRResult StencilXDR::codeSourceUnretrievableUncompressed<XDR_ENCODE>(
    XDRState<XDR_ENCODE>* xdr, ScriptSource* ss, uint8_t sourceCharSize,
    uint32_t uncompressedLength) {
  MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);

  if (sourceCharSize == 1) {
    UnretrievableSourceEncoder<Utf8Unit> encoder(xdr, ss, uncompressedLength);
    return encoder.encode();
  }

  UnretrievableSourceEncoder<char16_t> encoder(xdr, ss, uncompressedLength);
  return encoder.encode();
}

template <typename Unit, XDRMode mode>
/* static */
XDRResult StencilXDR::codeSourceUncompressedData(XDRState<mode>* const xdr,
                                                 ScriptSource* const ss) {
  static_assert(
      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
      "should handle UTF-8 and UTF-16");

  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(ss->isUncompressed<Unit>());
  } else {
    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
  }

  uint32_t uncompressedLength;
  if (mode == XDR_ENCODE) {
    uncompressedLength = ss->uncompressedData<Unit>()->length();
  }
  MOZ_TRY(xdr->codeUint32(&uncompressedLength));

  return codeSourceUnretrievableUncompressed(xdr, ss, sizeof(Unit),
                                             uncompressedLength);
}

template <typename Unit, XDRMode mode>
/* static */
XDRResult StencilXDR::codeSourceCompressedData(XDRState<mode>* const xdr,
                                               ScriptSource* const ss) {
  static_assert(
      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
      "should handle UTF-8 and UTF-16");

  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(ss->isCompressed<Unit>());
  } else {
    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
  }

  uint32_t uncompressedLength;
  if (mode == XDR_ENCODE) {
    uncompressedLength =
        ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
            .uncompressedLength;
  }
  MOZ_TRY(xdr->codeUint32(&uncompressedLength));

  uint32_t compressedLength;
  if (mode == XDR_ENCODE) {
    compressedLength =
        ss->data.as<ScriptSource::Compressed<Unit, SourceRetrievable::No>>()
            .raw.length();
  }
  MOZ_TRY(xdr->codeUint32(&compressedLength));

  if (mode == XDR_DECODE) {
    // Compressed data is always single-byte chars.
    auto bytes = xdr->fc()->getAllocator()->template make_pod_array<char>(
        compressedLength);
    if (!bytes) {
      return xdr->fail(JS::TranscodeResult::Throw);
    }
    MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));

    if (!ss->initializeWithUnretrievableCompressedSource<Unit>(
            xdr->fc(), std::move(bytes), compressedLength,
            uncompressedLength)) {
      return xdr->fail(JS::TranscodeResult::Throw);
    }
  } else {
    void* bytes = const_cast<char*>(ss->compressedData<Unit>()->raw.chars());
    MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
  }

  return Ok();
}

template <typename Unit,
          template <typename U, SourceRetrievable CanRetrieve> class Data,
          XDRMode mode>
/* static */
void StencilXDR::codeSourceRetrievable(ScriptSource* const ss) {
  static_assert(
      std::is_same_v<Unit, Utf8Unit> || std::is_same_v<Unit, char16_t>,
      "should handle UTF-8 and UTF-16");

  if (mode == XDR_ENCODE) {
    MOZ_ASSERT((ss->data.is<Data<Unit, SourceRetrievable::Yes>>()));
  } else {
    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
    ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
  }
}

template <typename Unit, XDRMode mode>
/* static */
void StencilXDR::codeSourceRetrievableData(ScriptSource* ss) {
  // There's nothing to code for retrievable data.  Just be sure to set
  // retrievable data when decoding.
  if (mode == XDR_ENCODE) {
    MOZ_ASSERT(ss->data.is<ScriptSource::Retrievable<Unit>>());
  } else {
    MOZ_ASSERT(ss->data.is<ScriptSource::Missing>());
    ss->data = ScriptSource::SourceType(ScriptSource::Retrievable<Unit>());
  }
}

template <XDRMode mode>
/* static */
XDRResult StencilXDR::codeSourceData(XDRState<mode>* const xdr,
                                     ScriptSource* const ss) {
  // The order here corresponds to the type order in |ScriptSource::SourceType|
  // so number->internal Variant tag is a no-op.
  enum class DataType {
    CompressedUtf8Retrievable,
    UncompressedUtf8Retrievable,
    CompressedUtf8NotRetrievable,
    UncompressedUtf8NotRetrievable,
    CompressedUtf16Retrievable,
    UncompressedUtf16Retrievable,
    CompressedUtf16NotRetrievable,
    UncompressedUtf16NotRetrievable,
    RetrievableUtf8,
    RetrievableUtf16,
    Missing,
  };

  DataType tag;
  {
    // This is terrible, but we can't do better.  When |mode == XDR_DECODE| we
    // don't have a |ScriptSource::data| |Variant| to match -- the entire XDR
    // idiom for tagged unions depends on coding a tag-number, then the
    // corresponding tagged data.  So we must manually define a tag-enum, code
    // it, then switch on it (and ignore the |Variant::match| API).
    class XDRDataTag {
     public:
      DataType operator()(
          const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::Yes>&) {
        return DataType::CompressedUtf8Retrievable;
      }
      DataType operator()(
          const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::Yes>&) {
        return DataType::UncompressedUtf8Retrievable;
      }
      DataType operator()(
          const ScriptSource::Compressed<Utf8Unit, SourceRetrievable::No>&) {
        return DataType::CompressedUtf8NotRetrievable;
      }
      DataType operator()(
          const ScriptSource::Uncompressed<Utf8Unit, SourceRetrievable::No>&) {
        return DataType::UncompressedUtf8NotRetrievable;
      }
      DataType operator()(
          const ScriptSource::Compressed<char16_t, SourceRetrievable::Yes>&) {
        return DataType::CompressedUtf16Retrievable;
      }
      DataType operator()(
          const ScriptSource::Uncompressed<char16_t, SourceRetrievable::Yes>&) {
        return DataType::UncompressedUtf16Retrievable;
      }
      DataType operator()(
          const ScriptSource::Compressed<char16_t, SourceRetrievable::No>&) {
        return DataType::CompressedUtf16NotRetrievable;
      }
      DataType operator()(
          const ScriptSource::Uncompressed<char16_t, SourceRetrievable::No>&) {
        return DataType::UncompressedUtf16NotRetrievable;
      }
      DataType operator()(const ScriptSource::Retrievable<Utf8Unit>&) {
        return DataType::RetrievableUtf8;
      }
      DataType operator()(const ScriptSource::Retrievable<char16_t>&) {
        return DataType::RetrievableUtf16;
      }
      DataType operator()(const ScriptSource::Missing&) {
        return DataType::Missing;
      }
    };

    uint8_t type;
    if (mode == XDR_ENCODE) {
      type = static_cast<uint8_t>(ss->data.match(XDRDataTag()));
    }
    MOZ_TRY(xdr->codeUint8(&type));

    if (type > static_cast<uint8_t>(DataType::Missing)) {
      // Fail in debug, but only soft-fail in release, if the type is invalid.
      MOZ_ASSERT_UNREACHABLE("bad tag");
      return xdr->fail(JS::TranscodeResult::Failure_BadDecode);
    }

    tag = static_cast<DataType>(type);
  }

  switch (tag) {
    case DataType::CompressedUtf8Retrievable:
      codeSourceRetrievable<Utf8Unit, ScriptSource::Compressed, mode>(ss);
      return Ok();

    case DataType::CompressedUtf8NotRetrievable:
      return codeSourceCompressedData<Utf8Unit>(xdr, ss);

    case DataType::UncompressedUtf8Retrievable:
      codeSourceRetrievable<Utf8Unit, ScriptSource::Uncompressed, mode>(ss);
      return Ok();

    case DataType::UncompressedUtf8NotRetrievable:
      return codeSourceUncompressedData<Utf8Unit>(xdr, ss);

    case DataType::CompressedUtf16Retrievable:
      codeSourceRetrievable<char16_t, ScriptSource::Compressed, mode>(ss);
      return Ok();

    case DataType::CompressedUtf16NotRetrievable:
      return codeSourceCompressedData<char16_t>(xdr, ss);

    case DataType::UncompressedUtf16Retrievable:
      codeSourceRetrievable<char16_t, ScriptSource::Uncompressed, mode>(ss);
      return Ok();

    case DataType::UncompressedUtf16NotRetrievable:
      return codeSourceUncompressedData<char16_t>(xdr, ss);

    case DataType::Missing: {
      MOZ_ASSERT(ss->data.is<ScriptSource::Missing>(),
                 "ScriptSource::data is initialized as missing, so neither "
                 "encoding nor decoding has to change anything");

      // There's no data to XDR for missing source.
      break;
    }

    case DataType::RetrievableUtf8:
      codeSourceRetrievableData<Utf8Unit, mode>(ss);
      return Ok();

    case DataType::RetrievableUtf16:
      codeSourceRetrievableData<char16_t, mode>(ss);
      return Ok();
  }

  // The range-check on |type| far above ought ensure the above |switch| is
  // exhaustive and all cases will return, but not all compilers understand
  // this.  Make the Missing case break to here so control obviously never flows
  // off the end.
  MOZ_ASSERT(tag == DataType::Missing);
  return Ok();
}

template <XDRMode mode>
/* static */
XDRResult StencilXDR::codeSource(XDRState<mode>* xdr,
                                 const JS::ReadOnlyDecodeOptions* maybeOptions,
                                 RefPtr<ScriptSource>& source) {
  FrontendContext* fc = xdr->fc();

  if (mode == XDR_DECODE) {
    // Allocate a new ScriptSource and root it with the holder.
    source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
    if (!source) {
      return xdr->fail(JS::TranscodeResult::Throw);
    }
  }

  static constexpr uint8_t HasFilename = 1 << 0;
  static constexpr uint8_t HasDisplayURL = 1 << 1;
  static constexpr uint8_t HasSourceMapURL = 1 << 2;
  static constexpr uint8_t MutedErrors = 1 << 3;

  uint8_t flags = 0;
  if (mode == XDR_ENCODE) {
    if (source->filename_) {
      flags |= HasFilename;
    }
    if (source->hasDisplayURL()) {
      flags |= HasDisplayURL;
    }
    if (source->hasSourceMapURL()) {
      flags |= HasSourceMapURL;
    }
    if (source->mutedErrors()) {
      flags |= MutedErrors;
    }
  }

  MOZ_TRY(xdr->codeUint8(&flags));

  if (flags & HasFilename) {
    XDRTranscodeString<char> chars;

    if (mode == XDR_ENCODE) {
      chars.construct<const char*>(source->filename());
    }
    MOZ_TRY(xdr->codeCharsZ(chars));
    if (mode == XDR_DECODE) {
      if (!source->setFilename(fc, std::move(chars.ref<UniqueChars>()))) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
    }
  }

  if (flags & HasDisplayURL) {
    XDRTranscodeString<char16_t> chars;

    if (mode == XDR_ENCODE) {
      chars.construct<const char16_t*>(source->displayURL());
    }
    MOZ_TRY(xdr->codeCharsZ(chars));
    if (mode == XDR_DECODE) {
      if (!source->setDisplayURL(fc,
                                 std::move(chars.ref<UniqueTwoByteChars>()))) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
    }
  }

  if (flags & HasSourceMapURL) {
    XDRTranscodeString<char16_t> chars;

    if (mode == XDR_ENCODE) {
      chars.construct<const char16_t*>(source->sourceMapURL());
    }
    MOZ_TRY(xdr->codeCharsZ(chars));
    if (mode == XDR_DECODE) {
      if (!source->setSourceMapURL(
              fc, std::move(chars.ref<UniqueTwoByteChars>()))) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
    }
  }

  MOZ_ASSERT(source->parameterListEnd_ == 0);

  if (flags & MutedErrors) {
    if (mode == XDR_DECODE) {
      source->mutedErrors_ = true;
    }
  }

  MOZ_TRY(xdr->codeUint32(&source->startLine_));
  MOZ_TRY(xdr->codeUint32(source->startColumn_.addressOfValueForTranscode()));

  // The introduction info doesn't persist across encode/decode.
  if (mode == XDR_DECODE) {
    source->introductionType_ = maybeOptions->introductionType;
    source->setIntroductionOffset(maybeOptions->introductionOffset);
    if (maybeOptions->introducerFilename()) {
      if (!source->setIntroducerFilename(
              fc, maybeOptions->introducerFilename().c_str())) {
        return xdr->fail(JS::TranscodeResult::Throw);
      }
    }
  }

  MOZ_TRY(codeSourceData(xdr, source.get()));

  return Ok();
}

template /* static */
    XDRResult
    StencilXDR::codeSource(XDRState<XDR_ENCODE>* xdr,
                           const JS::ReadOnlyDecodeOptions* maybeOptions,
                           RefPtr<ScriptSource>& holder);
template /* static */
    XDRResult
    StencilXDR::codeSource(XDRState<XDR_DECODE>* xdr,
                           const JS::ReadOnlyDecodeOptions* maybeOptions,
                           RefPtr<ScriptSource>& holder);

JS_PUBLIC_API bool JS::GetScriptTranscodingBuildId(
    JS::BuildIdCharVector* buildId) {
  MOZ_ASSERT(buildId->empty());
  MOZ_ASSERT(GetBuildId);

  if (!GetBuildId(buildId)) {
    return false;
  }

  // Note: the buildId returned here is also used for the bytecode cache MIME
  // type so use plain ASCII characters.

  if (!buildId->reserve(buildId->length() + 4)) {
    return false;
  }

  buildId->infallibleAppend('-');

  // XDR depends on pointer size and endianness.
  static_assert(sizeof(uintptr_t) == 4 || sizeof(uintptr_t) == 8);
  buildId->infallibleAppend(sizeof(uintptr_t) == 4 ? '4' : '8');
  buildId->infallibleAppend(MOZ_LITTLE_ENDIAN() ? 'l' : 'b');

  return true;
}

template <XDRMode mode>
static XDRResult VersionCheck(XDRState<mode>* xdr) {
  JS::BuildIdCharVector buildId;
  if (!JS::GetScriptTranscodingBuildId(&buildId)) {
    ReportOutOfMemory(xdr->fc());
    return xdr->fail(JS::TranscodeResult::Throw);
  }
  MOZ_ASSERT(!buildId.empty());

  uint32_t buildIdLength;
  if (mode == XDR_ENCODE) {
    buildIdLength = buildId.length();
  }

  MOZ_TRY(xdr->codeUint32(&buildIdLength));

  if (mode == XDR_DECODE && buildIdLength != buildId.length()) {
    return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
  }

  if (mode == XDR_ENCODE) {
    MOZ_TRY(xdr->codeBytes(buildId.begin(), buildIdLength));
  } else {
    JS::BuildIdCharVector decodedBuildId;

    // buildIdLength is already checked against the length of current
    // buildId.
    if (!decodedBuildId.resize(buildIdLength)) {
      ReportOutOfMemory(xdr->fc());
      return xdr->fail(JS::TranscodeResult::Throw);
    }

    MOZ_TRY(xdr->codeBytes(decodedBuildId.begin(), buildIdLength));

    // We do not provide binary compatibility with older scripts.
    if (!mozilla::ArrayEqual(decodedBuildId.begin(), buildId.begin(),
                             buildIdLength)) {
      return xdr->fail(JS::TranscodeResult::Failure_BadBuildId);
    }
  }

  return Ok();
}

XDRResult XDRStencilEncoder::codeStencil(
    const RefPtr<ScriptSource>& source,
    const frontend::CompilationStencil& stencil) {
#ifdef DEBUG
  auto sanityCheck = mozilla::MakeScopeExit(
      [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
#endif

  MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(this, stencil));

  MOZ_TRY(VersionCheck(this));

  uint32_t dummy = 0;
  size_t lengthOffset = buf->cursor();
  MOZ_TRY(codeUint32(&dummy));
  size_t hashOffset = buf->cursor();
  MOZ_TRY(codeUint32(&dummy));

  size_t contentOffset = buf->cursor();
  MOZ_TRY(frontend::StencilXDR::codeSource(
      this, nullptr, const_cast<RefPtr<ScriptSource>&>(source)));
  MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(
      thisconst_cast<frontend::CompilationStencil&>(stencil)));
  size_t endOffset = buf->cursor();

  if (endOffset > UINT32_MAX) {
    ReportOutOfMemory(fc());
    return fail(JS::TranscodeResult::Throw);
  }

  uint32_t length = endOffset - contentOffset;
  codeUint32At(&length, lengthOffset);

  const uint8_t* contentBegin = buf->bufferAt(contentOffset);
  uint32_t hash = mozilla::HashBytes(contentBegin, length);
  codeUint32At(&hash, hashOffset);

  return Ok();
}

XDRResult XDRStencilEncoder::codeStencil(
    const frontend::CompilationStencil& stencil) {
  return codeStencil(stencil.source, stencil);
}

static JS::TranscodeResult EncodeStencilImpl(
    JS::FrontendContext* fc, const frontend::CompilationStencil* initial,
    JS::TranscodeBuffer& buffer) {
  XDRStencilEncoder encoder(fc, buffer);
  XDRResult res = encoder.codeStencil(*initial);
  if (res.isErr()) {
    return res.unwrapErr();
  }
  return JS::TranscodeResult::Ok;
}

JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil,
                                      JS::TranscodeBuffer& buffer) {
  AutoReportFrontendContext fc(cx);

  const CompilationStencil* initial;
  UniquePtr<CompilationStencil> merged;
  if (stencil->canLazilyParse()) {
    merged.reset(stencil->getMerged(&fc));
    if (!merged) {
      return TranscodeResult::Throw;
    }
    initial = merged.get();
  } else {
    initial = stencil->getInitial();
  }

  return EncodeStencilImpl(&fc, initial, buffer);
}

JS::TranscodeResult js::EncodeStencil(JSContext* cx,
                                      frontend::CompilationStencil* stencil,
                                      JS::TranscodeBuffer& buffer) {
  AutoReportFrontendContext fc(cx);
  return EncodeStencilImpl(&fc, stencil, buffer);
}

XDRResult XDRStencilDecoder::codeStencil(
    const JS::ReadOnlyDecodeOptions& options,
    frontend::CompilationStencil& stencil) {
#ifdef DEBUG
  auto sanityCheck = mozilla::MakeScopeExit(
      [&] { MOZ_ASSERT(validateResultCode(fc(), resultCode())); });
#endif

  auto resetOptions = mozilla::MakeScopeExit([&] { options_ = nullptr; });
  options_ = &options;

  MOZ_TRY(VersionCheck(this));

  uint32_t length;
  MOZ_TRY(codeUint32(&length));

  uint32_t hash;
  MOZ_TRY(codeUint32(&hash));

  const uint8_t* contentBegin;
  MOZ_TRY(peekArray(length, &contentBegin));
  uint32_t actualHash = mozilla::HashBytes(contentBegin, length);

  if (MOZ_UNLIKELY(actualHash != hash)) {
    return fail(JS::TranscodeResult::Failure_BadDecode);
  }

  MOZ_TRY(frontend::StencilXDR::codeSource(this, &options, stencil.source));
  MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(this, stencil));

  return Ok();
}

JS::TranscodeResult JS::DecodeStencil(JSContext* cx,
                                      const JS::ReadOnlyDecodeOptions& options,
                                      const JS::TranscodeRange& range,
                                      JS::Stencil** stencilOut) {
  AutoReportFrontendContext fc(cx);
  return JS::DecodeStencil(&fc, options, range, stencilOut);
}

JS::TranscodeResult JS::DecodeStencil(JS::FrontendContext* fc,
                                      const JS::ReadOnlyDecodeOptions& options,
                                      const JS::TranscodeRange& range,
                                      JS::Stencil** stencilOut) {
  RefPtr<CompilationStencil> stencil;
  JS::TranscodeResult result =
      js::DecodeStencil(fc, options, range, getter_AddRefs(stencil));
  if (result != TranscodeResult::Ok) {
    return result;
  }

  RefPtr stencils =
      fc->getAllocator()->new_<frontend::InitialStencilAndDelazifications>();
  if (!stencils) {
    return TranscodeResult::Throw;
  }
  if (!stencils->init(fc, stencil.get())) {
    return TranscodeResult::Throw;
  }
  stencils.forget(stencilOut);
  return TranscodeResult::Ok;
}

JS::TranscodeResult js::DecodeStencil(
    JS::FrontendContext* fc, const JS::ReadOnlyDecodeOptions& options,
    const JS::TranscodeRange& range,
    frontend::CompilationStencil** stencilOut) {
  RefPtr<ScriptSource> source = fc->getAllocator()->new_<ScriptSource>();
  if (!source) {
    return JS::TranscodeResult::Throw;
  }
  RefPtr<CompilationStencil> stencil =
      fc->getAllocator()->new_<CompilationStencil>(source);
  if (!stencil) {
    return JS::TranscodeResult::Throw;
  }
  XDRStencilDecoder decoder(fc, range);
  XDRResult res = decoder.codeStencil(options, *stencil);
  if (res.isErr()) {
    return res.unwrapErr();
  }
  stencil.forget(stencilOut);
  return JS::TranscodeResult::Ok;
}

template /* static */ XDRResult StencilXDR::codeCompilationStencil(
    XDRState<XDR_ENCODE>* xdr, CompilationStencil& stencil);

template /* static */ XDRResult StencilXDR::codeCompilationStencil(
    XDRState<XDR_DECODE>* xdr, CompilationStencil& stencil);

/* static */ XDRResult StencilXDR::checkCompilationStencil(
    XDRStencilEncoder* encoder, const CompilationStencil& stencil) {
  if (stencil.asmJS) {
    return encoder->fail(JS::TranscodeResult::Failure_AsmJSNotSupported);
  }

  return Ok();
}

/* static */ XDRResult StencilXDR::checkCompilationStencil(
    const ExtensibleCompilationStencil& stencil) {
  if (stencil.asmJS) {
    return mozilla::Err(JS::TranscodeResult::Failure_AsmJSNotSupported);
  }

  return Ok();
}

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.