tor-browser

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

StructuredClone.cpp (139463B)


      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 /*
      8 * This file implements the structured data algorithms of
      9 * https://html.spec.whatwg.org/multipage/structured-data.html
     10 *
     11 * The spec is in two parts:
     12 *
     13 * -   StructuredSerialize examines a JS value and produces a graph of Records.
     14 * -   StructuredDeserialize walks the Records and produces a new JS value.
     15 *
     16 * The differences between our implementation and the spec are minor:
     17 *
     18 * -   We call the two phases "write" and "read".
     19 * -   Our algorithms use an explicit work stack, rather than recursion.
     20 * -   Serialized data is a flat array of bytes, not a (possibly cyclic) graph
     21 *     of "Records".
     22 * -   As a consequence, we handle non-treelike object graphs differently.
     23 *     We serialize objects that appear in multiple places in the input as
     24 *     backreferences, using sequential integer indexes.
     25 *     See `JSStructuredCloneReader::allObjs`, our take on the "memory" map
     26 *     in the spec's StructuredDeserialize.
     27 */
     28 
     29 #include "js/StructuredClone.h"
     30 
     31 #include "mozilla/Casting.h"
     32 #include "mozilla/CheckedInt.h"
     33 #include "mozilla/EndianUtils.h"
     34 #include "mozilla/FloatingPoint.h"
     35 #include "mozilla/Maybe.h"
     36 #include "mozilla/ScopeExit.h"
     37 
     38 #include <algorithm>
     39 #include <memory>
     40 #include <utility>
     41 
     42 #include "jsdate.h"
     43 
     44 #include "builtin/DataViewObject.h"
     45 #include "builtin/MapObject.h"
     46 #include "gc/GC.h"           // AutoSelectGCHeap
     47 #include "js/Array.h"        // JS::GetArrayLength, JS::IsArrayObject
     48 #include "js/ArrayBuffer.h"  // JS::{ArrayBufferHasData,DetachArrayBuffer,IsArrayBufferObject,New{,Mapped}ArrayBufferWithContents,ReleaseMappedArrayBufferContents}
     49 #include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin, JS::TaggedColumnNumberOneOrigin
     50 #include "js/Date.h"
     51 #include "js/experimental/TypedData.h"  // JS_NewDataView, JS_New{{Ui,I}nt{8,16,32},Float{32,64},Uint8Clamped,Big{Ui,I}nt64}ArrayWithBuffer
     52 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
     53 #include "js/GCAPI.h"
     54 #include "js/GCHashTable.h"
     55 #include "js/Object.h"              // JS::GetBuiltinClass
     56 #include "js/PropertyAndElement.h"  // JS_GetElement
     57 #include "js/RegExpFlags.h"         // JS::RegExpFlag, JS::RegExpFlags
     58 #include "js/ScalarType.h"          // js::Scalar::Type
     59 #include "js/SharedArrayBuffer.h"   // JS::IsSharedArrayBufferObject
     60 #include "js/Wrapper.h"
     61 #include "util/DifferentialTesting.h"
     62 #include "vm/BigIntType.h"
     63 #include "vm/ErrorObject.h"
     64 #include "vm/JSContext.h"
     65 #include "vm/PlainObject.h"  // js::PlainObject
     66 #include "vm/RegExpObject.h"
     67 #include "vm/SavedFrame.h"
     68 #include "vm/SharedArrayObject.h"
     69 #include "vm/TypedArrayObject.h"
     70 #include "wasm/WasmJS.h"
     71 
     72 #include "vm/ArrayObject-inl.h"
     73 #include "vm/Compartment-inl.h"
     74 #include "vm/ErrorObject-inl.h"
     75 #include "vm/JSContext-inl.h"
     76 #include "vm/JSObject-inl.h"
     77 #include "vm/NativeObject-inl.h"
     78 #include "vm/ObjectOperations-inl.h"
     79 #include "vm/Realm-inl.h"
     80 #include "vm/StringType-inl.h"
     81 
     82 using namespace js;
     83 
     84 using JS::CanonicalizeNaN;
     85 using JS::GetBuiltinClass;
     86 using JS::RegExpFlag;
     87 using JS::RegExpFlags;
     88 using JS::RootedValueVector;
     89 using mozilla::AssertedCast;
     90 using mozilla::BitwiseCast;
     91 using mozilla::Maybe;
     92 using mozilla::NativeEndian;
     93 using mozilla::NumbersAreIdentical;
     94 
     95 // When you make updates here, make sure you consider whether you need to bump
     96 // the value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h.  You
     97 // will likely need to increment the version if anything at all changes in the
     98 // serialization format.
     99 //
    100 // Note that SCTAG_END_OF_KEYS is written into the serialized form and should
    101 // have a stable ID, it need not be at the end of the list and should not be
    102 // used for sizing data structures.
    103 
    104 enum StructuredDataType : uint32_t {
    105  // Structured data types provided by the engine
    106  SCTAG_FLOAT_MAX = 0xFFF00000,
    107  SCTAG_HEADER = 0xFFF10000,
    108  SCTAG_NULL = 0xFFFF0000,
    109  SCTAG_UNDEFINED,
    110  SCTAG_BOOLEAN,
    111  SCTAG_INT32,
    112  SCTAG_STRING,
    113  SCTAG_DATE_OBJECT,
    114  SCTAG_REGEXP_OBJECT,
    115  SCTAG_ARRAY_OBJECT,
    116  SCTAG_OBJECT_OBJECT,
    117  SCTAG_ARRAY_BUFFER_OBJECT_V2,  // Old version, for backwards compatibility.
    118  SCTAG_BOOLEAN_OBJECT,
    119  SCTAG_STRING_OBJECT,
    120  SCTAG_NUMBER_OBJECT,
    121  SCTAG_BACK_REFERENCE_OBJECT,
    122  SCTAG_DO_NOT_USE_1,           // Required for backwards compatibility
    123  SCTAG_DO_NOT_USE_2,           // Required for backwards compatibility
    124  SCTAG_TYPED_ARRAY_OBJECT_V2,  // Old version, for backwards compatibility.
    125  SCTAG_MAP_OBJECT,
    126  SCTAG_SET_OBJECT,
    127  SCTAG_END_OF_KEYS,
    128  SCTAG_DO_NOT_USE_3,         // Required for backwards compatibility
    129  SCTAG_DATA_VIEW_OBJECT_V2,  // Old version, for backwards compatibility.
    130  SCTAG_SAVED_FRAME_OBJECT,
    131 
    132  // No new tags before principals.
    133  SCTAG_JSPRINCIPALS,
    134  SCTAG_NULL_JSPRINCIPALS,
    135  SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM,
    136  SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM,
    137 
    138  SCTAG_SHARED_ARRAY_BUFFER_OBJECT,
    139  SCTAG_SHARED_WASM_MEMORY_OBJECT,
    140 
    141  SCTAG_BIGINT,
    142  SCTAG_BIGINT_OBJECT,
    143 
    144  SCTAG_ARRAY_BUFFER_OBJECT,
    145  SCTAG_TYPED_ARRAY_OBJECT,
    146  SCTAG_DATA_VIEW_OBJECT,
    147 
    148  SCTAG_ERROR_OBJECT,
    149 
    150  SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT,
    151  SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT,
    152  SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT,
    153 
    154  SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
    155  SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
    156  SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
    157  SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int16,
    158  SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint16,
    159  SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int32,
    160  SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint32,
    161  SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float32,
    162  SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float64,
    163  SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED =
    164      SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped,
    165  // BigInt64 and BigUint64 are not supported in the v1 format.
    166  SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED,
    167 
    168  // Define a separate range of numbers for Transferable-only tags, since
    169  // they are not used for persistent clone buffers and therefore do not
    170  // require bumping JS_STRUCTURED_CLONE_VERSION.
    171  SCTAG_TRANSFER_MAP_HEADER = 0xFFFF0200,
    172  SCTAG_TRANSFER_MAP_PENDING_ENTRY,
    173  SCTAG_TRANSFER_MAP_ARRAY_BUFFER,
    174  SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER,
    175  SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES,
    176 
    177  SCTAG_END_OF_BUILTIN_TYPES
    178 };
    179 
    180 /*
    181 * Format of transfer map:
    182 *   - <SCTAG_TRANSFER_MAP_HEADER, UNREAD|TRANSFERRING|TRANSFERRED>
    183 *   - numTransferables (64 bits)
    184 *   - array of:
    185 *     - <SCTAG_TRANSFER_MAP_*, TransferableOwnership> pointer (64
    186 *       bits)
    187 *     - extraData (64 bits), eg byte length for ArrayBuffers
    188 *     - any data written for custom transferables
    189 */
    190 
    191 // Data associated with an SCTAG_TRANSFER_MAP_HEADER that tells whether the
    192 // contents have been read out yet or not. TRANSFERRING is for the case where we
    193 // have started but not completed reading, which due to errors could mean that
    194 // there are things still owned by the clone buffer that need to be released, so
    195 // discarding should not just be skipped.
    196 enum TransferableMapHeader {
    197  SCTAG_TM_UNREAD = 0,
    198  SCTAG_TM_TRANSFERRING,
    199  SCTAG_TM_TRANSFERRED,
    200 
    201  SCTAG_TM_END
    202 };
    203 
    204 static inline uint64_t PairToUInt64(uint32_t tag, uint32_t data) {
    205  return uint64_t(data) | (uint64_t(tag) << 32);
    206 }
    207 
    208 namespace js {
    209 
    210 template <typename T, typename AllocPolicy>
    211 struct BufferIterator {
    212  using BufferList = mozilla::BufferList<AllocPolicy>;
    213 
    214  explicit BufferIterator(const BufferList& buffer)
    215      : mBuffer(buffer), mIter(buffer.Iter()) {
    216    static_assert(8 % sizeof(T) == 0);
    217  }
    218 
    219  explicit BufferIterator(const JSStructuredCloneData& data)
    220      : mBuffer(data.bufList_), mIter(data.Start()) {}
    221 
    222  BufferIterator& operator=(const BufferIterator& other) {
    223    MOZ_ASSERT(&mBuffer == &other.mBuffer);
    224    mIter = other.mIter;
    225    return *this;
    226  }
    227 
    228  [[nodiscard]] bool advance(size_t size = sizeof(T)) {
    229    return mIter.AdvanceAcrossSegments(mBuffer, size);
    230  }
    231 
    232  BufferIterator operator++(int) {
    233    BufferIterator ret = *this;
    234    if (!advance(sizeof(T))) {
    235      MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
    236    }
    237    return ret;
    238  }
    239 
    240  BufferIterator& operator+=(size_t size) {
    241    if (!advance(size)) {
    242      MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
    243    }
    244    return *this;
    245  }
    246 
    247  size_t operator-(const BufferIterator& other) const {
    248    MOZ_ASSERT(&mBuffer == &other.mBuffer);
    249    return mBuffer.RangeLength(other.mIter, mIter);
    250  }
    251 
    252  bool operator==(const BufferIterator& other) const {
    253    return mBuffer.Start() == other.mBuffer.Start() && mIter == other.mIter;
    254  }
    255  bool operator!=(const BufferIterator& other) const {
    256    return !(*this == other);
    257  }
    258 
    259  bool done() const { return mIter.Done(); }
    260 
    261  [[nodiscard]] bool readBytes(char* outData, size_t size) {
    262    return mBuffer.ReadBytes(mIter, outData, size);
    263  }
    264 
    265  void write(const T& data) {
    266    MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
    267    *reinterpret_cast<T*>(mIter.Data()) = data;
    268  }
    269 
    270  T peek() const {
    271    MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
    272    return *reinterpret_cast<T*>(mIter.Data());
    273  }
    274 
    275  bool canPeek() const { return mIter.HasRoomFor(sizeof(T)); }
    276 
    277  const BufferList& mBuffer;
    278  typename BufferList::IterImpl mIter;
    279 };
    280 
    281 SharedArrayRawBufferRefs& SharedArrayRawBufferRefs::operator=(
    282    SharedArrayRawBufferRefs&& other) {
    283  takeOwnership(std::move(other));
    284  return *this;
    285 }
    286 
    287 SharedArrayRawBufferRefs::~SharedArrayRawBufferRefs() { releaseAll(); }
    288 
    289 bool SharedArrayRawBufferRefs::acquire(JSContext* cx,
    290                                       SharedArrayRawBuffer* rawbuf) {
    291  if (!refs_.append(rawbuf)) {
    292    ReportOutOfMemory(cx);
    293    return false;
    294  }
    295 
    296  if (!rawbuf->addReference()) {
    297    refs_.popBack();
    298    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    299                              JSMSG_SC_SAB_REFCNT_OFLO);
    300    return false;
    301  }
    302 
    303  return true;
    304 }
    305 
    306 bool SharedArrayRawBufferRefs::acquireAll(
    307    JSContext* cx, const SharedArrayRawBufferRefs& that) {
    308  if (!refs_.reserve(refs_.length() + that.refs_.length())) {
    309    ReportOutOfMemory(cx);
    310    return false;
    311  }
    312 
    313  for (auto ref : that.refs_) {
    314    if (!ref->addReference()) {
    315      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    316                                JSMSG_SC_SAB_REFCNT_OFLO);
    317      return false;
    318    }
    319    MOZ_ALWAYS_TRUE(refs_.append(ref));
    320  }
    321 
    322  return true;
    323 }
    324 
    325 void SharedArrayRawBufferRefs::takeOwnership(SharedArrayRawBufferRefs&& other) {
    326  MOZ_ASSERT(refs_.empty());
    327  refs_ = std::move(other.refs_);
    328 }
    329 
    330 void SharedArrayRawBufferRefs::releaseAll() {
    331  for (auto ref : refs_) {
    332    ref->dropReference();
    333  }
    334  refs_.clear();
    335 }
    336 
    337 // SCOutput provides an interface to write raw data -- eg uint64_ts, doubles,
    338 // arrays of bytes -- into a structured clone data output stream. It also knows
    339 // how to free any transferable data within that stream.
    340 //
    341 // Note that it contains a full JSStructuredCloneData object, which holds the
    342 // callbacks necessary to read/write/transfer/free the data. For the purpose of
    343 // this class, only the freeTransfer callback is relevant; the rest of the
    344 // callbacks are used by the higher-level JSStructuredCloneWriter interface.
    345 struct SCOutput {
    346 public:
    347  using Iter = BufferIterator<uint64_t, SystemAllocPolicy>;
    348 
    349  SCOutput(JSContext* cx, JS::StructuredCloneScope scope);
    350 
    351  JSContext* context() const { return cx; }
    352  JS::StructuredCloneScope scope() const { return buf.scope(); }
    353  void sameProcessScopeRequired() { buf.sameProcessScopeRequired(); }
    354 
    355  [[nodiscard]] bool write(uint64_t u);
    356  [[nodiscard]] bool writePair(uint32_t tag, uint32_t data);
    357  [[nodiscard]] bool writeDouble(double d);
    358  [[nodiscard]] bool writeBytes(const void* p, size_t nbytes);
    359  [[nodiscard]] bool writeChars(const Latin1Char* p, size_t nchars);
    360  [[nodiscard]] bool writeChars(const char16_t* p, size_t nchars);
    361 
    362  template <class T>
    363  [[nodiscard]] bool writeArray(const T* p, size_t nelems);
    364 
    365  void setCallbacks(const JSStructuredCloneCallbacks* callbacks, void* closure,
    366                    OwnTransferablePolicy policy) {
    367    buf.setCallbacks(callbacks, closure, policy);
    368  }
    369  void extractBuffer(JSStructuredCloneData* data) { *data = std::move(buf); }
    370 
    371  uint64_t tell() const { return buf.Size(); }
    372  uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
    373  Iter iter() { return Iter(buf); }
    374 
    375  size_t offset(Iter dest) { return dest - iter(); }
    376 
    377  JSContext* cx;
    378  JSStructuredCloneData buf;
    379 };
    380 
    381 class SCInput {
    382 public:
    383  using BufferIterator = js::BufferIterator<uint64_t, SystemAllocPolicy>;
    384 
    385  SCInput(JSContext* cx, const JSStructuredCloneData& data);
    386 
    387  JSContext* context() const { return cx; }
    388 
    389  static void getPtr(uint64_t data, void** ptr);
    390  static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
    391 
    392  [[nodiscard]] bool read(uint64_t* p);
    393  [[nodiscard]] bool readPair(uint32_t* tagp, uint32_t* datap);
    394  [[nodiscard]] bool readDouble(double* p);
    395  [[nodiscard]] bool readBytes(void* p, size_t nbytes);
    396  [[nodiscard]] bool readChars(Latin1Char* p, size_t nchars);
    397  [[nodiscard]] bool readChars(char16_t* p, size_t nchars);
    398  [[nodiscard]] bool readPtr(void**);
    399 
    400  [[nodiscard]] bool get(uint64_t* p);
    401  [[nodiscard]] bool getPair(uint32_t* tagp, uint32_t* datap);
    402 
    403  const BufferIterator& tell() const { return point; }
    404  void seekTo(const BufferIterator& pos) { point = pos; }
    405  [[nodiscard]] bool seekBy(size_t pos) {
    406    if (!point.advance(pos)) {
    407      reportTruncated();
    408      return false;
    409    }
    410    return true;
    411  }
    412 
    413  template <class T>
    414  [[nodiscard]] bool readArray(T* p, size_t nelems);
    415 
    416  bool reportTruncated() {
    417    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    418                              JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
    419    return false;
    420  }
    421 
    422 private:
    423  void staticAssertions() {
    424    static_assert(sizeof(char16_t) == 2);
    425    static_assert(sizeof(uint32_t) == 4);
    426  }
    427 
    428  JSContext* cx;
    429  BufferIterator point;
    430 };
    431 
    432 }  // namespace js
    433 
    434 struct JSStructuredCloneReader {
    435 public:
    436  explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
    437                                   const JS::CloneDataPolicy& cloneDataPolicy,
    438                                   const JSStructuredCloneCallbacks* cb,
    439                                   void* cbClosure);
    440 
    441  SCInput& input() { return in; }
    442  bool read(MutableHandleValue vp, size_t nbytes);
    443 
    444 private:
    445  JSContext* context() { return in.context(); }
    446 
    447  bool readHeader();
    448  bool readTransferMap();
    449 
    450  [[nodiscard]] bool readUint32(uint32_t* num);
    451 
    452  enum ShouldAtomizeStrings : bool {
    453    DontAtomizeStrings = false,
    454    AtomizeStrings = true
    455  };
    456 
    457  template <typename CharT>
    458  JSString* readStringImpl(uint32_t nchars, ShouldAtomizeStrings atomize);
    459  JSString* readString(uint32_t data, ShouldAtomizeStrings atomize);
    460 
    461  BigInt* readBigInt(uint32_t data);
    462 
    463  [[nodiscard]] bool readTypedArray(uint32_t arrayType, uint64_t nelems,
    464                                    MutableHandleValue vp, bool v1Read = false);
    465 
    466  [[nodiscard]] bool readDataView(uint64_t byteLength, MutableHandleValue vp);
    467 
    468  [[nodiscard]] bool readArrayBuffer(StructuredDataType type, uint32_t data,
    469                                     MutableHandleValue vp);
    470  [[nodiscard]] bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems,
    471                                       MutableHandleValue vp);
    472 
    473  [[nodiscard]] bool readSharedArrayBuffer(StructuredDataType type,
    474                                           MutableHandleValue vp);
    475 
    476  [[nodiscard]] bool readSharedWasmMemory(uint32_t nbytes,
    477                                          MutableHandleValue vp);
    478 
    479  // A serialized SavedFrame contains primitive values in a header followed by
    480  // an optional parent frame that is read recursively.
    481  [[nodiscard]] JSObject* readSavedFrameHeader(uint32_t principalsTag);
    482  [[nodiscard]] bool readSavedFrameFields(Handle<SavedFrame*> frameObj,
    483                                          HandleValue parent, bool* state);
    484 
    485  // A serialized Error contains primitive values in a header followed by
    486  // 'cause', 'errors', and 'stack' fields that are read recursively.
    487  [[nodiscard]] JSObject* readErrorHeader(uint32_t type);
    488  [[nodiscard]] bool readErrorFields(Handle<ErrorObject*> errorObj,
    489                                     HandleValue cause, bool* state);
    490 
    491  [[nodiscard]] bool readMapField(Handle<MapObject*> mapObj, HandleValue key);
    492 
    493  [[nodiscard]] bool readObjectField(HandleObject obj, HandleValue key);
    494 
    495  [[nodiscard]] bool startRead(
    496      MutableHandleValue vp,
    497      ShouldAtomizeStrings atomizeStrings = DontAtomizeStrings);
    498 
    499  SCInput& in;
    500 
    501  // The widest scope that the caller will accept, where
    502  // SameProcess is the widest (it can store anything it wants)
    503  // and DifferentProcess is the narrowest (it cannot contain pointers and must
    504  // be valid cross-process.)
    505  //
    506  // Although this can be initially set to other "unresolved" values (eg
    507  // Unassigned), by the end of a successful readHeader() this will be resolved
    508  // to SameProcess or DifferentProcess.
    509  JS::StructuredCloneScope allowedScope;
    510 
    511  const JS::CloneDataPolicy cloneDataPolicy;
    512 
    513  // Stack of objects with properties remaining to be read.
    514  RootedValueVector objs;
    515 
    516  // Maintain a stack of state values for the `objs` stack. Since this is only
    517  // needed for a very small subset of objects (those with a known set of
    518  // object children), the state information is stored as a stack of
    519  // <object, state> pairs where the object determines which element of the
    520  // `objs` stack that it corresponds to. So when reading from the `objs` stack,
    521  // the state will be retrieved only if the top object on `objState` matches
    522  // the top object of `objs`.
    523  //
    524  // Currently, the only state needed is a boolean indicating whether the fields
    525  // have been read yet.
    526  Rooted<GCVector<std::pair<HeapPtr<JSObject*>, bool>, 8>> objState;
    527 
    528  // Array of all objects read during this deserialization, for resolving
    529  // backreferences.
    530  //
    531  // For backreferences to work correctly, objects must be added to this
    532  // array in exactly the order expected by the version of the Writer that
    533  // created the serialized data, even across years and format versions. This
    534  // is usually no problem, since both algorithms do a single linear pass
    535  // over the serialized data. There is one hitch; see readTypedArray.
    536  //
    537  // The values in this vector are objects, except it can temporarily have
    538  // one `undefined` placeholder value (the readTypedArray hack).
    539  RootedValueVector allObjs;
    540 
    541  size_t numItemsRead;
    542 
    543  // The user defined callbacks that will be used for cloning.
    544  const JSStructuredCloneCallbacks* callbacks;
    545 
    546  // Any value passed to JS_ReadStructuredClone.
    547  void* closure;
    548 
    549  // The heap to use for allocating common GC things. This starts out as the
    550  // nursery (the default) but may switch to the tenured heap if nursery
    551  // collection occurs, as nursery allocation is pointless after the
    552  // deserialized root object is tenured.
    553  //
    554  // This is only used for the most common kind, e.g. plain objects, strings
    555  // and a couple of others.
    556  AutoSelectGCHeap gcHeap;
    557 
    558  friend bool JS_ReadString(JSStructuredCloneReader* r,
    559                            JS::MutableHandleString str);
    560  friend bool JS_ReadTypedArray(JSStructuredCloneReader* r,
    561                                MutableHandleValue vp);
    562 
    563  // Provide a way to detect whether any of the clone data is never used. When
    564  // "tail" data (currently, this is only stored data for Transferred
    565  // ArrayBuffers in the DifferentProcess scope) is read, record the first and
    566  // last positions. At the end of deserialization, make sure there's nothing
    567  // between the end of the main data and the beginning of the tail, nor after
    568  // the end of the tail.
    569  mozilla::Maybe<SCInput::BufferIterator> tailStartPos;
    570  mozilla::Maybe<SCInput::BufferIterator> tailEndPos;
    571 };
    572 
    573 struct JSStructuredCloneWriter {
    574 public:
    575  explicit JSStructuredCloneWriter(JSContext* cx,
    576                                   JS::StructuredCloneScope scope,
    577                                   const JS::CloneDataPolicy& cloneDataPolicy,
    578                                   const JSStructuredCloneCallbacks* cb,
    579                                   void* cbClosure, const Value& tVal)
    580      : out(cx, scope),
    581        callbacks(cb),
    582        closure(cbClosure),
    583        objs(cx),
    584        counts(cx),
    585        objectEntries(cx),
    586        otherEntries(cx),
    587        memory(cx),
    588        transferable(cx, tVal),
    589        transferableObjects(cx, TransferableObjectsList(cx)),
    590        cloneDataPolicy(cloneDataPolicy) {
    591    out.setCallbacks(cb, cbClosure,
    592                     OwnTransferablePolicy::OwnsTransferablesIfAny);
    593  }
    594 
    595  bool init() {
    596    return parseTransferable() && writeHeader() && writeTransferMap();
    597  }
    598 
    599  bool write(HandleValue v);
    600 
    601  SCOutput& output() { return out; }
    602 
    603  void extractBuffer(JSStructuredCloneData* newData) {
    604    out.extractBuffer(newData);
    605  }
    606 
    607 private:
    608  JSStructuredCloneWriter() = delete;
    609  JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
    610 
    611  JSContext* context() { return out.context(); }
    612 
    613  bool writeHeader();
    614  bool writeTransferMap();
    615 
    616  bool writeString(uint32_t tag, JSString* str);
    617  bool writeBigInt(uint32_t tag, BigInt* bi);
    618  bool writeArrayBuffer(HandleObject obj);
    619  bool writeTypedArray(HandleObject obj);
    620  bool writeDataView(HandleObject obj);
    621  bool writeSharedArrayBuffer(HandleObject obj);
    622  bool writeSharedWasmMemory(HandleObject obj);
    623  bool startObject(HandleObject obj, bool* backref);
    624  bool writePrimitive(HandleValue v);
    625  bool startWrite(HandleValue v);
    626  bool traverseObject(HandleObject obj, ESClass cls);
    627  bool traverseMap(HandleObject obj);
    628  bool traverseSet(HandleObject obj);
    629  bool traverseSavedFrame(HandleObject obj);
    630  bool traverseError(HandleObject obj);
    631 
    632  template <typename... Args>
    633  bool reportDataCloneError(uint32_t errorId, Args&&... aArgs);
    634 
    635  bool parseTransferable();
    636  bool transferOwnership();
    637 
    638  inline void checkStack();
    639 
    640  SCOutput out;
    641 
    642  // The user defined callbacks that will be used to signal cloning, in some
    643  // cases.
    644  const JSStructuredCloneCallbacks* callbacks;
    645 
    646  // Any value passed to the callbacks.
    647  void* closure;
    648 
    649  // Vector of objects with properties remaining to be written.
    650  //
    651  // NB: These can span multiple compartments, so the compartment must be
    652  // entered before any manipulation is performed.
    653  RootedValueVector objs;
    654 
    655  // counts[i] is the number of entries of objs[i] remaining to be written.
    656  // counts.length() == objs.length() and sum(counts) == entries.length().
    657  Vector<size_t> counts;
    658 
    659  // For JSObject: Property IDs as value
    660  RootedIdVector objectEntries;
    661 
    662  // For Map: Key followed by value
    663  // For Set: Key
    664  // For SavedFrame: parent SavedFrame
    665  // For Error: cause, errors, stack
    666  RootedValueVector otherEntries;
    667 
    668  // The "memory" list described in the HTML5 internal structured cloning
    669  // algorithm.  memory is a superset of objs; items are never removed from
    670  // Memory until a serialization operation is finished
    671  using CloneMemory = GCHashMap<JSObject*, uint32_t,
    672                                StableCellHasher<JSObject*>, SystemAllocPolicy>;
    673  Rooted<CloneMemory> memory;
    674 
    675  // Set of transferable objects
    676  RootedValue transferable;
    677  using TransferableObjectsList = GCVector<JSObject*>;
    678  Rooted<TransferableObjectsList> transferableObjects;
    679 
    680  const JS::CloneDataPolicy cloneDataPolicy;
    681 
    682  friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
    683  friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
    684  friend bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj);
    685 };
    686 
    687 JS_PUBLIC_API uint64_t js::GetSCOffset(JSStructuredCloneWriter* writer) {
    688  MOZ_ASSERT(writer);
    689  return writer->output().count() * sizeof(uint64_t);
    690 }
    691 
    692 static_assert(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
    693 static_assert(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
    694 static_assert(Scalar::Int8 == 0);
    695 
    696 template <typename... Args>
    697 static void ReportDataCloneError(JSContext* cx,
    698                                 const JSStructuredCloneCallbacks* callbacks,
    699                                 uint32_t errorId, void* closure,
    700                                 Args&&... aArgs) {
    701  unsigned errorNumber;
    702  switch (errorId) {
    703    case JS_SCERR_DUP_TRANSFERABLE:
    704      errorNumber = JSMSG_SC_DUP_TRANSFERABLE;
    705      break;
    706 
    707    case JS_SCERR_TRANSFERABLE:
    708      errorNumber = JSMSG_SC_NOT_TRANSFERABLE;
    709      break;
    710 
    711    case JS_SCERR_UNSUPPORTED_TYPE:
    712      errorNumber = JSMSG_SC_UNSUPPORTED_TYPE;
    713      break;
    714 
    715    case JS_SCERR_SHMEM_TRANSFERABLE:
    716      errorNumber = JSMSG_SC_SHMEM_TRANSFERABLE;
    717      break;
    718 
    719    case JS_SCERR_TRANSFERABLE_TWICE:
    720      errorNumber = JSMSG_SC_TRANSFERABLE_TWICE;
    721      break;
    722 
    723    case JS_SCERR_TYPED_ARRAY_DETACHED:
    724      errorNumber = JSMSG_TYPED_ARRAY_DETACHED;
    725      break;
    726 
    727    case JS_SCERR_WASM_NO_TRANSFER:
    728      errorNumber = JSMSG_WASM_NO_TRANSFER;
    729      break;
    730 
    731    case JS_SCERR_NOT_CLONABLE:
    732      errorNumber = JSMSG_SC_NOT_CLONABLE;
    733      break;
    734 
    735    case JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP:
    736      errorNumber = JSMSG_SC_NOT_CLONABLE_WITH_COOP_COEP;
    737      break;
    738 
    739    default:
    740      MOZ_CRASH("Unkown errorId");
    741      break;
    742  }
    743 
    744  if (callbacks && callbacks->reportError) {
    745    MOZ_RELEASE_ASSERT(!cx->isExceptionPending());
    746 
    747    JSErrorReport report;
    748    report.errorNumber = errorNumber;
    749    // Get js error message if it's possible and propagate it through callback.
    750    if (JS_ExpandErrorArgumentsASCII(cx, GetErrorMessage, errorNumber, &report,
    751                                     std::forward<Args>(aArgs)...) &&
    752        report.message()) {
    753      callbacks->reportError(cx, errorId, closure, report.message().c_str());
    754    } else {
    755      ReportOutOfMemory(cx);
    756 
    757      callbacks->reportError(cx, errorId, closure, "");
    758    }
    759 
    760    return;
    761  }
    762 
    763  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber,
    764                            std::forward<Args>(aArgs)...);
    765 }
    766 
    767 bool WriteStructuredClone(JSContext* cx, HandleValue v,
    768                          JSStructuredCloneData* bufp,
    769                          JS::StructuredCloneScope scope,
    770                          const JS::CloneDataPolicy& cloneDataPolicy,
    771                          const JSStructuredCloneCallbacks* cb, void* cbClosure,
    772                          const Value& transferable) {
    773  JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure,
    774                            transferable);
    775  if (!w.init()) {
    776    return false;
    777  }
    778  if (!w.write(v)) {
    779    return false;
    780  }
    781  w.extractBuffer(bufp);
    782  return true;
    783 }
    784 
    785 bool ReadStructuredClone(JSContext* cx, const JSStructuredCloneData& data,
    786                         JS::StructuredCloneScope scope, MutableHandleValue vp,
    787                         const JS::CloneDataPolicy& cloneDataPolicy,
    788                         const JSStructuredCloneCallbacks* cb,
    789                         void* cbClosure) {
    790  if (data.Size() % 8) {
    791    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    792                              JSMSG_SC_BAD_SERIALIZED_DATA, "misaligned");
    793    return false;
    794  }
    795  SCInput in(cx, data);
    796  JSStructuredCloneReader r(in, scope, cloneDataPolicy, cb, cbClosure);
    797  return r.read(vp, data.Size());
    798 }
    799 
    800 static bool StructuredCloneHasTransferObjects(
    801    const JSStructuredCloneData& data) {
    802  if (data.Size() < sizeof(uint64_t)) {
    803    return false;
    804  }
    805 
    806  uint64_t u;
    807  BufferIterator<uint64_t, SystemAllocPolicy> iter(data);
    808  MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u)));
    809  uint32_t tag = uint32_t(u >> 32);
    810  return (tag == SCTAG_TRANSFER_MAP_HEADER);
    811 }
    812 
    813 namespace js {
    814 
    815 SCInput::SCInput(JSContext* cx, const JSStructuredCloneData& data)
    816    : cx(cx), point(data) {
    817  static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0,
    818                "structured clone buffer reads should be aligned");
    819  MOZ_ASSERT(data.Size() % 8 == 0);
    820 }
    821 
    822 bool SCInput::read(uint64_t* p) {
    823  if (!point.canPeek()) {
    824    *p = 0;  // initialize to shut GCC up
    825    return reportTruncated();
    826  }
    827  *p = NativeEndian::swapFromLittleEndian(point.peek());
    828  MOZ_ALWAYS_TRUE(point.advance());
    829  return true;
    830 }
    831 
    832 bool SCInput::readPair(uint32_t* tagp, uint32_t* datap) {
    833  uint64_t u;
    834  bool ok = read(&u);
    835  if (ok) {
    836    *tagp = uint32_t(u >> 32);
    837    *datap = uint32_t(u);
    838  }
    839  return ok;
    840 }
    841 
    842 bool SCInput::get(uint64_t* p) {
    843  if (!point.canPeek()) {
    844    return reportTruncated();
    845  }
    846  *p = NativeEndian::swapFromLittleEndian(point.peek());
    847  return true;
    848 }
    849 
    850 bool SCInput::getPair(uint32_t* tagp, uint32_t* datap) {
    851  uint64_t u = 0;
    852  if (!get(&u)) {
    853    return false;
    854  }
    855 
    856  *tagp = uint32_t(u >> 32);
    857  *datap = uint32_t(u);
    858  return true;
    859 }
    860 
    861 void SCInput::getPair(uint64_t data, uint32_t* tagp, uint32_t* datap) {
    862  uint64_t u = NativeEndian::swapFromLittleEndian(data);
    863  *tagp = uint32_t(u >> 32);
    864  *datap = uint32_t(u);
    865 }
    866 
    867 bool SCInput::readDouble(double* p) {
    868  uint64_t u;
    869  if (!read(&u)) {
    870    return false;
    871  }
    872  *p = CanonicalizeNaN(mozilla::BitwiseCast<double>(u));
    873  return true;
    874 }
    875 
    876 template <typename T>
    877 static void swapFromLittleEndianInPlace(T* ptr, size_t nelems) {
    878  if (nelems > 0) {
    879    NativeEndian::swapFromLittleEndianInPlace(ptr, nelems);
    880  }
    881 }
    882 
    883 template <>
    884 void swapFromLittleEndianInPlace(uint8_t* ptr, size_t nelems) {}
    885 
    886 // Data is packed into an integral number of uint64_t words. Compute the
    887 // padding required to finish off the final word.
    888 static size_t ComputePadding(size_t nelems, size_t elemSize) {
    889  // We want total length mod 8, where total length is nelems * sizeof(T),
    890  // but that might overflow. So reduce nelems to nelems mod 8, since we are
    891  // going to be doing a mod 8 later anyway.
    892  size_t leftoverLength = (nelems % sizeof(uint64_t)) * elemSize;
    893  return (-leftoverLength) & (sizeof(uint64_t) - 1);
    894 }
    895 
    896 template <class T>
    897 bool SCInput::readArray(T* p, size_t nelems) {
    898  if (!nelems) {
    899    return true;
    900  }
    901 
    902  static_assert(sizeof(uint64_t) % sizeof(T) == 0);
    903 
    904  // Fail if nelems is so huge that computing the full size will overflow.
    905  mozilla::CheckedInt<size_t> size =
    906      mozilla::CheckedInt<size_t>(nelems) * sizeof(T);
    907  if (!size.isValid()) {
    908    return reportTruncated();
    909  }
    910 
    911  if (!point.readBytes(reinterpret_cast<char*>(p), size.value())) {
    912    // To avoid any way in which uninitialized data could escape, zero the array
    913    // if filling it failed.
    914    std::uninitialized_fill_n(p, nelems, 0);
    915    return reportTruncated();
    916  }
    917 
    918  swapFromLittleEndianInPlace(p, nelems);
    919 
    920  point += ComputePadding(nelems, sizeof(T));
    921 
    922  return true;
    923 }
    924 
    925 bool SCInput::readBytes(void* p, size_t nbytes) {
    926  return readArray((uint8_t*)p, nbytes);
    927 }
    928 
    929 bool SCInput::readChars(Latin1Char* p, size_t nchars) {
    930  static_assert(sizeof(Latin1Char) == sizeof(uint8_t),
    931                "Latin1Char must fit in 1 byte");
    932  return readBytes(p, nchars);
    933 }
    934 
    935 bool SCInput::readChars(char16_t* p, size_t nchars) {
    936  MOZ_ASSERT(sizeof(char16_t) == sizeof(uint16_t));
    937  return readArray((uint16_t*)p, nchars);
    938 }
    939 
    940 void SCInput::getPtr(uint64_t data, void** ptr) {
    941  *ptr = reinterpret_cast<void*>(NativeEndian::swapFromLittleEndian(data));
    942 }
    943 
    944 bool SCInput::readPtr(void** p) {
    945  uint64_t u;
    946  if (!read(&u)) {
    947    return false;
    948  }
    949  *p = reinterpret_cast<void*>(u);
    950  return true;
    951 }
    952 
    953 SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope)
    954    : cx(cx), buf(scope) {}
    955 
    956 bool SCOutput::write(uint64_t u) {
    957  uint64_t v = NativeEndian::swapToLittleEndian(u);
    958  if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) {
    959    ReportOutOfMemory(context());
    960    return false;
    961  }
    962  return true;
    963 }
    964 
    965 bool SCOutput::writePair(uint32_t tag, uint32_t data) {
    966  // As it happens, the tag word appears after the data word in the output.
    967  // This is because exponents occupy the last 2 bytes of doubles on the
    968  // little-endian platforms we care most about.
    969  //
    970  // For example, TrueValue() is written using writePair(SCTAG_BOOLEAN, 1).
    971  // PairToUInt64 produces the number 0xFFFF000200000001.
    972  // That is written out as the bytes 01 00 00 00 02 00 FF FF.
    973  return write(PairToUInt64(tag, data));
    974 }
    975 
    976 static inline double ReinterpretPairAsDouble(uint32_t tag, uint32_t data) {
    977  return BitwiseCast<double>(PairToUInt64(tag, data));
    978 }
    979 
    980 bool SCOutput::writeDouble(double d) {
    981  return write(BitwiseCast<uint64_t>(CanonicalizeNaN(d)));
    982 }
    983 
    984 template <class T>
    985 bool SCOutput::writeArray(const T* p, size_t nelems) {
    986  static_assert(8 % sizeof(T) == 0);
    987  static_assert(sizeof(uint64_t) % sizeof(T) == 0);
    988 
    989  if (nelems == 0) {
    990    return true;
    991  }
    992 
    993  for (size_t i = 0; i < nelems; i++) {
    994    T value = NativeEndian::swapToLittleEndian(p[i]);
    995    if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value))) {
    996      ReportOutOfMemory(context());
    997      return false;
    998    }
    999  }
   1000 
   1001  // Zero-pad to 8 bytes boundary.
   1002  size_t padbytes = ComputePadding(nelems, sizeof(T));
   1003  char zeroes[sizeof(uint64_t)] = {0};
   1004  if (!buf.AppendBytes(zeroes, padbytes)) {
   1005    ReportOutOfMemory(context());
   1006    return false;
   1007  }
   1008 
   1009  return true;
   1010 }
   1011 
   1012 template <>
   1013 bool SCOutput::writeArray<uint8_t>(const uint8_t* p, size_t nelems) {
   1014  if (nelems == 0) {
   1015    return true;
   1016  }
   1017 
   1018  if (!buf.AppendBytes(reinterpret_cast<const char*>(p), nelems)) {
   1019    ReportOutOfMemory(context());
   1020    return false;
   1021  }
   1022 
   1023  // zero-pad to 8 bytes boundary
   1024  size_t padbytes = ComputePadding(nelems, 1);
   1025  char zeroes[sizeof(uint64_t)] = {0};
   1026  if (!buf.AppendBytes(zeroes, padbytes)) {
   1027    ReportOutOfMemory(context());
   1028    return false;
   1029  }
   1030 
   1031  return true;
   1032 }
   1033 
   1034 bool SCOutput::writeBytes(const void* p, size_t nbytes) {
   1035  return writeArray((const uint8_t*)p, nbytes);
   1036 }
   1037 
   1038 bool SCOutput::writeChars(const char16_t* p, size_t nchars) {
   1039  static_assert(sizeof(char16_t) == sizeof(uint16_t),
   1040                "required so that treating char16_t[] memory as uint16_t[] "
   1041                "memory is permissible");
   1042  return writeArray((const uint16_t*)p, nchars);
   1043 }
   1044 
   1045 bool SCOutput::writeChars(const Latin1Char* p, size_t nchars) {
   1046  static_assert(sizeof(Latin1Char) == sizeof(uint8_t),
   1047                "Latin1Char must fit in 1 byte");
   1048  return writeBytes(p, nchars);
   1049 }
   1050 
   1051 }  // namespace js
   1052 
   1053 JSStructuredCloneData::~JSStructuredCloneData() { discardTransferables(); }
   1054 
   1055 // If the buffer contains Transferables, free them. Note that custom
   1056 // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
   1057 // delete their transferables.
   1058 void JSStructuredCloneData::discardTransferables() {
   1059  if (!Size()) {
   1060    return;
   1061  }
   1062 
   1063  if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny) {
   1064    return;
   1065  }
   1066 
   1067  // DifferentProcess clones cannot contain pointers, so nothing needs to be
   1068  // released.
   1069  if (scope() == JS::StructuredCloneScope::DifferentProcess) {
   1070    return;
   1071  }
   1072 
   1073  FreeTransferStructuredCloneOp freeTransfer = nullptr;
   1074  if (callbacks_) {
   1075    freeTransfer = callbacks_->freeTransfer;
   1076  }
   1077 
   1078  auto point = BufferIterator<uint64_t, SystemAllocPolicy>(*this);
   1079  if (point.done()) {
   1080    return;  // Empty buffer
   1081  }
   1082 
   1083  uint32_t tag, data;
   1084  MOZ_RELEASE_ASSERT(point.canPeek());
   1085  SCInput::getPair(point.peek(), &tag, &data);
   1086  MOZ_ALWAYS_TRUE(point.advance());
   1087 
   1088  if (tag == SCTAG_HEADER) {
   1089    if (point.done()) {
   1090      return;
   1091    }
   1092 
   1093    MOZ_RELEASE_ASSERT(point.canPeek());
   1094    SCInput::getPair(point.peek(), &tag, &data);
   1095    MOZ_ALWAYS_TRUE(point.advance());
   1096  }
   1097 
   1098  if (tag != SCTAG_TRANSFER_MAP_HEADER) {
   1099    return;
   1100  }
   1101 
   1102  if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED) {
   1103    return;
   1104  }
   1105 
   1106  // freeTransfer should not GC
   1107  JS::AutoSuppressGCAnalysis nogc;
   1108 
   1109  if (point.done()) {
   1110    return;
   1111  }
   1112 
   1113  MOZ_RELEASE_ASSERT(point.canPeek());
   1114  uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
   1115  MOZ_ALWAYS_TRUE(point.advance());
   1116  while (numTransferables--) {
   1117    if (!point.canPeek()) {
   1118      return;
   1119    }
   1120 
   1121    uint32_t ownership;
   1122    SCInput::getPair(point.peek(), &tag, &ownership);
   1123    MOZ_ALWAYS_TRUE(point.advance());
   1124    MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
   1125    if (!point.canPeek()) {
   1126      return;
   1127    }
   1128 
   1129    void* content;
   1130    SCInput::getPtr(point.peek(), &content);
   1131    MOZ_ALWAYS_TRUE(point.advance());
   1132    if (!point.canPeek()) {
   1133      return;
   1134    }
   1135 
   1136    uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
   1137    MOZ_ALWAYS_TRUE(point.advance());
   1138 
   1139    if (ownership < JS::SCTAG_TMO_FIRST_OWNED) {
   1140      continue;
   1141    }
   1142 
   1143    if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
   1144      js_free(content);
   1145    } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
   1146      JS::ReleaseMappedArrayBufferContents(content, extraData);
   1147    } else if (freeTransfer) {
   1148      freeTransfer(tag, JS::TransferableOwnership(ownership), content,
   1149                   extraData, closure_);
   1150    } else {
   1151      MOZ_ASSERT(false, "unknown ownership");
   1152    }
   1153  }
   1154 }
   1155 
   1156 static_assert(JSString::MAX_LENGTH < UINT32_MAX);
   1157 
   1158 bool JSStructuredCloneWriter::parseTransferable() {
   1159  // NOTE: The transferables set is tested for non-emptiness at various
   1160  //       junctures in structured cloning, so this set must be initialized
   1161  //       by this method in all non-error cases.
   1162  MOZ_ASSERT(transferableObjects.empty(),
   1163             "parseTransferable called with stale data");
   1164 
   1165  if (transferable.isNull() || transferable.isUndefined()) {
   1166    return true;
   1167  }
   1168 
   1169  if (!transferable.isObject()) {
   1170    return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1171  }
   1172 
   1173  JSContext* cx = context();
   1174  RootedObject array(cx, &transferable.toObject());
   1175  bool isArray;
   1176  if (!JS::IsArrayObject(cx, array, &isArray)) {
   1177    return false;
   1178  }
   1179  if (!isArray) {
   1180    return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1181  }
   1182 
   1183  uint32_t length;
   1184  if (!JS::GetArrayLength(cx, array, &length)) {
   1185    return false;
   1186  }
   1187 
   1188  // Initialize the set for the provided array's length.
   1189  if (!transferableObjects.reserve(length)) {
   1190    return false;
   1191  }
   1192 
   1193  if (length == 0) {
   1194    return true;
   1195  }
   1196 
   1197  RootedValue v(context());
   1198  RootedObject tObj(context());
   1199 
   1200  Rooted<GCHashSet<js::HeapPtr<JSObject*>,
   1201                   js::StableCellHasher<js::HeapPtr<JSObject*>>,
   1202                   SystemAllocPolicy>>
   1203      seen(context());
   1204 
   1205  for (uint32_t i = 0; i < length; ++i) {
   1206    if (!CheckForInterrupt(cx)) {
   1207      return false;
   1208    }
   1209 
   1210    if (!JS_GetElement(cx, array, i, &v)) {
   1211      return false;
   1212    }
   1213 
   1214    if (!v.isObject()) {
   1215      return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1216    }
   1217    tObj = &v.toObject();
   1218 
   1219    RootedObject unwrappedObj(cx, CheckedUnwrapStatic(tObj));
   1220    if (!unwrappedObj) {
   1221      ReportAccessDenied(cx);
   1222      return false;
   1223    }
   1224 
   1225    // Shared memory cannot be transferred because it is not possible (nor
   1226    // desirable) to detach the memory in agents that already hold a
   1227    // reference to it.
   1228 
   1229    if (unwrappedObj->is<SharedArrayBufferObject>()) {
   1230      return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
   1231    }
   1232 
   1233    else if (unwrappedObj->is<WasmMemoryObject>()) {
   1234      if (unwrappedObj->as<WasmMemoryObject>().isShared()) {
   1235        return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
   1236      }
   1237    }
   1238 
   1239    // External array buffers may be able to be transferred in the future,
   1240    // but that is not currently implemented.
   1241    //
   1242    // Immutable array buffers can't be transferred, because they can't be
   1243    // detached.
   1244 
   1245    else if (unwrappedObj->is<ArrayBufferObject>()) {
   1246      if (unwrappedObj->as<ArrayBufferObject>().isExternal() ||
   1247          unwrappedObj->as<ArrayBufferObject>().isImmutable()) {
   1248        return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1249      }
   1250    }
   1251 
   1252    else {
   1253      if (!out.buf.callbacks_ || !out.buf.callbacks_->canTransfer) {
   1254        return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1255      }
   1256 
   1257      JSAutoRealm ar(cx, unwrappedObj);
   1258      bool sameProcessScopeRequired = false;
   1259      if (!out.buf.callbacks_->canTransfer(
   1260              cx, unwrappedObj, &sameProcessScopeRequired, out.buf.closure_)) {
   1261        return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   1262      }
   1263 
   1264      if (sameProcessScopeRequired) {
   1265        output().sameProcessScopeRequired();
   1266      }
   1267    }
   1268 
   1269    // No duplicates allowed. Normally the transferable list is very short, but
   1270    // some users are passing >10k. Switch to a hash-based lookup when the
   1271    // linear list starts getting long.
   1272    constexpr uint32_t MAX_LINEAR = 10;
   1273 
   1274    // Switch from a linear scan to a set lookup, initializing the set with all
   1275    // objects seen so far.
   1276    if (i == MAX_LINEAR) {
   1277      for (JSObject* obj : transferableObjects) {
   1278        if (!seen.putNew(obj)) {
   1279          seen.clear();  // Fall back to linear scan on OOM.
   1280          break;
   1281        }
   1282      }
   1283    }
   1284 
   1285    if (seen.empty()) {
   1286      if (std::find(transferableObjects.begin(), transferableObjects.end(),
   1287                    tObj) != transferableObjects.end()) {
   1288        return reportDataCloneError(JS_SCERR_DUP_TRANSFERABLE);
   1289      }
   1290    } else {
   1291      MOZ_ASSERT(seen.count() == i);  // All objs are distinct up to this point.
   1292      auto p = seen.lookupForAdd(tObj);
   1293      if (p) {
   1294        return reportDataCloneError(JS_SCERR_DUP_TRANSFERABLE);
   1295      }
   1296      if (!seen.add(p, tObj)) {
   1297        seen.clear();  // Fall back to linear scan on OOM.
   1298      }
   1299    }
   1300 
   1301    if (!transferableObjects.append(tObj)) {
   1302      return false;
   1303    }
   1304  }
   1305 
   1306  return true;
   1307 }
   1308 
   1309 template <typename... Args>
   1310 bool JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId,
   1311                                                   Args&&... aArgs) {
   1312  ReportDataCloneError(context(), out.buf.callbacks_, errorId, out.buf.closure_,
   1313                       std::forward<Args>(aArgs)...);
   1314  return false;
   1315 }
   1316 
   1317 bool JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str) {
   1318  JSLinearString* linear = str->ensureLinear(context());
   1319  if (!linear) {
   1320    return false;
   1321  }
   1322 
   1323 #if FUZZING_JS_FUZZILLI
   1324  if (js::SupportDifferentialTesting()) {
   1325    // TODO we could always output a twoByteChar string
   1326    return true;
   1327  }
   1328 #endif
   1329 
   1330  static_assert(JSString::MAX_LENGTH < (1 << 30),
   1331                "String length must fit in 30 bits");
   1332 
   1333  // Try to share the underlying StringBuffer without copying the contents.
   1334  bool useBuffer = linear->hasStringBuffer() &&
   1335                   output().scope() == JS::StructuredCloneScope::SameProcess;
   1336 
   1337  uint32_t length = linear->length();
   1338  bool isLatin1 = linear->hasLatin1Chars();
   1339  uint32_t lengthAndBits =
   1340      length | (uint32_t(isLatin1) << 31) | (uint32_t(useBuffer) << 30);
   1341  if (!out.writePair(tag, lengthAndBits)) {
   1342    return false;
   1343  }
   1344 
   1345  if (useBuffer) {
   1346    mozilla::StringBuffer* buffer = linear->stringBuffer();
   1347    if (!out.buf.stringBufferRefsHeld_.emplaceBack(buffer)) {
   1348      ReportOutOfMemory(context());
   1349      return false;
   1350    }
   1351    uintptr_t p = reinterpret_cast<uintptr_t>(buffer);
   1352    return out.writeBytes(&p, sizeof(p));
   1353  }
   1354 
   1355  JS::AutoCheckCannotGC nogc;
   1356  return linear->hasLatin1Chars()
   1357             ? out.writeChars(linear->latin1Chars(nogc), length)
   1358             : out.writeChars(linear->twoByteChars(nogc), length);
   1359 }
   1360 
   1361 bool JSStructuredCloneWriter::writeBigInt(uint32_t tag, BigInt* bi) {
   1362  bool signBit = bi->isNegative();
   1363  size_t length = bi->digitLength();
   1364  // The length must fit in 31 bits to leave room for a sign bit.
   1365  if (length > size_t(INT32_MAX)) {
   1366    return false;
   1367  }
   1368  uint32_t lengthAndSign = length | (static_cast<uint32_t>(signBit) << 31);
   1369 
   1370  if (!out.writePair(tag, lengthAndSign)) {
   1371    return false;
   1372  }
   1373  return out.writeArray(bi->digits().data(), length);
   1374 }
   1375 
   1376 inline void JSStructuredCloneWriter::checkStack() {
   1377 #ifdef DEBUG
   1378  // To avoid making serialization O(n^2), limit stack-checking at 10.
   1379  const size_t MAX = 10;
   1380 
   1381  size_t limit = std::min(counts.length(), MAX);
   1382  MOZ_ASSERT(objs.length() == counts.length());
   1383  size_t total = 0;
   1384  for (size_t i = 0; i < limit; i++) {
   1385    MOZ_ASSERT(total + counts[i] >= total);
   1386    total += counts[i];
   1387  }
   1388  if (counts.length() <= MAX) {
   1389    MOZ_ASSERT(total == objectEntries.length() + otherEntries.length());
   1390  } else {
   1391    MOZ_ASSERT(total <= objectEntries.length() + otherEntries.length());
   1392  }
   1393 
   1394  size_t j = objs.length();
   1395  for (size_t i = 0; i < limit; i++) {
   1396    --j;
   1397    MOZ_ASSERT(memory.has(&objs[j].toObject()));
   1398  }
   1399 #endif
   1400 }
   1401 
   1402 /*
   1403 * Write out a typed array. Note that post-v1 structured clone buffers do not
   1404 * perform endianness conversion on stored data, so multibyte typed arrays
   1405 * cannot be deserialized into a different endianness machine. Endianness
   1406 * conversion would prevent sharing ArrayBuffers: if you have Int8Array and
   1407 * Int16Array views of the same ArrayBuffer, should the data bytes be
   1408 * byte-swapped when writing or not? The Int8Array requires them to not be
   1409 * swapped; the Int16Array requires that they are.
   1410 */
   1411 bool JSStructuredCloneWriter::writeTypedArray(HandleObject obj) {
   1412  Rooted<TypedArrayObject*> tarr(context(),
   1413                                 obj->maybeUnwrapAs<TypedArrayObject>());
   1414  JSAutoRealm ar(context(), tarr);
   1415 
   1416 #ifdef FUZZING_JS_FUZZILLI
   1417  if (js::SupportDifferentialTesting() && !tarr->hasBuffer()) {
   1418    // fake oom because differential testing will fail
   1419    fprintf(stderr, "[unhandlable oom]");
   1420    _exit(-1);
   1421    return false;
   1422  }
   1423 #endif
   1424 
   1425  if (!TypedArrayObject::ensureHasBuffer(context(), tarr)) {
   1426    return false;
   1427  }
   1428 
   1429  if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, uint32_t(tarr->type()))) {
   1430    return false;
   1431  }
   1432 
   1433  mozilla::Maybe<size_t> nelems = tarr->length();
   1434  if (!nelems) {
   1435    return reportDataCloneError(JS_SCERR_TYPED_ARRAY_DETACHED);
   1436  }
   1437 
   1438  // Auto-length TypedArrays are tagged by storing `-1` for the length. We still
   1439  // need to query the length to check for detached or out-of-bounds lengths.
   1440  bool isAutoLength = tarr->is<ResizableTypedArrayObject>() &&
   1441                      tarr->as<ResizableTypedArrayObject>().isAutoLength();
   1442  uint64_t length = isAutoLength ? uint64_t(-1) : uint64_t(*nelems);
   1443  if (!out.write(length)) {
   1444    return false;
   1445  }
   1446 
   1447  // Write out the ArrayBuffer tag and contents
   1448  RootedValue val(context(), tarr->bufferValue());
   1449  if (!startWrite(val)) {
   1450    return false;
   1451  }
   1452 
   1453  uint64_t byteOffset = tarr->byteOffset().valueOr(0);
   1454  return out.write(byteOffset);
   1455 }
   1456 
   1457 bool JSStructuredCloneWriter::writeDataView(HandleObject obj) {
   1458  Rooted<DataViewObject*> view(context(), obj->maybeUnwrapAs<DataViewObject>());
   1459  JSAutoRealm ar(context(), view);
   1460 
   1461  if (!out.writePair(SCTAG_DATA_VIEW_OBJECT, 0)) {
   1462    return false;
   1463  }
   1464 
   1465  mozilla::Maybe<size_t> byteLength = view->byteLength();
   1466  if (!byteLength) {
   1467    return reportDataCloneError(JS_SCERR_TYPED_ARRAY_DETACHED);
   1468  }
   1469 
   1470  // Auto-length DataViews are tagged by storing `-1` for the length. We still
   1471  // need to query the length to check for detached or out-of-bounds lengths.
   1472  bool isAutoLength = view->is<ResizableDataViewObject>() &&
   1473                      view->as<ResizableDataViewObject>().isAutoLength();
   1474  uint64_t length = isAutoLength ? uint64_t(-1) : uint64_t(*byteLength);
   1475  if (!out.write(length)) {
   1476    return false;
   1477  }
   1478 
   1479  // Write out the ArrayBuffer tag and contents
   1480  RootedValue val(context(), view->bufferValue());
   1481  if (!startWrite(val)) {
   1482    return false;
   1483  }
   1484 
   1485  uint64_t byteOffset = view->byteOffset().valueOr(0);
   1486  return out.write(byteOffset);
   1487 }
   1488 
   1489 bool JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj) {
   1490  Rooted<ArrayBufferObject*> buffer(context(),
   1491                                    obj->maybeUnwrapAs<ArrayBufferObject>());
   1492  JSAutoRealm ar(context(), buffer);
   1493 
   1494  StructuredDataType type =
   1495      buffer->isResizable()   ? SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT
   1496      : buffer->isImmutable() ? SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT
   1497                              : SCTAG_ARRAY_BUFFER_OBJECT;
   1498 
   1499  if (!out.writePair(type, 0)) {
   1500    return false;
   1501  }
   1502 
   1503  uint64_t byteLength = buffer->byteLength();
   1504  if (!out.write(byteLength)) {
   1505    return false;
   1506  }
   1507 
   1508  if (buffer->isResizable()) {
   1509    uint64_t maxByteLength =
   1510        buffer->as<ResizableArrayBufferObject>().maxByteLength();
   1511    if (!out.write(maxByteLength)) {
   1512      return false;
   1513    }
   1514  }
   1515 
   1516  return out.writeBytes(buffer->dataPointer(), byteLength);
   1517 }
   1518 
   1519 bool JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj) {
   1520  MOZ_ASSERT(obj->canUnwrapAs<SharedArrayBufferObject>());
   1521 
   1522  if (!cloneDataPolicy.areSharedMemoryObjectsAllowed()) {
   1523    auto error = context()->realm()->creationOptions().getCoopAndCoepEnabled()
   1524                     ? JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP
   1525                     : JS_SCERR_NOT_CLONABLE;
   1526    reportDataCloneError(error, "SharedArrayBuffer");
   1527    return false;
   1528  }
   1529 
   1530  output().sameProcessScopeRequired();
   1531 
   1532  // We must not transmit SAB pointers (including for WebAssembly.Memory)
   1533  // cross-process.  The cloneDataPolicy should have guarded against this;
   1534  // since it did not then throw, with a very explicit message.
   1535 
   1536  if (output().scope() > JS::StructuredCloneScope::SameProcess) {
   1537    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   1538                              JSMSG_SC_SHMEM_POLICY);
   1539    return false;
   1540  }
   1541 
   1542  Rooted<SharedArrayBufferObject*> sharedArrayBuffer(
   1543      context(), obj->maybeUnwrapAs<SharedArrayBufferObject>());
   1544  SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
   1545 
   1546  if (!out.buf.refsHeld_.acquire(context(), rawbuf)) {
   1547    return false;
   1548  }
   1549 
   1550  // We must serialize the length so that the buffer object arrives in the
   1551  // receiver with the same length, and not with the length read from the
   1552  // rawbuf - that length can be different, and it can change at any time.
   1553 
   1554  StructuredDataType type = !sharedArrayBuffer->isGrowable()
   1555                                ? SCTAG_SHARED_ARRAY_BUFFER_OBJECT
   1556                                : SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT;
   1557 
   1558  intptr_t p = reinterpret_cast<intptr_t>(rawbuf);
   1559  uint64_t byteLength = sharedArrayBuffer->byteLengthOrMaxByteLength();
   1560  if (!(out.writePair(type, /* unused data word */ 0) &&
   1561        out.writeBytes(&byteLength, sizeof(byteLength)) &&
   1562        out.writeBytes(&p, sizeof(p)))) {
   1563    return false;
   1564  }
   1565 
   1566  if (callbacks && callbacks->sabCloned &&
   1567      !callbacks->sabCloned(context(), /*receiving=*/false, closure)) {
   1568    return false;
   1569  }
   1570 
   1571  return true;
   1572 }
   1573 
   1574 bool JSStructuredCloneWriter::writeSharedWasmMemory(HandleObject obj) {
   1575  MOZ_ASSERT(obj->canUnwrapAs<WasmMemoryObject>());
   1576 
   1577  // Check the policy here so that we can report a sane error.
   1578  if (!cloneDataPolicy.areSharedMemoryObjectsAllowed()) {
   1579    auto error = context()->realm()->creationOptions().getCoopAndCoepEnabled()
   1580                     ? JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP
   1581                     : JS_SCERR_NOT_CLONABLE;
   1582    reportDataCloneError(error, "WebAssembly.Memory");
   1583    return false;
   1584  }
   1585 
   1586  // If this changes, might need to change what we write.
   1587  MOZ_ASSERT(WasmMemoryObject::RESERVED_SLOTS == 3);
   1588 
   1589  Rooted<WasmMemoryObject*> memoryObj(context(),
   1590                                      &obj->unwrapAs<WasmMemoryObject>());
   1591  Rooted<SharedArrayBufferObject*> sab(
   1592      context(), &memoryObj->buffer().as<SharedArrayBufferObject>());
   1593 
   1594  return out.writePair(SCTAG_SHARED_WASM_MEMORY_OBJECT, 0) &&
   1595         out.writePair(SCTAG_BOOLEAN, memoryObj->isHuge()) &&
   1596         writeSharedArrayBuffer(sab);
   1597 }
   1598 
   1599 bool JSStructuredCloneWriter::startObject(HandleObject obj, bool* backref) {
   1600  // Handle cycles in the object graph.
   1601  CloneMemory::AddPtr p = memory.lookupForAdd(obj);
   1602  if ((*backref = p.found())) {
   1603    return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value());
   1604  }
   1605  if (!memory.add(p, obj, memory.count())) {
   1606    ReportOutOfMemory(context());
   1607    return false;
   1608  }
   1609 
   1610  if (memory.count() == UINT32_MAX) {
   1611    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   1612                              JSMSG_NEED_DIET, "object graph to serialize");
   1613    return false;
   1614  }
   1615 
   1616  return true;
   1617 }
   1618 
   1619 static bool TryAppendNativeProperties(JSContext* cx, HandleObject obj,
   1620                                      MutableHandleIdVector entries,
   1621                                      size_t* properties, bool* optimized) {
   1622  *optimized = false;
   1623 
   1624  if (!obj->is<NativeObject>()) {
   1625    return true;
   1626  }
   1627 
   1628  Handle<NativeObject*> nobj = obj.as<NativeObject>();
   1629  if (nobj->isIndexed() || nobj->is<TypedArrayObject>() ||
   1630      nobj->getClass()->getNewEnumerate() || nobj->getClass()->getEnumerate()) {
   1631    return true;
   1632  }
   1633 
   1634  *optimized = true;
   1635 
   1636  size_t count = 0;
   1637  // We iterate from the last to the first property, so the property names
   1638  // are already in reverse order.
   1639  for (ShapePropertyIter<NoGC> iter(nobj->shape()); !iter.done(); iter++) {
   1640    jsid id = iter->key();
   1641 
   1642    // Ignore symbols and non-enumerable properties.
   1643    if (!iter->enumerable() || id.isSymbol()) {
   1644      continue;
   1645    }
   1646 
   1647    MOZ_ASSERT(id.isString());
   1648    if (!entries.append(id)) {
   1649      return false;
   1650    }
   1651 
   1652    count++;
   1653  }
   1654 
   1655  // Add dense element ids in reverse order.
   1656  for (uint32_t i = nobj->getDenseInitializedLength(); i > 0; --i) {
   1657    if (nobj->getDenseElement(i - 1).isMagic(JS_ELEMENTS_HOLE)) {
   1658      continue;
   1659    }
   1660 
   1661    if (!entries.append(PropertyKey::Int(i - 1))) {
   1662      return false;
   1663    }
   1664 
   1665    count++;
   1666  }
   1667 
   1668  *properties = count;
   1669  return true;
   1670 }
   1671 
   1672 // Objects are written as a "preorder" traversal of the object graph: object
   1673 // "headers" (the class tag and any data needed for initial construction) are
   1674 // visited first, then the children are recursed through (where children are
   1675 // properties, Set or Map entries, etc.). So for example
   1676 //
   1677 //     obj1 = { key1: { key1.1: val1.1, key1.2: val1.2 }, key2: {} }
   1678 //
   1679 // would be stored as:
   1680 //
   1681 //     <Object tag for obj1>
   1682 //       <key1 data>
   1683 //       <Object tag for key1's value>
   1684 //         <key1.1 data>
   1685 //         <val1.1 data>
   1686 //         <key1.2 data>
   1687 //         <val1.2 data>
   1688 //       <end-of-children marker for key1's value>
   1689 //       <key2 data>
   1690 //       <Object tag for key2's value>
   1691 //       <end-of-children marker for key2's value>
   1692 //     <end-of-children marker for obj1>
   1693 //
   1694 // This nests nicely (ie, an entire recursive value starts with its tag and
   1695 // ends with its end-of-children marker) and so it can be presented indented.
   1696 // But see traverseMap below for how this looks different for Maps.
   1697 bool JSStructuredCloneWriter::traverseObject(HandleObject obj, ESClass cls) {
   1698  size_t count;
   1699  bool optimized = false;
   1700  if (!js::SupportDifferentialTesting()) {
   1701    if (!TryAppendNativeProperties(context(), obj, &objectEntries, &count,
   1702                                   &optimized)) {
   1703      return false;
   1704    }
   1705  }
   1706 
   1707  if (!optimized) {
   1708    // Get enumerable property ids and put them in reverse order so that they
   1709    // will come off the stack in forward order.
   1710    RootedIdVector properties(context());
   1711    if (!GetPropertyKeys(context(), obj, JSITER_OWNONLY, &properties)) {
   1712      return false;
   1713    }
   1714 
   1715    for (size_t i = properties.length(); i > 0; --i) {
   1716      jsid id = properties[i - 1];
   1717 
   1718      MOZ_ASSERT(id.isString() || id.isInt());
   1719      if (!objectEntries.append(id)) {
   1720        return false;
   1721      }
   1722    }
   1723 
   1724    count = properties.length();
   1725  }
   1726 
   1727  // Push obj and count to the stack.
   1728  if (!objs.append(ObjectValue(*obj)) || !counts.append(count)) {
   1729    return false;
   1730  }
   1731 
   1732  checkStack();
   1733 
   1734 #if DEBUG
   1735  ESClass cls2;
   1736  if (!GetBuiltinClass(context(), obj, &cls2)) {
   1737    return false;
   1738  }
   1739  MOZ_ASSERT(cls2 == cls);
   1740 #endif
   1741 
   1742  // Write the header for obj.
   1743  if (cls == ESClass::Array) {
   1744    uint32_t length = 0;
   1745    if (!JS::GetArrayLength(context(), obj, &length)) {
   1746      return false;
   1747    }
   1748 
   1749    return out.writePair(SCTAG_ARRAY_OBJECT,
   1750                         NativeEndian::swapToLittleEndian(length));
   1751  }
   1752 
   1753  return out.writePair(SCTAG_OBJECT_OBJECT, 0);
   1754 }
   1755 
   1756 // Use the same basic setup as for traverseObject, but now keys can themselves
   1757 // be complex objects. Keys and values are visited first via startWrite(), then
   1758 // the key's children (if any) are handled, then the value's children.
   1759 //
   1760 //     m = new Map();
   1761 //     m.set(key1 = ..., value1 = ...)
   1762 //
   1763 // where key1 and value2 are both objects would be stored as
   1764 //
   1765 //     <Map tag>
   1766 //     <key1 class tag>
   1767 //     <value1 class tag>
   1768 //     ...key1 fields...
   1769 //     <end-of-children marker for key1>
   1770 //     ...value1 fields...
   1771 //     <end-of-children marker for value1>
   1772 //     <end-of-children marker for Map>
   1773 //
   1774 // Notice how the end-of-children marker for key1 is sandwiched between the
   1775 // value1 beginning and end.
   1776 bool JSStructuredCloneWriter::traverseMap(HandleObject obj) {
   1777  Rooted<GCVector<Value>> newEntries(context(), GCVector<Value>(context()));
   1778  {
   1779    // If there is no wrapper, the compartment munging is a no-op.
   1780    Rooted<MapObject*> unwrapped(context(), obj->maybeUnwrapAs<MapObject>());
   1781    MOZ_ASSERT(unwrapped);
   1782    JSAutoRealm ar(context(), unwrapped);
   1783    if (!unwrapped->getKeysAndValuesInterleaved(&newEntries)) {
   1784      return false;
   1785    }
   1786  }
   1787  if (!context()->compartment()->wrap(context(), &newEntries)) {
   1788    return false;
   1789  }
   1790 
   1791  for (size_t i = newEntries.length(); i > 0; --i) {
   1792    if (!otherEntries.append(newEntries[i - 1])) {
   1793      return false;
   1794    }
   1795  }
   1796 
   1797  // Push obj and count to the stack.
   1798  if (!objs.append(ObjectValue(*obj)) || !counts.append(newEntries.length())) {
   1799    return false;
   1800  }
   1801 
   1802  checkStack();
   1803 
   1804  // Write the header for obj.
   1805  return out.writePair(SCTAG_MAP_OBJECT, 0);
   1806 }
   1807 
   1808 // Similar to traverseMap, only there is a single value instead of a key and
   1809 // value, and thus no interleaving is possible: a value will be fully emitted
   1810 // before the next value is begun.
   1811 bool JSStructuredCloneWriter::traverseSet(HandleObject obj) {
   1812  Rooted<GCVector<Value>> keys(context(), GCVector<Value>(context()));
   1813  {
   1814    // If there is no wrapper, the compartment munging is a no-op.
   1815    Rooted<SetObject*> unwrapped(context(), obj->maybeUnwrapAs<SetObject>());
   1816    MOZ_ASSERT(unwrapped);
   1817    JSAutoRealm ar(context(), unwrapped);
   1818    if (!unwrapped->keys(&keys)) {
   1819      return false;
   1820    }
   1821  }
   1822  if (!context()->compartment()->wrap(context(), &keys)) {
   1823    return false;
   1824  }
   1825 
   1826  for (size_t i = keys.length(); i > 0; --i) {
   1827    if (!otherEntries.append(keys[i - 1])) {
   1828      return false;
   1829    }
   1830  }
   1831 
   1832  // Push obj and count to the stack.
   1833  if (!objs.append(ObjectValue(*obj)) || !counts.append(keys.length())) {
   1834    return false;
   1835  }
   1836 
   1837  checkStack();
   1838 
   1839  // Write the header for obj.
   1840  return out.writePair(SCTAG_SET_OBJECT, 0);
   1841 }
   1842 
   1843 bool JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj) {
   1844  Rooted<SavedFrame*> savedFrame(context(), obj->maybeUnwrapAs<SavedFrame>());
   1845  MOZ_ASSERT(savedFrame);
   1846 
   1847  RootedObject parent(context(), savedFrame->getParent());
   1848  if (!context()->compartment()->wrap(context(), &parent)) {
   1849    return false;
   1850  }
   1851 
   1852  if (!objs.append(ObjectValue(*obj)) ||
   1853      !otherEntries.append(parent ? ObjectValue(*parent) : NullValue()) ||
   1854      !counts.append(1)) {
   1855    return false;
   1856  }
   1857 
   1858  checkStack();
   1859 
   1860  // Write the SavedFrame tag and the SavedFrame's principals.
   1861 
   1862  if (savedFrame->getPrincipals() ==
   1863      &ReconstructedSavedFramePrincipals::IsSystem) {
   1864    if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
   1865                       SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM)) {
   1866      return false;
   1867    };
   1868  } else if (savedFrame->getPrincipals() ==
   1869             &ReconstructedSavedFramePrincipals::IsNotSystem) {
   1870    if (!out.writePair(
   1871            SCTAG_SAVED_FRAME_OBJECT,
   1872            SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM)) {
   1873      return false;
   1874    }
   1875  } else {
   1876    if (auto principals = savedFrame->getPrincipals()) {
   1877      if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) ||
   1878          !principals->write(context(), this)) {
   1879        return false;
   1880      }
   1881    } else {
   1882      if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS)) {
   1883        return false;
   1884      }
   1885    }
   1886  }
   1887 
   1888  // Write the SavedFrame's reserved slots, except for the parent, which is
   1889  // queued on objs for further traversal.
   1890 
   1891  RootedValue val(context());
   1892 
   1893  val = BooleanValue(savedFrame->getMutedErrors());
   1894  if (!writePrimitive(val)) {
   1895    return false;
   1896  }
   1897 
   1898  context()->markAtom(savedFrame->getSource());
   1899  val = StringValue(savedFrame->getSource());
   1900  if (!writePrimitive(val)) {
   1901    return false;
   1902  }
   1903 
   1904  val = NumberValue(savedFrame->getLine());
   1905  if (!writePrimitive(val)) {
   1906    return false;
   1907  }
   1908 
   1909  val = NumberValue(*savedFrame->getColumn().addressOfValueForTranscode());
   1910  if (!writePrimitive(val)) {
   1911    return false;
   1912  }
   1913 
   1914  auto name = savedFrame->getFunctionDisplayName();
   1915  if (name) {
   1916    context()->markAtom(name);
   1917  }
   1918  val = name ? StringValue(name) : NullValue();
   1919  if (!writePrimitive(val)) {
   1920    return false;
   1921  }
   1922 
   1923  auto cause = savedFrame->getAsyncCause();
   1924  if (cause) {
   1925    context()->markAtom(cause);
   1926  }
   1927  val = cause ? StringValue(cause) : NullValue();
   1928  if (!writePrimitive(val)) {
   1929    return false;
   1930  }
   1931 
   1932  return true;
   1933 }
   1934 
   1935 // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
   1936 // 2.7.3 StructuredSerializeInternal ( value, forStorage [ , memory ] )
   1937 //
   1938 // Step 17. Otherwise, if value has an [[ErrorData]] internal slot and
   1939 //          value is not a platform object, then:
   1940 //
   1941 // Note: This contains custom extensions for handling non-standard properties.
   1942 bool JSStructuredCloneWriter::traverseError(HandleObject obj) {
   1943  JSContext* cx = context();
   1944 
   1945  // 1. Let name be ? Get(value, "name").
   1946  RootedValue name(cx);
   1947  if (!GetProperty(cx, obj, obj, cx->names().name, &name)) {
   1948    return false;
   1949  }
   1950 
   1951  // 2. If name is not one of "Error", "EvalError", "RangeError",
   1952  // "ReferenceError", "SyntaxError", "TypeError", or "URIError",
   1953  // (not yet specified: or "AggregateError")
   1954  // then set name to "Error".
   1955  JSExnType type = JSEXN_ERR;
   1956  if (name.isString()) {
   1957    JSLinearString* linear = name.toString()->ensureLinear(cx);
   1958    if (!linear) {
   1959      return false;
   1960    }
   1961 
   1962    if (EqualStrings(linear, cx->names().Error)) {
   1963      type = JSEXN_ERR;
   1964    } else if (EqualStrings(linear, cx->names().EvalError)) {
   1965      type = JSEXN_EVALERR;
   1966    } else if (EqualStrings(linear, cx->names().RangeError)) {
   1967      type = JSEXN_RANGEERR;
   1968    } else if (EqualStrings(linear, cx->names().ReferenceError)) {
   1969      type = JSEXN_REFERENCEERR;
   1970    } else if (EqualStrings(linear, cx->names().SyntaxError)) {
   1971      type = JSEXN_SYNTAXERR;
   1972    } else if (EqualStrings(linear, cx->names().TypeError)) {
   1973      type = JSEXN_TYPEERR;
   1974    } else if (EqualStrings(linear, cx->names().URIError)) {
   1975      type = JSEXN_URIERR;
   1976    } else if (EqualStrings(linear, cx->names().AggregateError)) {
   1977      type = JSEXN_AGGREGATEERR;
   1978    }
   1979  }
   1980 
   1981  // 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
   1982  RootedId messageId(cx, NameToId(cx->names().message));
   1983  Rooted<Maybe<PropertyDescriptor>> messageDesc(cx);
   1984  if (!GetOwnPropertyDescriptor(cx, obj, messageId, &messageDesc)) {
   1985    return false;
   1986  }
   1987 
   1988  // 4. Let message be undefined if IsDataDescriptor(valueMessageDesc) is false,
   1989  //    and ? ToString(valueMessageDesc.[[Value]]) otherwise.
   1990  RootedString message(cx);
   1991  if (messageDesc.isSome() && messageDesc->isDataDescriptor()) {
   1992    RootedValue messageVal(cx, messageDesc->value());
   1993    message = ToString<CanGC>(cx, messageVal);
   1994    if (!message) {
   1995      return false;
   1996    }
   1997  }
   1998 
   1999  // 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]:
   2000  // message }.
   2001 
   2002  if (!objs.append(ObjectValue(*obj))) {
   2003    return false;
   2004  }
   2005 
   2006  Rooted<ErrorObject*> unwrapped(cx, obj->maybeUnwrapAs<ErrorObject>());
   2007  MOZ_ASSERT(unwrapped);
   2008 
   2009  // Non-standard: Serialize |stack|.
   2010  // The Error stack property is saved as SavedFrames.
   2011  RootedValue stack(cx, NullValue());
   2012  RootedObject stackObj(cx, unwrapped->stack());
   2013  if (stackObj) {
   2014    MOZ_ASSERT(stackObj->canUnwrapAs<SavedFrame>());
   2015    stack.setObject(*stackObj);
   2016    if (!cx->compartment()->wrap(cx, &stack)) {
   2017      return false;
   2018    }
   2019  }
   2020  if (!otherEntries.append(stack)) {
   2021    return false;
   2022  }
   2023 
   2024  // Serialize |errors|
   2025  if (type == JSEXN_AGGREGATEERR) {
   2026    RootedValue errors(cx);
   2027    if (!GetProperty(cx, obj, obj, cx->names().errors, &errors)) {
   2028      return false;
   2029    }
   2030    if (!otherEntries.append(errors)) {
   2031      return false;
   2032    }
   2033  } else {
   2034    if (!otherEntries.append(NullValue())) {
   2035      return false;
   2036    }
   2037  }
   2038 
   2039  // Non-standard: Serialize |cause|. Because this property
   2040  // might be missing we also write "hasCause" later.
   2041  RootedId causeId(cx, NameToId(cx->names().cause));
   2042  Rooted<Maybe<PropertyDescriptor>> causeDesc(cx);
   2043  if (!GetOwnPropertyDescriptor(cx, obj, causeId, &causeDesc)) {
   2044    return false;
   2045  }
   2046 
   2047  Rooted<Maybe<Value>> cause(cx);
   2048  if (causeDesc.isSome() && causeDesc->isDataDescriptor()) {
   2049    cause = mozilla::Some(causeDesc->value());
   2050  }
   2051  if (!cx->compartment()->wrap(cx, &cause)) {
   2052    return false;
   2053  }
   2054  if (!otherEntries.append(cause.get().valueOr(NullValue()))) {
   2055    return false;
   2056  }
   2057 
   2058  // |cause| + |errors| + |stack|, pushed in reverse order
   2059  if (!counts.append(3)) {
   2060    return false;
   2061  }
   2062 
   2063  checkStack();
   2064 
   2065  if (!out.writePair(SCTAG_ERROR_OBJECT, type)) {
   2066    return false;
   2067  }
   2068 
   2069  RootedValue val(cx, message ? StringValue(message) : NullValue());
   2070  if (!writePrimitive(val)) {
   2071    return false;
   2072  }
   2073 
   2074  // hasCause
   2075  val = BooleanValue(cause.isSome());
   2076  if (!writePrimitive(val)) {
   2077    return false;
   2078  }
   2079 
   2080  // Non-standard: Also serialize fileName, lineNumber and columnNumber.
   2081  {
   2082    JSAutoRealm ar(cx, unwrapped);
   2083    val = StringValue(unwrapped->fileName(cx));
   2084  }
   2085  if (!cx->compartment()->wrap(cx, &val) || !writePrimitive(val)) {
   2086    return false;
   2087  }
   2088 
   2089  val = Int32Value(unwrapped->lineNumber());
   2090  if (!writePrimitive(val)) {
   2091    return false;
   2092  }
   2093 
   2094  val = Int32Value(*unwrapped->columnNumber().addressOfValueForTranscode());
   2095  return writePrimitive(val);
   2096 }
   2097 
   2098 bool JSStructuredCloneWriter::writePrimitive(HandleValue v) {
   2099  MOZ_ASSERT(v.isPrimitive());
   2100  context()->check(v);
   2101 
   2102  if (v.isString()) {
   2103    return writeString(SCTAG_STRING, v.toString());
   2104  } else if (v.isInt32()) {
   2105    if (js::SupportDifferentialTesting()) {
   2106      return out.writeDouble(v.toInt32());
   2107    }
   2108    return out.writePair(SCTAG_INT32, v.toInt32());
   2109  } else if (v.isDouble()) {
   2110    return out.writeDouble(v.toDouble());
   2111  } else if (v.isBoolean()) {
   2112    return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
   2113  } else if (v.isNull()) {
   2114    return out.writePair(SCTAG_NULL, 0);
   2115  } else if (v.isUndefined()) {
   2116    return out.writePair(SCTAG_UNDEFINED, 0);
   2117  } else if (v.isBigInt()) {
   2118    return writeBigInt(SCTAG_BIGINT, v.toBigInt());
   2119  }
   2120 
   2121  return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
   2122 }
   2123 
   2124 bool JSStructuredCloneWriter::startWrite(HandleValue v) {
   2125  context()->check(v);
   2126 
   2127  if (v.isPrimitive()) {
   2128    return writePrimitive(v);
   2129  }
   2130 
   2131  if (!v.isObject()) {
   2132    return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
   2133  }
   2134 
   2135  RootedObject obj(context(), &v.toObject());
   2136 
   2137  bool backref;
   2138  if (!startObject(obj, &backref)) {
   2139    return false;
   2140  }
   2141  if (backref) {
   2142    return true;
   2143  }
   2144 
   2145  ESClass cls;
   2146  if (!GetBuiltinClass(context(), obj, &cls)) {
   2147    return false;
   2148  }
   2149 
   2150  switch (cls) {
   2151    case ESClass::Object:
   2152    case ESClass::Array:
   2153      return traverseObject(obj, cls);
   2154    case ESClass::Number: {
   2155      RootedValue unboxed(context());
   2156      if (!Unbox(context(), obj, &unboxed)) {
   2157        return false;
   2158      }
   2159      return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
   2160             out.writeDouble(unboxed.toNumber());
   2161    }
   2162    case ESClass::String: {
   2163      RootedValue unboxed(context());
   2164      if (!Unbox(context(), obj, &unboxed)) {
   2165        return false;
   2166      }
   2167      return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
   2168    }
   2169    case ESClass::Boolean: {
   2170      RootedValue unboxed(context());
   2171      if (!Unbox(context(), obj, &unboxed)) {
   2172        return false;
   2173      }
   2174      return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
   2175    }
   2176    case ESClass::RegExp: {
   2177      RegExpShared* re = RegExpToShared(context(), obj);
   2178      if (!re) {
   2179        return false;
   2180      }
   2181      return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags().value()) &&
   2182             writeString(SCTAG_STRING, re->getSource());
   2183    }
   2184    case ESClass::ArrayBuffer: {
   2185      if (JS::IsArrayBufferObject(obj) && JS::ArrayBufferHasData(obj)) {
   2186        return writeArrayBuffer(obj);
   2187      }
   2188      break;
   2189    }
   2190    case ESClass::SharedArrayBuffer:
   2191      if (JS::IsSharedArrayBufferObject(obj)) {
   2192        return writeSharedArrayBuffer(obj);
   2193      }
   2194      break;
   2195    case ESClass::Date: {
   2196      RootedValue unboxed(context());
   2197      if (!Unbox(context(), obj, &unboxed)) {
   2198        return false;
   2199      }
   2200      return out.writePair(SCTAG_DATE_OBJECT, 0) &&
   2201             out.writeDouble(unboxed.toNumber());
   2202    }
   2203    case ESClass::Set:
   2204      return traverseSet(obj);
   2205    case ESClass::Map:
   2206      return traverseMap(obj);
   2207    case ESClass::Error:
   2208      return traverseError(obj);
   2209    case ESClass::BigInt: {
   2210      RootedValue unboxed(context());
   2211      if (!Unbox(context(), obj, &unboxed)) {
   2212        return false;
   2213      }
   2214      return writeBigInt(SCTAG_BIGINT_OBJECT, unboxed.toBigInt());
   2215    }
   2216    case ESClass::Promise:
   2217    case ESClass::MapIterator:
   2218    case ESClass::SetIterator:
   2219    case ESClass::Arguments:
   2220    case ESClass::Function:
   2221      break;
   2222    case ESClass::Other: {
   2223      if (obj->canUnwrapAs<TypedArrayObject>()) {
   2224        return writeTypedArray(obj);
   2225      }
   2226      if (obj->canUnwrapAs<DataViewObject>()) {
   2227        return writeDataView(obj);
   2228      }
   2229      if (wasm::IsSharedWasmMemoryObject(obj)) {
   2230        return writeSharedWasmMemory(obj);
   2231      }
   2232      if (obj->canUnwrapAs<SavedFrame>()) {
   2233        return traverseSavedFrame(obj);
   2234      }
   2235      break;
   2236    }
   2237  }
   2238 
   2239  if (out.buf.callbacks_ && out.buf.callbacks_->write) {
   2240    bool sameProcessScopeRequired = false;
   2241    if (!out.buf.callbacks_->write(context(), this, obj,
   2242                                   &sameProcessScopeRequired,
   2243                                   out.buf.closure_)) {
   2244      return false;
   2245    }
   2246 
   2247    if (sameProcessScopeRequired) {
   2248      output().sameProcessScopeRequired();
   2249    }
   2250 
   2251    return true;
   2252  }
   2253 
   2254  return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
   2255 }
   2256 
   2257 bool JSStructuredCloneWriter::writeHeader() {
   2258  return out.writePair(SCTAG_HEADER, (uint32_t)output().scope());
   2259 }
   2260 
   2261 bool JSStructuredCloneWriter::writeTransferMap() {
   2262  if (transferableObjects.empty()) {
   2263    return true;
   2264  }
   2265 
   2266  if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD)) {
   2267    return false;
   2268  }
   2269 
   2270  if (!out.write(transferableObjects.length())) {
   2271    return false;
   2272  }
   2273 
   2274  RootedObject obj(context());
   2275  for (auto* o : transferableObjects) {
   2276    obj = o;
   2277    if (!memory.put(obj, memory.count())) {
   2278      ReportOutOfMemory(context());
   2279      return false;
   2280    }
   2281 
   2282    // Emit a placeholder pointer.  We defer stealing the data until later
   2283    // (and, if necessary, detaching this object if it's an ArrayBuffer).
   2284    if (!out.writePair(SCTAG_TRANSFER_MAP_PENDING_ENTRY,
   2285                       JS::SCTAG_TMO_UNFILLED)) {
   2286      return false;
   2287    }
   2288    if (!out.write(0)) {  // Pointer to ArrayBuffer contents.
   2289      return false;
   2290    }
   2291    if (!out.write(0)) {  // extraData
   2292      return false;
   2293    }
   2294  }
   2295 
   2296  return true;
   2297 }
   2298 
   2299 bool JSStructuredCloneWriter::transferOwnership() {
   2300  if (transferableObjects.empty()) {
   2301    return true;
   2302  }
   2303 
   2304  // Walk along the transferables and the transfer map at the same time,
   2305  // grabbing out pointers from the transferables and stuffing them into the
   2306  // transfer map.
   2307  auto point = out.iter();
   2308  MOZ_RELEASE_ASSERT(point.canPeek());
   2309  MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) ==
   2310             SCTAG_HEADER);
   2311  point++;
   2312  MOZ_RELEASE_ASSERT(point.canPeek());
   2313  MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) ==
   2314             SCTAG_TRANSFER_MAP_HEADER);
   2315  point++;
   2316  MOZ_RELEASE_ASSERT(point.canPeek());
   2317  MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) ==
   2318             transferableObjects.length());
   2319  point++;
   2320 
   2321  JSContext* cx = context();
   2322  RootedObject obj(cx);
   2323  JS::StructuredCloneScope scope = output().scope();
   2324  for (auto* o : transferableObjects) {
   2325    obj = o;
   2326 
   2327    uint32_t tag;
   2328    JS::TransferableOwnership ownership;
   2329    void* content;
   2330    uint64_t extraData;
   2331 
   2332 #if DEBUG
   2333    SCInput::getPair(point.peek(), &tag, (uint32_t*)&ownership);
   2334    MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
   2335    MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
   2336 #endif
   2337 
   2338    ESClass cls;
   2339    if (!GetBuiltinClass(cx, obj, &cls)) {
   2340      return false;
   2341    }
   2342 
   2343    if (cls == ESClass::ArrayBuffer) {
   2344      tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
   2345 
   2346      // The current setup of the array buffer inheritance hierarchy doesn't
   2347      // lend itself well to generic manipulation via proxies.
   2348      Rooted<ArrayBufferObject*> arrayBuffer(
   2349          cx, obj->maybeUnwrapAs<ArrayBufferObject>());
   2350      JSAutoRealm ar(cx, arrayBuffer);
   2351 
   2352      MOZ_ASSERT(!arrayBuffer->isImmutable(),
   2353                 "Immutable array buffers can't be transferred");
   2354 
   2355      if (arrayBuffer->isDetached()) {
   2356        reportDataCloneError(JS_SCERR_TYPED_ARRAY_DETACHED);
   2357        return false;
   2358      }
   2359 
   2360      if (arrayBuffer->isPreparedForAsmJS()) {
   2361        reportDataCloneError(JS_SCERR_WASM_NO_TRANSFER);
   2362        return false;
   2363      }
   2364 
   2365      if (scope == JS::StructuredCloneScope::DifferentProcess ||
   2366          scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB ||
   2367          arrayBuffer->isResizable()) {
   2368        // Write Transferred ArrayBuffers in DifferentProcess scope at
   2369        // the end of the clone buffer, and store the offset within the
   2370        // buffer to where the ArrayBuffer was written. Note that this
   2371        // will invalidate the current position iterator.
   2372        //
   2373        // Resizable ArrayBuffers need to store two extra data, the byte length
   2374        // and the maximum byte length, but the current transferables format
   2375        // supports only a single additional datum. Therefore resizable buffers
   2376        // currently go through this slower code path.
   2377 
   2378        size_t pointOffset = out.offset(point);
   2379        tag = SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER;
   2380        ownership = JS::SCTAG_TMO_UNOWNED;
   2381        content = nullptr;
   2382        extraData = out.tell() -
   2383                    pointOffset;  // Offset from tag to current end of buffer
   2384        if (!writeArrayBuffer(arrayBuffer)) {
   2385          ReportOutOfMemory(cx);
   2386          return false;
   2387        }
   2388 
   2389        // Must refresh the point iterator after its collection has
   2390        // been modified.
   2391        point = out.iter();
   2392        point += pointOffset;
   2393 
   2394        if (!JS::DetachArrayBuffer(cx, arrayBuffer)) {
   2395          return false;
   2396        }
   2397      } else {
   2398        size_t nbytes = arrayBuffer->byteLength();
   2399 
   2400        using BufferContents = ArrayBufferObject::BufferContents;
   2401 
   2402        BufferContents bufContents =
   2403            ArrayBufferObject::extractStructuredCloneContents(cx, arrayBuffer);
   2404        if (!bufContents) {
   2405          return false;  // out of memory
   2406        }
   2407 
   2408        content = bufContents.data();
   2409        if (bufContents.kind() == ArrayBufferObject::MAPPED) {
   2410          ownership = JS::SCTAG_TMO_MAPPED_DATA;
   2411        } else {
   2412          MOZ_ASSERT(
   2413              bufContents.kind() ==
   2414                      ArrayBufferObject::MALLOCED_ARRAYBUFFER_CONTENTS_ARENA ||
   2415                  bufContents.kind() ==
   2416                      ArrayBufferObject::MALLOCED_UNKNOWN_ARENA,
   2417              "failing to handle new ArrayBuffer kind?");
   2418          ownership = JS::SCTAG_TMO_ALLOC_DATA;
   2419        }
   2420        extraData = nbytes;
   2421      }
   2422    } else {
   2423      if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer) {
   2424        return reportDataCloneError(JS_SCERR_TRANSFERABLE);
   2425      }
   2426      if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag,
   2427                                             &ownership, &content,
   2428                                             &extraData)) {
   2429        return false;
   2430      }
   2431      MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
   2432    }
   2433 
   2434    point.write(NativeEndian::swapToLittleEndian(PairToUInt64(tag, ownership)));
   2435    MOZ_ALWAYS_TRUE(point.advance());
   2436    point.write(
   2437        NativeEndian::swapToLittleEndian(reinterpret_cast<uint64_t>(content)));
   2438    MOZ_ALWAYS_TRUE(point.advance());
   2439    point.write(NativeEndian::swapToLittleEndian(extraData));
   2440    MOZ_ALWAYS_TRUE(point.advance());
   2441  }
   2442 
   2443 #if DEBUG
   2444  // Make sure there aren't any more transfer map entries after the expected
   2445  // number we read out.
   2446  if (!point.done()) {
   2447    uint32_t tag, data;
   2448    SCInput::getPair(point.peek(), &tag, &data);
   2449    MOZ_ASSERT(tag < SCTAG_TRANSFER_MAP_HEADER ||
   2450               tag >= SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES);
   2451  }
   2452 #endif
   2453  return true;
   2454 }
   2455 
   2456 bool JSStructuredCloneWriter::write(HandleValue v) {
   2457  if (!startWrite(v)) {
   2458    return false;
   2459  }
   2460 
   2461  RootedObject obj(context());
   2462  RootedValue key(context());
   2463  RootedValue val(context());
   2464  RootedId id(context());
   2465 
   2466  RootedValue cause(context());
   2467  RootedValue errors(context());
   2468  RootedValue stack(context());
   2469 
   2470  while (!counts.empty()) {
   2471    obj = &objs.back().toObject();
   2472    context()->check(obj);
   2473    if (counts.back()) {
   2474      counts.back()--;
   2475 
   2476      ESClass cls;
   2477      if (!GetBuiltinClass(context(), obj, &cls)) {
   2478        return false;
   2479      }
   2480 
   2481      if (cls == ESClass::Map) {
   2482        key = otherEntries.popCopy();
   2483        checkStack();
   2484 
   2485        counts.back()--;
   2486        val = otherEntries.popCopy();
   2487        checkStack();
   2488 
   2489        if (!startWrite(key) || !startWrite(val)) {
   2490          return false;
   2491        }
   2492      } else if (cls == ESClass::Set || obj->canUnwrapAs<SavedFrame>()) {
   2493        key = otherEntries.popCopy();
   2494        checkStack();
   2495 
   2496        if (!startWrite(key)) {
   2497          return false;
   2498        }
   2499      } else if (cls == ESClass::Error) {
   2500        cause = otherEntries.popCopy();
   2501        checkStack();
   2502 
   2503        counts.back()--;
   2504        errors = otherEntries.popCopy();
   2505        checkStack();
   2506 
   2507        counts.back()--;
   2508        stack = otherEntries.popCopy();
   2509        checkStack();
   2510 
   2511        if (!startWrite(cause) || !startWrite(errors) || !startWrite(stack)) {
   2512          return false;
   2513        }
   2514      } else {
   2515        id = objectEntries.popCopy();
   2516        key = IdToValue(id);
   2517        checkStack();
   2518 
   2519        // If obj still has an own property named id, write it out.
   2520        bool found;
   2521        if (GetOwnPropertyPure(context(), obj, id, val.address(), &found)) {
   2522          if (found) {
   2523            if (!writePrimitive(key) || !startWrite(val)) {
   2524              return false;
   2525            }
   2526          }
   2527          continue;
   2528        }
   2529 
   2530        if (!HasOwnProperty(context(), obj, id, &found)) {
   2531          return false;
   2532        }
   2533 
   2534        if (found) {
   2535 #if FUZZING_JS_FUZZILLI
   2536          // supress calls into user code
   2537          if (js::SupportDifferentialTesting()) {
   2538            fprintf(stderr, "Differential testing: cannot call GetProperty\n");
   2539            return false;
   2540          }
   2541 #endif
   2542 
   2543          if (!writePrimitive(key) ||
   2544              !GetProperty(context(), obj, obj, id, &val) || !startWrite(val)) {
   2545            return false;
   2546          }
   2547        }
   2548      }
   2549    } else {
   2550      if (!out.writePair(SCTAG_END_OF_KEYS, 0)) {
   2551        return false;
   2552      }
   2553      objs.popBack();
   2554      counts.popBack();
   2555    }
   2556  }
   2557 
   2558  memory.clear();
   2559  return transferOwnership();
   2560 }
   2561 
   2562 JSStructuredCloneReader::JSStructuredCloneReader(
   2563    SCInput& in, JS::StructuredCloneScope scope,
   2564    const JS::CloneDataPolicy& cloneDataPolicy,
   2565    const JSStructuredCloneCallbacks* cb, void* cbClosure)
   2566    : in(in),
   2567      allowedScope(scope),
   2568      cloneDataPolicy(cloneDataPolicy),
   2569      objs(in.context()),
   2570      objState(in.context(), in.context()),
   2571      allObjs(in.context()),
   2572      numItemsRead(0),
   2573      callbacks(cb),
   2574      closure(cbClosure),
   2575      gcHeap(in.context()) {
   2576  // Avoid the need to bounds check by keeping a never-matching element at the
   2577  // base of the `objState` stack. This append() will always succeed because
   2578  // the objState vector has a nonzero MinInlineCapacity.
   2579  MOZ_ALWAYS_TRUE(objState.append(std::make_pair(nullptr, true)));
   2580 }
   2581 
   2582 template <typename CharT>
   2583 JSString* JSStructuredCloneReader::readStringImpl(
   2584    uint32_t nchars, ShouldAtomizeStrings atomize) {
   2585  if (atomize) {
   2586    AtomStringChars<CharT> chars;
   2587    if (!chars.maybeAlloc(context(), nchars) ||
   2588        !in.readChars(chars.data(), nchars)) {
   2589      return nullptr;
   2590    }
   2591    return chars.toAtom(context(), nchars);
   2592  }
   2593 
   2594  // Uses `StringChars::unsafeData()` because `readChars` can report an error,
   2595  // which can trigger a GC.
   2596  StringChars<CharT> chars(context());
   2597  if (!chars.maybeAlloc(context(), nchars, gcHeap) ||
   2598      !in.readChars(chars.unsafeData(), nchars)) {
   2599    return nullptr;
   2600  }
   2601  return chars.template toStringDontDeflate<CanGC>(context(), nchars, gcHeap);
   2602 }
   2603 
   2604 JSString* JSStructuredCloneReader::readString(uint32_t data,
   2605                                              ShouldAtomizeStrings atomize) {
   2606  uint32_t nchars = data & BitMask(30);
   2607  bool latin1 = data & (1 << 31);
   2608  bool hasBuffer = data & (1 << 30);
   2609 
   2610  if (nchars > JSString::MAX_LENGTH) {
   2611    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2612                              JSMSG_SC_BAD_SERIALIZED_DATA, "string length");
   2613    return nullptr;
   2614  }
   2615 
   2616  if (hasBuffer) {
   2617    if (allowedScope > JS::StructuredCloneScope::SameProcess) {
   2618      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2619                                JSMSG_SC_BAD_SERIALIZED_DATA,
   2620                                "invalid scope for string buffer");
   2621      return nullptr;
   2622    }
   2623 
   2624    uintptr_t p;
   2625    if (!in.readBytes(&p, sizeof(p))) {
   2626      in.reportTruncated();
   2627      return nullptr;
   2628    }
   2629    RefPtr<mozilla::StringBuffer> buffer(
   2630        reinterpret_cast<mozilla::StringBuffer*>(p));
   2631    JSContext* cx = context();
   2632    if (atomize) {
   2633      if (latin1) {
   2634        return AtomizeChars(cx, static_cast<Latin1Char*>(buffer->Data()),
   2635                            nchars);
   2636      }
   2637      return AtomizeChars(cx, static_cast<char16_t*>(buffer->Data()), nchars);
   2638    }
   2639    if (latin1) {
   2640      Rooted<JSString::OwnedChars<Latin1Char>> owned(cx, std::move(buffer),
   2641                                                     nchars);
   2642      return JSLinearString::newValidLength<CanGC, Latin1Char>(cx, &owned,
   2643                                                               gcHeap);
   2644    }
   2645    Rooted<JSString::OwnedChars<char16_t>> owned(cx, std::move(buffer), nchars);
   2646    return JSLinearString::newValidLength<CanGC, char16_t>(cx, &owned, gcHeap);
   2647  }
   2648 
   2649  return latin1 ? readStringImpl<Latin1Char>(nchars, atomize)
   2650                : readStringImpl<char16_t>(nchars, atomize);
   2651 }
   2652 
   2653 [[nodiscard]] bool JSStructuredCloneReader::readUint32(uint32_t* num) {
   2654  Rooted<Value> lineVal(context());
   2655  if (!startRead(&lineVal)) {
   2656    return false;
   2657  }
   2658  if (!lineVal.isInt32()) {
   2659    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2660                              JSMSG_SC_BAD_SERIALIZED_DATA, "integer required");
   2661    return false;
   2662  }
   2663  *num = uint32_t(lineVal.toInt32());
   2664  return true;
   2665 }
   2666 
   2667 BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) {
   2668  size_t length = data & BitMask(31);
   2669  bool isNegative = data & (1 << 31);
   2670  if (length == 0) {
   2671    return BigInt::zero(context());
   2672  }
   2673  RootedBigInt result(context(), BigInt::createUninitialized(
   2674                                     context(), length, isNegative, gcHeap));
   2675  if (!result) {
   2676    return nullptr;
   2677  }
   2678  if (!in.readArray(result->digits().data(), length)) {
   2679    return nullptr;
   2680  }
   2681  return JS::BigInt::destructivelyTrimHighZeroDigits(context(), result);
   2682 }
   2683 
   2684 static uint32_t TagToV1ArrayType(uint32_t tag) {
   2685  MOZ_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN &&
   2686             tag <= SCTAG_TYPED_ARRAY_V1_MAX);
   2687  return tag - SCTAG_TYPED_ARRAY_V1_MIN;
   2688 }
   2689 
   2690 bool JSStructuredCloneReader::readTypedArray(uint32_t arrayType,
   2691                                             uint64_t nelems,
   2692                                             MutableHandleValue vp,
   2693                                             bool v1Read) {
   2694  if (arrayType > (v1Read ? Scalar::Uint8Clamped : Scalar::Float16)) {
   2695    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2696                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2697                              "unhandled typed array element type");
   2698    return false;
   2699  }
   2700 
   2701  // Push a placeholder onto the allObjs list to stand in for the typed array.
   2702  uint32_t placeholderIndex = allObjs.length();
   2703  Value dummy = UndefinedValue();
   2704  if (!allObjs.append(dummy)) {
   2705    return false;
   2706  }
   2707 
   2708  // Auto-length TypedArrays are tagged by using `-1` for their length.
   2709  bool isAutoLength = nelems == uint64_t(-1);
   2710 
   2711  // Zero |nelems| if it was only used as a tag.
   2712  if (isAutoLength) {
   2713    nelems = 0;
   2714  }
   2715 
   2716  // Read the ArrayBuffer object and its contents (but no properties)
   2717  RootedValue v(context());
   2718  uint64_t byteOffset;
   2719  if (v1Read) {
   2720    MOZ_ASSERT(!isAutoLength, "v1Read can't produce auto-length TypedArrays");
   2721    if (!readV1ArrayBuffer(arrayType, nelems, &v)) {
   2722      return false;
   2723    }
   2724    byteOffset = 0;
   2725  } else {
   2726    if (!startRead(&v)) {
   2727      return false;
   2728    }
   2729    if (!in.read(&byteOffset)) {
   2730      return false;
   2731    }
   2732  }
   2733 
   2734  // Ensure invalid 64-bit values won't be truncated below.
   2735  if (nelems > ArrayBufferObject::ByteLengthLimit ||
   2736      byteOffset > ArrayBufferObject::ByteLengthLimit) {
   2737    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2738                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2739                              "invalid typed array length or offset");
   2740    return false;
   2741  }
   2742 
   2743  if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
   2744    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2745                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2746                              "typed array must be backed by an ArrayBuffer");
   2747    return false;
   2748  }
   2749 
   2750  RootedObject buffer(context(), &v.toObject());
   2751  RootedObject obj(context(), nullptr);
   2752 
   2753  // Negative values represent an absent length parameter.
   2754  int64_t length = isAutoLength ? -1 : int64_t(nelems);
   2755 
   2756  switch (arrayType) {
   2757 #define CREATE_FROM_BUFFER(ExternalType, NativeType, Name)             \
   2758  case Scalar::Name:                                                   \
   2759    obj = JS::TypedArray<Scalar::Name>::fromBuffer(context(), buffer,  \
   2760                                                   byteOffset, length) \
   2761              .asObject();                                             \
   2762    break;
   2763 
   2764    JS_FOR_EACH_TYPED_ARRAY(CREATE_FROM_BUFFER)
   2765 #undef CREATE_FROM_BUFFER
   2766 
   2767    default:
   2768      MOZ_CRASH("Can't happen: arrayType range checked above");
   2769  }
   2770 
   2771  if (!obj) {
   2772    return false;
   2773  }
   2774  vp.setObject(*obj);
   2775 
   2776  allObjs[placeholderIndex].set(vp);
   2777 
   2778  return true;
   2779 }
   2780 
   2781 bool JSStructuredCloneReader::readDataView(uint64_t byteLength,
   2782                                           MutableHandleValue vp) {
   2783  // Push a placeholder onto the allObjs list to stand in for the DataView.
   2784  uint32_t placeholderIndex = allObjs.length();
   2785  Value dummy = UndefinedValue();
   2786  if (!allObjs.append(dummy)) {
   2787    return false;
   2788  }
   2789 
   2790  // Read the ArrayBuffer object and its contents (but no properties).
   2791  RootedValue v(context());
   2792  if (!startRead(&v)) {
   2793    return false;
   2794  }
   2795  if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
   2796    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2797                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2798                              "DataView must be backed by an ArrayBuffer");
   2799    return false;
   2800  }
   2801 
   2802  // Read byteOffset.
   2803  uint64_t byteOffset;
   2804  if (!in.read(&byteOffset)) {
   2805    return false;
   2806  }
   2807 
   2808  // Auto-length DataViews are tagged by using `-1` for their byte length.
   2809  bool isAutoLength = byteLength == uint64_t(-1);
   2810 
   2811  // Zero |byteLength| if it was only used as a tag.
   2812  if (isAutoLength) {
   2813    byteLength = 0;
   2814  }
   2815 
   2816  // Ensure invalid 64-bit values won't be truncated below.
   2817  if (byteLength > ArrayBufferObject::ByteLengthLimit ||
   2818      byteOffset > ArrayBufferObject::ByteLengthLimit) {
   2819    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2820                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2821                              "invalid DataView length or offset");
   2822    return false;
   2823  }
   2824 
   2825  RootedObject buffer(context(), &v.toObject());
   2826  RootedObject obj(context());
   2827  if (!isAutoLength) {
   2828    obj = JS_NewDataView(context(), buffer, byteOffset, byteLength);
   2829  } else {
   2830    obj = js::NewDataView(context(), buffer, byteOffset);
   2831  }
   2832  if (!obj) {
   2833    return false;
   2834  }
   2835  vp.setObject(*obj);
   2836 
   2837  allObjs[placeholderIndex].set(vp);
   2838 
   2839  return true;
   2840 }
   2841 
   2842 bool JSStructuredCloneReader::readArrayBuffer(StructuredDataType type,
   2843                                              uint32_t data,
   2844                                              MutableHandleValue vp) {
   2845  // V2 stores the length in |data|. The current version stores the
   2846  // length separately to allow larger length values.
   2847  uint64_t nbytes = 0;
   2848  uint64_t maxbytes = 0;
   2849  if (type == SCTAG_ARRAY_BUFFER_OBJECT ||
   2850      type == SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
   2851    if (!in.read(&nbytes)) {
   2852      return false;
   2853    }
   2854  } else if (type == SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT) {
   2855    if (!in.read(&nbytes)) {
   2856      return false;
   2857    }
   2858    if (!in.read(&maxbytes)) {
   2859      return false;
   2860    }
   2861  } else {
   2862    MOZ_ASSERT(type == SCTAG_ARRAY_BUFFER_OBJECT_V2);
   2863    nbytes = data;
   2864  }
   2865 
   2866  // The maximum ArrayBuffer size depends on the platform, and we cast to size_t
   2867  // below, so we have to check this here.
   2868  if (nbytes > ArrayBufferObject::ByteLengthLimit ||
   2869      maxbytes > ArrayBufferObject::ByteLengthLimit) {
   2870    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2871                              JSMSG_BAD_ARRAY_LENGTH);
   2872    return false;
   2873  }
   2874 
   2875  JSObject* obj;
   2876  if (type == SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT) {
   2877    obj = ResizableArrayBufferObject::createZeroed(context(), size_t(nbytes),
   2878                                                   size_t(maxbytes));
   2879  } else if (type == SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
   2880    MOZ_ASSERT(maxbytes == 0);
   2881    obj = ImmutableArrayBufferObject::createZeroed(context(), size_t(nbytes));
   2882  } else {
   2883    MOZ_ASSERT(maxbytes == 0);
   2884    obj = ArrayBufferObject::createZeroed(context(), size_t(nbytes));
   2885  }
   2886  if (!obj) {
   2887    return false;
   2888  }
   2889  vp.setObject(*obj);
   2890  ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
   2891  MOZ_ASSERT(buffer.byteLength() == nbytes);
   2892  return in.readArray(buffer.dataPointer(), nbytes);
   2893 }
   2894 
   2895 bool JSStructuredCloneReader::readSharedArrayBuffer(StructuredDataType type,
   2896                                                    MutableHandleValue vp) {
   2897  MOZ_ASSERT(type == SCTAG_SHARED_ARRAY_BUFFER_OBJECT ||
   2898             type == SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT);
   2899 
   2900  if (!cloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed() ||
   2901      !cloneDataPolicy.areSharedMemoryObjectsAllowed()) {
   2902    auto error = context()->realm()->creationOptions().getCoopAndCoepEnabled()
   2903                     ? JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP
   2904                     : JS_SCERR_NOT_CLONABLE;
   2905    ReportDataCloneError(context(), callbacks, error, closure,
   2906                         "SharedArrayBuffer");
   2907    return false;
   2908  }
   2909 
   2910  uint64_t byteLength;
   2911  if (!in.readBytes(&byteLength, sizeof(byteLength))) {
   2912    return in.reportTruncated();
   2913  }
   2914 
   2915  // The maximum ArrayBuffer size depends on the platform, and we cast to size_t
   2916  // below, so we have to check this here.
   2917  if (byteLength > ArrayBufferObject::ByteLengthLimit) {
   2918    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2919                              JSMSG_BAD_ARRAY_LENGTH);
   2920    return false;
   2921  }
   2922 
   2923  intptr_t p;
   2924  if (!in.readBytes(&p, sizeof(p))) {
   2925    return in.reportTruncated();
   2926  }
   2927 
   2928  bool isGrowable = type == SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT;
   2929 
   2930  SharedArrayRawBuffer* rawbuf = reinterpret_cast<SharedArrayRawBuffer*>(p);
   2931  MOZ_RELEASE_ASSERT(rawbuf->isWasm() || isGrowable == rawbuf->isGrowableJS());
   2932 
   2933  // There's no guarantee that the receiving agent has enabled shared memory
   2934  // even if the transmitting agent has done so.  Ideally we'd check at the
   2935  // transmission point, but that's tricky, and it will be a very rare problem
   2936  // in any case.  Just fail at the receiving end if we can't handle it.
   2937 
   2938  if (!context()
   2939           ->realm()
   2940           ->creationOptions()
   2941           .getSharedMemoryAndAtomicsEnabled()) {
   2942    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2943                              JSMSG_SC_SAB_DISABLED);
   2944    return false;
   2945  }
   2946 
   2947  // The new object will have a new reference to the rawbuf.
   2948 
   2949  if (!rawbuf->addReference()) {
   2950    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   2951                              JSMSG_SC_SAB_REFCNT_OFLO);
   2952    return false;
   2953  }
   2954 
   2955  RootedObject obj(context());
   2956  if (!isGrowable) {
   2957    obj = SharedArrayBufferObject::New(context(), rawbuf, byteLength);
   2958  } else {
   2959    obj = SharedArrayBufferObject::NewGrowable(context(), rawbuf, byteLength);
   2960  }
   2961  if (!obj) {
   2962    rawbuf->dropReference();
   2963    return false;
   2964  }
   2965 
   2966  // `rawbuf` is now owned by `obj`.
   2967 
   2968  if (callbacks && callbacks->sabCloned &&
   2969      !callbacks->sabCloned(context(), /*receiving=*/true, closure)) {
   2970    return false;
   2971  }
   2972 
   2973  vp.setObject(*obj);
   2974  return true;
   2975 }
   2976 
   2977 bool JSStructuredCloneReader::readSharedWasmMemory(uint32_t nbytes,
   2978                                                   MutableHandleValue vp) {
   2979  JSContext* cx = context();
   2980  if (nbytes != 0) {
   2981    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2982                              JSMSG_SC_BAD_SERIALIZED_DATA,
   2983                              "invalid shared wasm memory tag");
   2984    return false;
   2985  }
   2986 
   2987  if (!cloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed() ||
   2988      !cloneDataPolicy.areSharedMemoryObjectsAllowed()) {
   2989    auto error = context()->realm()->creationOptions().getCoopAndCoepEnabled()
   2990                     ? JS_SCERR_NOT_CLONABLE_WITH_COOP_COEP
   2991                     : JS_SCERR_NOT_CLONABLE;
   2992    ReportDataCloneError(cx, callbacks, error, closure, "WebAssembly.Memory");
   2993    return false;
   2994  }
   2995 
   2996  // Read the isHuge flag
   2997  RootedValue isHuge(cx);
   2998  if (!startRead(&isHuge)) {
   2999    return false;
   3000  }
   3001 
   3002  // Read the SharedArrayBuffer object.
   3003  RootedValue payload(cx);
   3004  if (!startRead(&payload)) {
   3005    return false;
   3006  }
   3007  if (!payload.isObject() ||
   3008      !payload.toObject().is<SharedArrayBufferObject>() ||
   3009      payload.toObject().as<SharedArrayBufferObject>().isGrowable()) {
   3010    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3011                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3012                              "shared wasm memory must be backed by a "
   3013                              "non-growable SharedArrayBuffer");
   3014    return false;
   3015  }
   3016 
   3017  Rooted<ArrayBufferObjectMaybeShared*> sab(
   3018      cx, &payload.toObject().as<SharedArrayBufferObject>());
   3019 
   3020  // Construct the memory.
   3021  RootedObject proto(
   3022      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmMemory));
   3023  if (!proto) {
   3024    return false;
   3025  }
   3026  RootedObject memory(
   3027      cx, WasmMemoryObject::create(cx, sab, isHuge.toBoolean(), proto));
   3028  if (!memory) {
   3029    return false;
   3030  }
   3031 
   3032  vp.setObject(*memory);
   3033  return true;
   3034 }
   3035 
   3036 /*
   3037 * Read in the data for a structured clone version 1 ArrayBuffer, performing
   3038 * endianness-conversion while reading.
   3039 */
   3040 bool JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType,
   3041                                                uint32_t nelems,
   3042                                                MutableHandleValue vp) {
   3043  if (arrayType > Scalar::Uint8Clamped) {
   3044    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3045                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3046                              "invalid TypedArray type");
   3047    return false;
   3048  }
   3049 
   3050  mozilla::CheckedInt<size_t> nbytes =
   3051      mozilla::CheckedInt<size_t>(nelems) *
   3052      TypedArrayElemSize(static_cast<Scalar::Type>(arrayType));
   3053  if (!nbytes.isValid() || nbytes.value() > UINT32_MAX) {
   3054    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3055                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3056                              "invalid typed array size");
   3057    return false;
   3058  }
   3059 
   3060  JSObject* obj = ArrayBufferObject::createZeroed(context(), nbytes.value());
   3061  if (!obj) {
   3062    return false;
   3063  }
   3064  vp.setObject(*obj);
   3065  ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
   3066  MOZ_ASSERT(buffer.byteLength() == nbytes);
   3067 
   3068  switch (arrayType) {
   3069    case Scalar::Int8:
   3070    case Scalar::Uint8:
   3071    case Scalar::Uint8Clamped:
   3072      return in.readArray((uint8_t*)buffer.dataPointer(), nelems);
   3073    case Scalar::Int16:
   3074    case Scalar::Uint16:
   3075      return in.readArray((uint16_t*)buffer.dataPointer(), nelems);
   3076    case Scalar::Int32:
   3077    case Scalar::Uint32:
   3078    case Scalar::Float32:
   3079      return in.readArray((uint32_t*)buffer.dataPointer(), nelems);
   3080    case Scalar::Float64:
   3081    case Scalar::BigInt64:
   3082    case Scalar::BigUint64:
   3083      return in.readArray((uint64_t*)buffer.dataPointer(), nelems);
   3084    default:
   3085      MOZ_CRASH("Can't happen: arrayType range checked by caller");
   3086  }
   3087 }
   3088 
   3089 static bool PrimitiveToObject(JSContext* cx, MutableHandleValue vp) {
   3090  JSObject* obj = js::PrimitiveToObject(cx, vp);
   3091  if (!obj) {
   3092    return false;
   3093  }
   3094 
   3095  vp.setObject(*obj);
   3096  return true;
   3097 }
   3098 
   3099 bool JSStructuredCloneReader::startRead(MutableHandleValue vp,
   3100                                        ShouldAtomizeStrings atomizeStrings) {
   3101  uint32_t tag, data;
   3102  bool alreadAppended = false;
   3103 
   3104  AutoCheckRecursionLimit recursion(in.context());
   3105  if (!recursion.check(in.context())) {
   3106    return false;
   3107  }
   3108 
   3109  if (!in.readPair(&tag, &data)) {
   3110    return false;
   3111  }
   3112 
   3113  numItemsRead++;
   3114 
   3115  switch (tag) {
   3116    case SCTAG_NULL:
   3117      vp.setNull();
   3118      break;
   3119 
   3120    case SCTAG_UNDEFINED:
   3121      vp.setUndefined();
   3122      break;
   3123 
   3124    case SCTAG_INT32:
   3125      vp.setInt32(data);
   3126      break;
   3127 
   3128    case SCTAG_BOOLEAN:
   3129    case SCTAG_BOOLEAN_OBJECT:
   3130      vp.setBoolean(!!data);
   3131      if (tag == SCTAG_BOOLEAN_OBJECT && !PrimitiveToObject(context(), vp)) {
   3132        return false;
   3133      }
   3134      break;
   3135 
   3136    case SCTAG_STRING:
   3137    case SCTAG_STRING_OBJECT: {
   3138      JSString* str = readString(data, atomizeStrings);
   3139      if (!str) {
   3140        return false;
   3141      }
   3142      vp.setString(str);
   3143      if (tag == SCTAG_STRING_OBJECT && !PrimitiveToObject(context(), vp)) {
   3144        return false;
   3145      }
   3146      break;
   3147    }
   3148 
   3149    case SCTAG_NUMBER_OBJECT: {
   3150      double d;
   3151      if (!in.readDouble(&d)) {
   3152        return false;
   3153      }
   3154      vp.setDouble(CanonicalizeNaN(d));
   3155      if (!PrimitiveToObject(context(), vp)) {
   3156        return false;
   3157      }
   3158      break;
   3159    }
   3160 
   3161    case SCTAG_BIGINT:
   3162    case SCTAG_BIGINT_OBJECT: {
   3163      RootedBigInt bi(context(), readBigInt(data));
   3164      if (!bi) {
   3165        return false;
   3166      }
   3167      vp.setBigInt(bi);
   3168      if (tag == SCTAG_BIGINT_OBJECT && !PrimitiveToObject(context(), vp)) {
   3169        return false;
   3170      }
   3171      break;
   3172    }
   3173 
   3174    case SCTAG_DATE_OBJECT: {
   3175      double d;
   3176      if (!in.readDouble(&d)) {
   3177        return false;
   3178      }
   3179      JS::ClippedTime t = JS::TimeClip(d);
   3180      if (!NumbersAreIdentical(d, t.toDouble())) {
   3181        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3182                                  JSMSG_SC_BAD_SERIALIZED_DATA, "date");
   3183        return false;
   3184      }
   3185      JSObject* obj = NewDateObjectMsec(context(), t);
   3186      if (!obj) {
   3187        return false;
   3188      }
   3189      vp.setObject(*obj);
   3190      break;
   3191    }
   3192 
   3193    case SCTAG_REGEXP_OBJECT: {
   3194      if ((data & RegExpFlag::AllFlags) != data) {
   3195        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3196                                  JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
   3197        return false;
   3198      }
   3199 
   3200      RegExpFlags flags(AssertedCast<uint8_t>(data));
   3201 
   3202      uint32_t tag2, stringData;
   3203      if (!in.readPair(&tag2, &stringData)) {
   3204        return false;
   3205      }
   3206      if (tag2 != SCTAG_STRING) {
   3207        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3208                                  JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
   3209        return false;
   3210      }
   3211 
   3212      JSString* str = readString(stringData, AtomizeStrings);
   3213      if (!str) {
   3214        return false;
   3215      }
   3216 
   3217      Rooted<JSAtom*> atom(context(), &str->asAtom());
   3218 
   3219      NewObjectKind kind =
   3220          gcHeap == gc::Heap::Tenured ? TenuredObject : GenericObject;
   3221      RegExpObject* reobj = RegExpObject::create(context(), atom, flags, kind);
   3222      if (!reobj) {
   3223        return false;
   3224      }
   3225      vp.setObject(*reobj);
   3226      break;
   3227    }
   3228 
   3229    case SCTAG_ARRAY_OBJECT:
   3230    case SCTAG_OBJECT_OBJECT: {
   3231      NewObjectKind kind =
   3232          gcHeap == gc::Heap::Tenured ? TenuredObject : GenericObject;
   3233      JSObject* obj;
   3234      if (tag == SCTAG_ARRAY_OBJECT) {
   3235        obj = NewDenseUnallocatedArray(
   3236            context(), NativeEndian::swapFromLittleEndian(data), kind);
   3237      } else {
   3238        obj = NewPlainObject(context(), kind);
   3239      }
   3240      if (!obj || !objs.append(ObjectValue(*obj))) {
   3241        return false;
   3242      }
   3243 
   3244      vp.setObject(*obj);
   3245      break;
   3246    }
   3247 
   3248    case SCTAG_BACK_REFERENCE_OBJECT: {
   3249      if (data >= allObjs.length() || !allObjs[data].isObject()) {
   3250        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3251                                  JSMSG_SC_BAD_SERIALIZED_DATA,
   3252                                  "invalid back reference in input");
   3253        return false;
   3254      }
   3255      vp.set(allObjs[data]);
   3256      return true;
   3257    }
   3258 
   3259    case SCTAG_TRANSFER_MAP_HEADER:
   3260    case SCTAG_TRANSFER_MAP_PENDING_ENTRY:
   3261      // We should be past all the transfer map tags.
   3262      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3263                                JSMSG_SC_BAD_SERIALIZED_DATA, "invalid input");
   3264      return false;
   3265 
   3266    case SCTAG_ARRAY_BUFFER_OBJECT_V2:
   3267    case SCTAG_ARRAY_BUFFER_OBJECT:
   3268    case SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT:
   3269    case SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT:
   3270      if (!readArrayBuffer(StructuredDataType(tag), data, vp)) {
   3271        return false;
   3272      }
   3273      break;
   3274 
   3275    case SCTAG_SHARED_ARRAY_BUFFER_OBJECT:
   3276    case SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT:
   3277      if (!readSharedArrayBuffer(StructuredDataType(tag), vp)) {
   3278        return false;
   3279      }
   3280      break;
   3281 
   3282    case SCTAG_SHARED_WASM_MEMORY_OBJECT:
   3283      if (!readSharedWasmMemory(data, vp)) {
   3284        return false;
   3285      }
   3286      break;
   3287 
   3288    case SCTAG_TYPED_ARRAY_OBJECT_V2: {
   3289      // readTypedArray adds the array to allObjs.
   3290      // V2 stores the length (nelems) in |data| and the arrayType separately.
   3291      uint64_t arrayType;
   3292      if (!in.read(&arrayType)) {
   3293        return false;
   3294      }
   3295      uint64_t nelems = data;
   3296      return readTypedArray(arrayType, nelems, vp);
   3297    }
   3298 
   3299    case SCTAG_TYPED_ARRAY_OBJECT: {
   3300      // readTypedArray adds the array to allObjs.
   3301      // The current version stores the array type in |data| and the length
   3302      // (nelems) separately to support large TypedArrays.
   3303      uint32_t arrayType = data;
   3304      uint64_t nelems;
   3305      if (!in.read(&nelems)) {
   3306        return false;
   3307      }
   3308      return readTypedArray(arrayType, nelems, vp);
   3309    }
   3310 
   3311    case SCTAG_DATA_VIEW_OBJECT_V2: {
   3312      // readDataView adds the array to allObjs.
   3313      uint64_t byteLength = data;
   3314      return readDataView(byteLength, vp);
   3315    }
   3316 
   3317    case SCTAG_DATA_VIEW_OBJECT: {
   3318      // readDataView adds the array to allObjs.
   3319      uint64_t byteLength;
   3320      if (!in.read(&byteLength)) {
   3321        return false;
   3322      }
   3323      return readDataView(byteLength, vp);
   3324    }
   3325 
   3326    case SCTAG_MAP_OBJECT: {
   3327      JSObject* obj = MapObject::create(context());
   3328      if (!obj || !objs.append(ObjectValue(*obj))) {
   3329        return false;
   3330      }
   3331      vp.setObject(*obj);
   3332      break;
   3333    }
   3334 
   3335    case SCTAG_SET_OBJECT: {
   3336      JSObject* obj = SetObject::create(context());
   3337      if (!obj || !objs.append(ObjectValue(*obj))) {
   3338        return false;
   3339      }
   3340      vp.setObject(*obj);
   3341      break;
   3342    }
   3343 
   3344    case SCTAG_SAVED_FRAME_OBJECT: {
   3345      auto* obj = readSavedFrameHeader(data);
   3346      if (!obj || !objs.append(ObjectValue(*obj)) ||
   3347          !objState.append(std::make_pair(obj, false))) {
   3348        return false;
   3349      }
   3350      vp.setObject(*obj);
   3351      break;
   3352    }
   3353 
   3354    case SCTAG_ERROR_OBJECT: {
   3355      auto* obj = readErrorHeader(data);
   3356      if (!obj || !objs.append(ObjectValue(*obj)) ||
   3357          !objState.append(std::make_pair(obj, false))) {
   3358        return false;
   3359      }
   3360      vp.setObject(*obj);
   3361      break;
   3362    }
   3363 
   3364    case SCTAG_END_OF_KEYS:
   3365      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3366                                JSMSG_SC_BAD_SERIALIZED_DATA,
   3367                                "truncated input");
   3368      return false;
   3369      break;
   3370 
   3371    default: {
   3372      if (tag <= SCTAG_FLOAT_MAX) {
   3373        double d = ReinterpretPairAsDouble(tag, data);
   3374        vp.setNumber(CanonicalizeNaN(d));
   3375        break;
   3376      }
   3377 
   3378      if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
   3379        // A v1-format typed array
   3380        // readTypedArray adds the array to allObjs
   3381        return readTypedArray(TagToV1ArrayType(tag), data, vp, true);
   3382      }
   3383 
   3384      if (!callbacks || !callbacks->read) {
   3385        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3386                                  JSMSG_SC_BAD_SERIALIZED_DATA,
   3387                                  "unsupported type");
   3388        return false;
   3389      }
   3390 
   3391      // callbacks->read() might read other objects from the buffer.
   3392      // In startWrite we always write the object itself before calling
   3393      // the custom function. We should do the same here to keep
   3394      // indexing consistent.
   3395      uint32_t placeholderIndex = allObjs.length();
   3396      Value dummy = UndefinedValue();
   3397      if (!allObjs.append(dummy)) {
   3398        return false;
   3399      }
   3400      JSObject* obj =
   3401          callbacks->read(context(), this, cloneDataPolicy, tag, data, closure);
   3402      if (!obj) {
   3403        return false;
   3404      }
   3405      vp.setObject(*obj);
   3406      allObjs[placeholderIndex].set(vp);
   3407      alreadAppended = true;
   3408    }
   3409  }
   3410 
   3411  if (!alreadAppended && vp.isObject() && !allObjs.append(vp)) {
   3412    return false;
   3413  }
   3414 
   3415  return true;
   3416 }
   3417 
   3418 bool JSStructuredCloneReader::readHeader() {
   3419  uint32_t tag, data;
   3420  if (!in.getPair(&tag, &data)) {
   3421    return in.reportTruncated();
   3422  }
   3423 
   3424  JS::StructuredCloneScope storedScope;
   3425  if (tag == SCTAG_HEADER) {
   3426    MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
   3427    storedScope = JS::StructuredCloneScope(data);
   3428  } else {
   3429    // Old structured clone buffer. We must have read it from disk.
   3430    storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB;
   3431  }
   3432 
   3433  // Backward compatibility with old structured clone buffers. Value '0' was
   3434  // used for SameProcessSameThread scope.
   3435  if ((int)storedScope == 0) {
   3436    storedScope = JS::StructuredCloneScope::SameProcess;
   3437  }
   3438 
   3439  if (storedScope < JS::StructuredCloneScope::SameProcess ||
   3440      storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB) {
   3441    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3442                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3443                              "invalid structured clone scope");
   3444    return false;
   3445  }
   3446 
   3447  if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) {
   3448    // Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB
   3449    // clones are incorrect. Treat them as if they were DifferentProcess.
   3450    allowedScope = JS::StructuredCloneScope::DifferentProcess;
   3451    return true;
   3452  }
   3453 
   3454  if (storedScope < allowedScope) {
   3455    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3456                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3457                              "incompatible structured clone scope");
   3458    return false;
   3459  }
   3460 
   3461  return true;
   3462 }
   3463 
   3464 bool JSStructuredCloneReader::readTransferMap() {
   3465  JSContext* cx = context();
   3466  auto headerPos = in.tell();
   3467 
   3468  uint32_t tag, data;
   3469  if (!in.getPair(&tag, &data)) {
   3470    return in.reportTruncated();
   3471  }
   3472 
   3473  if (tag != SCTAG_TRANSFER_MAP_HEADER) {
   3474    // No transfer map header found.
   3475    return true;
   3476  }
   3477 
   3478  if (data >= SCTAG_TM_END) {
   3479    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3480                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3481                              "invalid transfer map header");
   3482    return false;
   3483  }
   3484  auto transferState = static_cast<TransferableMapHeader>(data);
   3485 
   3486  if (transferState == SCTAG_TM_TRANSFERRED) {
   3487    return true;
   3488  }
   3489 
   3490  if (transferState == SCTAG_TM_TRANSFERRING) {
   3491    ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE_TWICE, closure);
   3492    return false;
   3493  }
   3494 
   3495  headerPos.write(
   3496      PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRING));
   3497 
   3498  uint64_t numTransferables;
   3499  MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
   3500  if (!in.read(&numTransferables)) {
   3501    return false;
   3502  }
   3503 
   3504  for (uint64_t i = 0; i < numTransferables; i++) {
   3505    auto pos = in.tell();
   3506 
   3507    if (!in.readPair(&tag, &data)) {
   3508      return false;
   3509    }
   3510 
   3511    if (tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY) {
   3512      ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
   3513      return false;
   3514    }
   3515 
   3516    RootedObject obj(cx);
   3517 
   3518    void* content;
   3519    if (!in.readPtr(&content)) {
   3520      return false;
   3521    }
   3522 
   3523    uint64_t extraData;
   3524    if (!in.read(&extraData)) {
   3525      return false;
   3526    }
   3527 
   3528    if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) {
   3529      MOZ_ASSERT(allowedScope <= JS::StructuredCloneScope::LastResolvedScope);
   3530      if (allowedScope == JS::StructuredCloneScope::DifferentProcess) {
   3531        // Transferred ArrayBuffers in a DifferentProcess clone buffer
   3532        // are treated as if they weren't Transferred at all. We should
   3533        // only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER.
   3534        ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
   3535        return false;
   3536      }
   3537 
   3538      MOZ_RELEASE_ASSERT(extraData <= ArrayBufferObject::ByteLengthLimit);
   3539      size_t nbytes = extraData;
   3540 
   3541      MOZ_ASSERT(data == JS::SCTAG_TMO_ALLOC_DATA ||
   3542                 data == JS::SCTAG_TMO_MAPPED_DATA);
   3543      if (data == JS::SCTAG_TMO_ALLOC_DATA) {
   3544        // When the ArrayBuffer can't be allocated, |content| will be free'ed
   3545        // in `JSStructuredCloneData::discardTransferables()`.
   3546        obj = JS::NewArrayBufferWithContents(
   3547            cx, nbytes, content,
   3548            JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
   3549      } else if (data == JS::SCTAG_TMO_MAPPED_DATA) {
   3550        obj = JS::NewMappedArrayBufferWithContents(cx, nbytes, content);
   3551      }
   3552    } else if (tag == SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER) {
   3553      auto savedPos = in.tell();
   3554      auto guard = mozilla::MakeScopeExit([&] { in.seekTo(savedPos); });
   3555      in.seekTo(pos);
   3556      if (!in.seekBy(static_cast<size_t>(extraData))) {
   3557        return false;
   3558      }
   3559 
   3560      if (tailStartPos.isNothing()) {
   3561        tailStartPos = mozilla::Some(in.tell());
   3562      }
   3563 
   3564      uint32_t tag, data;
   3565      if (!in.readPair(&tag, &data)) {
   3566        return false;
   3567      }
   3568      if (tag != SCTAG_ARRAY_BUFFER_OBJECT_V2 &&
   3569          tag != SCTAG_ARRAY_BUFFER_OBJECT &&
   3570          tag != SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT &&
   3571          tag != SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
   3572        ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
   3573        return false;
   3574      }
   3575      RootedValue val(cx);
   3576      if (!readArrayBuffer(StructuredDataType(tag), data, &val)) {
   3577        return false;
   3578      }
   3579      obj = &val.toObject();
   3580      tailEndPos = mozilla::Some(in.tell());
   3581    } else {
   3582      if (!callbacks || !callbacks->readTransfer) {
   3583        ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
   3584        return false;
   3585      }
   3586      if (!callbacks->readTransfer(cx, this, cloneDataPolicy, tag, content,
   3587                                   extraData, closure, &obj)) {
   3588        if (!cx->isExceptionPending()) {
   3589          ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
   3590        }
   3591        return false;
   3592      }
   3593      MOZ_ASSERT(obj);
   3594      MOZ_ASSERT(!cx->isExceptionPending());
   3595    }
   3596 
   3597    // On failure, the buffer will still own the data (since its ownership
   3598    // will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
   3599    // DiscardTransferables.
   3600    if (!obj) {
   3601      return false;
   3602    }
   3603 
   3604    // Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
   3605    // buffer.
   3606    pos.write(PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED));
   3607    MOZ_ASSERT(!pos.done());
   3608 
   3609    if (!allObjs.append(ObjectValue(*obj))) {
   3610      return false;
   3611    }
   3612  }
   3613 
   3614  // Mark the whole transfer map as consumed.
   3615 #ifdef DEBUG
   3616  SCInput::getPair(headerPos.peek(), &tag, &data);
   3617  MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
   3618  MOZ_ASSERT(TransferableMapHeader(data) == SCTAG_TM_TRANSFERRING);
   3619 #endif
   3620  headerPos.write(
   3621      PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
   3622 
   3623  return true;
   3624 }
   3625 
   3626 JSObject* JSStructuredCloneReader::readSavedFrameHeader(
   3627    uint32_t principalsTag) {
   3628  Rooted<SavedFrame*> savedFrame(context(), SavedFrame::create(context()));
   3629  if (!savedFrame) {
   3630    return nullptr;
   3631  }
   3632 
   3633  JSPrincipals* principals;
   3634  if (principalsTag == SCTAG_JSPRINCIPALS) {
   3635    if (!context()->runtime()->readPrincipals) {
   3636      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3637                                JSMSG_SC_UNSUPPORTED_TYPE);
   3638      return nullptr;
   3639    }
   3640 
   3641    if (!context()->runtime()->readPrincipals(context(), this, &principals)) {
   3642      return nullptr;
   3643    }
   3644  } else if (principalsTag ==
   3645             SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) {
   3646    principals = &ReconstructedSavedFramePrincipals::IsSystem;
   3647    principals->refcount++;
   3648  } else if (principalsTag ==
   3649             SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) {
   3650    principals = &ReconstructedSavedFramePrincipals::IsNotSystem;
   3651    principals->refcount++;
   3652  } else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) {
   3653    principals = nullptr;
   3654  } else {
   3655    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3656                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3657                              "bad SavedFrame principals");
   3658    return nullptr;
   3659  }
   3660 
   3661  RootedValue mutedErrors(context());
   3662  RootedValue source(context());
   3663  {
   3664    // Read a |mutedErrors| boolean followed by a |source| string.
   3665    // The |mutedErrors| boolean is present in all new structured-clone data,
   3666    // but in older data it will be absent and only the |source| string will be
   3667    // found.
   3668    if (!startRead(&mutedErrors, AtomizeStrings)) {
   3669      return nullptr;
   3670    }
   3671 
   3672    if (mutedErrors.isBoolean()) {
   3673      if (!startRead(&source, AtomizeStrings)) {
   3674        return nullptr;
   3675      }
   3676      if (!source.isString()) {
   3677        JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3678                                  JSMSG_SC_BAD_SERIALIZED_DATA,
   3679                                  "bad source string");
   3680        return nullptr;
   3681      }
   3682    } else if (mutedErrors.isString()) {
   3683      // Backwards compatibility: Handle missing |mutedErrors| boolean,
   3684      // this is actually just a |source| string.
   3685      source = mutedErrors;
   3686      mutedErrors.setBoolean(true);  // Safe default value.
   3687    } else {
   3688      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3689                                JSMSG_SC_BAD_SERIALIZED_DATA,
   3690                                "invalid mutedErrors");
   3691      return nullptr;
   3692    }
   3693  }
   3694 
   3695  savedFrame->initPrincipalsAlreadyHeldAndMutedErrors(principals,
   3696                                                      mutedErrors.toBoolean());
   3697 
   3698  savedFrame->initSource(&source.toString()->asAtom());
   3699 
   3700  uint32_t line;
   3701  if (!readUint32(&line)) {
   3702    return nullptr;
   3703  }
   3704  savedFrame->initLine(line);
   3705 
   3706  JS::TaggedColumnNumberOneOrigin column;
   3707  if (!readUint32(column.addressOfValueForTranscode())) {
   3708    return nullptr;
   3709  }
   3710  savedFrame->initColumn(column);
   3711 
   3712  // Don't specify a source ID when reading a cloned saved frame, as these IDs
   3713  // are only valid within a specific process.
   3714  savedFrame->initSourceId(0);
   3715 
   3716  RootedValue name(context());
   3717  if (!startRead(&name, AtomizeStrings)) {
   3718    return nullptr;
   3719  }
   3720  if (!(name.isString() || name.isNull())) {
   3721    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3722                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3723                              "invalid saved frame cause");
   3724    return nullptr;
   3725  }
   3726  JSAtom* atomName = nullptr;
   3727  if (name.isString()) {
   3728    atomName = &name.toString()->asAtom();
   3729  }
   3730 
   3731  savedFrame->initFunctionDisplayName(atomName);
   3732 
   3733  RootedValue cause(context());
   3734  if (!startRead(&cause, AtomizeStrings)) {
   3735    return nullptr;
   3736  }
   3737  if (!(cause.isString() || cause.isNull())) {
   3738    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3739                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3740                              "invalid saved frame cause");
   3741    return nullptr;
   3742  }
   3743  JSAtom* atomCause = nullptr;
   3744  if (cause.isString()) {
   3745    atomCause = &cause.toString()->asAtom();
   3746  }
   3747  savedFrame->initAsyncCause(atomCause);
   3748 
   3749  return savedFrame;
   3750 }
   3751 
   3752 // SavedFrame object: there is one child value, the parent SavedFrame,
   3753 // which is either null or another SavedFrame object.
   3754 bool JSStructuredCloneReader::readSavedFrameFields(Handle<SavedFrame*> frameObj,
   3755                                                   HandleValue parent,
   3756                                                   bool* state) {
   3757  if (*state) {
   3758    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3759                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3760                              "multiple SavedFrame parents");
   3761    return false;
   3762  }
   3763 
   3764  SavedFrame* parentFrame;
   3765  if (parent.isNull()) {
   3766    parentFrame = nullptr;
   3767  } else if (parent.isObject() && parent.toObject().is<SavedFrame>()) {
   3768    parentFrame = &parent.toObject().as<SavedFrame>();
   3769  } else {
   3770    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3771                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3772                              "invalid SavedFrame parent");
   3773    return false;
   3774  }
   3775 
   3776  frameObj->initParent(parentFrame);
   3777  *state = true;
   3778  return true;
   3779 }
   3780 
   3781 JSObject* JSStructuredCloneReader::readErrorHeader(uint32_t type) {
   3782  JSContext* cx = context();
   3783 
   3784  switch (type) {
   3785    case JSEXN_ERR:
   3786    case JSEXN_EVALERR:
   3787    case JSEXN_RANGEERR:
   3788    case JSEXN_REFERENCEERR:
   3789    case JSEXN_SYNTAXERR:
   3790    case JSEXN_TYPEERR:
   3791    case JSEXN_URIERR:
   3792    case JSEXN_AGGREGATEERR:
   3793      break;
   3794    default:
   3795      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3796                                JSMSG_SC_BAD_SERIALIZED_DATA,
   3797                                "invalid error type");
   3798      return nullptr;
   3799  }
   3800 
   3801  RootedString message(cx);
   3802  {
   3803    RootedValue messageVal(cx);
   3804    if (!startRead(&messageVal)) {
   3805      return nullptr;
   3806    }
   3807    if (messageVal.isString()) {
   3808      message = messageVal.toString();
   3809    } else if (!messageVal.isNull()) {
   3810      JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3811                                JSMSG_SC_BAD_SERIALIZED_DATA,
   3812                                "invalid 'message' field for Error object");
   3813      return nullptr;
   3814    }
   3815  }
   3816 
   3817  // We have to set |cause| to something if it exists, otherwise the shape
   3818  // would be wrong. The actual value will be overwritten later.
   3819  RootedValue val(cx);
   3820  if (!startRead(&val)) {
   3821    return nullptr;
   3822  }
   3823  bool hasCause = ToBoolean(val);
   3824  Rooted<Maybe<Value>> cause(cx, mozilla::Nothing());
   3825  if (hasCause) {
   3826    cause = mozilla::Some(BooleanValue(true));
   3827  }
   3828 
   3829  if (!startRead(&val)) {
   3830    return nullptr;
   3831  }
   3832  if (!val.isString()) {
   3833    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3834                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3835                              "invalid 'fileName' field for Error object");
   3836    return nullptr;
   3837  }
   3838  RootedString fileName(cx, val.toString());
   3839 
   3840  uint32_t lineNumber;
   3841  JS::ColumnNumberOneOrigin columnNumber;
   3842  if (!readUint32(&lineNumber) ||
   3843      !readUint32(columnNumber.addressOfValueForTranscode())) {
   3844    return nullptr;
   3845  }
   3846 
   3847  // The |cause| and |stack| slots of the objects might be overwritten later.
   3848  // For AggregateErrors the |errors| property will be added.
   3849  RootedObject errorObj(
   3850      cx, ErrorObject::create(cx, static_cast<JSExnType>(type), nullptr,
   3851                              fileName, 0, lineNumber, columnNumber, nullptr,
   3852                              message, cause));
   3853  if (!errorObj) {
   3854    return nullptr;
   3855  }
   3856 
   3857  return errorObj;
   3858 }
   3859 
   3860 // Error objects have 3 fields, some or all of them null: cause,
   3861 // errors, and stack.
   3862 bool JSStructuredCloneReader::readErrorFields(Handle<ErrorObject*> errorObj,
   3863                                              HandleValue cause, bool* state) {
   3864  JSContext* cx = context();
   3865  if (*state) {
   3866    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3867                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3868                              "unexpected child value seen for Error object");
   3869    return false;
   3870  }
   3871 
   3872  RootedValue errors(cx);
   3873  RootedValue stack(cx);
   3874  if (!startRead(&errors) || !startRead(&stack)) {
   3875    return false;
   3876  }
   3877 
   3878  bool hasCause = errorObj->getCause().isSome();
   3879  if (hasCause) {
   3880    errorObj->setCauseSlot(cause);
   3881  } else if (!cause.isNull()) {
   3882    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3883                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3884                              "invalid 'cause' field for Error object");
   3885    return false;
   3886  }
   3887 
   3888  if (errorObj->type() == JSEXN_AGGREGATEERR) {
   3889    if (!DefineDataProperty(context(), errorObj, cx->names().errors, errors,
   3890                            0)) {
   3891      return false;
   3892    }
   3893  } else if (!errors.isNull()) {
   3894    JS_ReportErrorNumberASCII(
   3895        cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
   3896        "unexpected 'errors' field seen for non-AggregateError");
   3897    return false;
   3898  }
   3899 
   3900  if (stack.isObject()) {
   3901    RootedObject stackObj(cx, &stack.toObject());
   3902    if (!stackObj->is<SavedFrame>()) {
   3903      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3904                                JSMSG_SC_BAD_SERIALIZED_DATA,
   3905                                "invalid 'stack' field for Error object");
   3906      return false;
   3907    }
   3908    errorObj->setStackSlot(stack);
   3909  } else if (!stack.isNull()) {
   3910    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3911                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3912                              "invalid 'stack' field for Error object");
   3913    return false;
   3914  }
   3915 
   3916  *state = true;
   3917  return true;
   3918 }
   3919 
   3920 // Read a value and treat as a key,value pair.
   3921 bool JSStructuredCloneReader::readMapField(Handle<MapObject*> mapObj,
   3922                                           HandleValue key) {
   3923  RootedValue val(context());
   3924  if (!startRead(&val)) {
   3925    return false;
   3926  }
   3927  return mapObj->set(context(), key, val);
   3928 }
   3929 
   3930 // Read a value and treat as a key,value pair. Interpret as a plain property
   3931 // value.
   3932 bool JSStructuredCloneReader::readObjectField(HandleObject obj,
   3933                                              HandleValue key) {
   3934  if (!key.isString() && !key.isInt32()) {
   3935    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   3936                              JSMSG_SC_BAD_SERIALIZED_DATA,
   3937                              "property key expected");
   3938    return false;
   3939  }
   3940 
   3941  RootedValue val(context());
   3942  if (!startRead(&val)) {
   3943    return false;
   3944  }
   3945 
   3946  RootedId id(context());
   3947  if (!PrimitiveValueToId<CanGC>(context(), key, &id)) {
   3948    return false;
   3949  }
   3950 
   3951  // Fast path for adding a new property to a plain object. The property names
   3952  // we see here should be unique, but we check for duplicates to guard against
   3953  // corrupt or malicious data.
   3954  if (id.isString() && obj->is<PlainObject>() &&
   3955      MOZ_LIKELY(!obj->as<PlainObject>().contains(context(), id))) {
   3956    return AddDataPropertyToPlainObject(context(), obj.as<PlainObject>(), id,
   3957                                        val);
   3958  }
   3959 
   3960  // Fast path for adding an array element. The index shouldn't exceed the
   3961  // array's length, but we check for this in `addDenseElementNoLengthChange` to
   3962  // guard against corrupt or malicious data.
   3963  if (id.isInt() && obj->is<ArrayObject>()) {
   3964    ArrayObject* arr = &obj->as<ArrayObject>();
   3965    switch (arr->addDenseElementNoLengthChange(context(), id.toInt(), val)) {
   3966      case DenseElementResult::Failure:
   3967        return false;
   3968      case DenseElementResult::Success:
   3969        return true;
   3970      case DenseElementResult::Incomplete:
   3971        // Fall-through to slow path.
   3972        break;
   3973    }
   3974  }
   3975 
   3976  return DefineDataProperty(context(), obj, id, val);
   3977 }
   3978 
   3979 // Perform the whole recursive reading procedure.
   3980 bool JSStructuredCloneReader::read(MutableHandleValue vp, size_t nbytes) {
   3981  if (!readHeader()) {
   3982    return false;
   3983  }
   3984  MOZ_ASSERT(allowedScope <= JS::StructuredCloneScope::LastResolvedScope,
   3985             "allowedScope should have been resolved by now");
   3986 
   3987  if (!readTransferMap()) {
   3988    return false;
   3989  }
   3990 
   3991  MOZ_ASSERT(objs.length() == 0);
   3992  MOZ_ASSERT(objState.length() == 1);
   3993 
   3994  // Start out by reading in the main object and pushing it onto the 'objs'
   3995  // stack. The data related to this object and its descendants extends from
   3996  // here to the SCTAG_END_OF_KEYS at the end of the stream.
   3997  if (!startRead(vp)) {
   3998    return false;
   3999  }
   4000 
   4001  // Stop when the stack shows that all objects have been read.
   4002  while (objs.length() != 0) {
   4003    // What happens depends on the top obj on the objs stack.
   4004    RootedObject obj(context(), &objs.back().toObject());
   4005 
   4006    uint32_t tag, data;
   4007    if (!in.getPair(&tag, &data)) {
   4008      return false;
   4009    }
   4010 
   4011    if (tag == SCTAG_END_OF_KEYS) {
   4012      // Pop the current obj off the stack, since we are done with it and
   4013      // its children.
   4014      MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
   4015      objs.popBack();
   4016      if (objState.back().first == obj) {
   4017        objState.popBack();
   4018      }
   4019      continue;
   4020    }
   4021 
   4022    // Remember the index of the current top of the state stack, which will
   4023    // correspond to the state for `obj` iff `obj` is a type that uses state.
   4024    // startRead() may push additional entries before the state is accessed and
   4025    // updated while filling in the object's data.
   4026    size_t objStateIdx = objState.length() - 1;
   4027 
   4028    // The input stream contains a sequence of "child" values, whose
   4029    // interpretation depends on the type of obj. These values can be
   4030    // anything, and startRead() will push onto 'objs' for any non-leaf
   4031    // value (i.e., anything that may contain children).
   4032    //
   4033    // startRead() will allocate the (empty) object, but note that when
   4034    // startRead() returns, 'key' is not yet initialized with any of its
   4035    // properties. Those will be filled in by returning to the head of this
   4036    // loop, processing the first child obj, and continuing until all
   4037    // children have been fully created.
   4038    //
   4039    // Note that this means the ordering in the stream is a little funky for
   4040    // things like Map. See the comment above traverseMap() for an example.
   4041 
   4042    bool expectKeyValuePairs =
   4043        !(obj->is<MapObject>() || obj->is<SetObject>() ||
   4044          obj->is<SavedFrame>() || obj->is<ErrorObject>());
   4045 
   4046    RootedValue key(context());
   4047    ShouldAtomizeStrings atomize =
   4048        expectKeyValuePairs ? AtomizeStrings : DontAtomizeStrings;
   4049    if (!startRead(&key, atomize)) {
   4050      return false;
   4051    }
   4052 
   4053    if (key.isNull() && expectKeyValuePairs) {
   4054      // Backwards compatibility: Null formerly indicated the end of
   4055      // object properties.
   4056 
   4057      // No legacy objects used the state stack.
   4058      MOZ_ASSERT(objState[objStateIdx].first() != obj);
   4059 
   4060      objs.popBack();
   4061      continue;
   4062    }
   4063 
   4064    context()->check(key);
   4065 
   4066    if (obj->is<SetObject>()) {
   4067      // Set object: the values between obj header (from startRead()) and
   4068      // SCTAG_END_OF_KEYS are all interpreted as values to add to the set.
   4069      if (!obj->as<SetObject>().add(context(), key)) {
   4070        return false;
   4071      }
   4072    } else if (obj->is<MapObject>()) {
   4073      Rooted<MapObject*> mapObj(context(), &obj->as<MapObject>());
   4074      if (!readMapField(mapObj, key)) {
   4075        return false;
   4076      }
   4077    } else if (obj->is<SavedFrame>()) {
   4078      Rooted<SavedFrame*> frameObj(context(), &obj->as<SavedFrame>());
   4079      MOZ_ASSERT(objState[objStateIdx].first() == obj);
   4080      bool state = objState[objStateIdx].second();
   4081      if (!readSavedFrameFields(frameObj, key, &state)) {
   4082        return false;
   4083      }
   4084      objState[objStateIdx].second() = state;
   4085    } else if (obj->is<ErrorObject>()) {
   4086      Rooted<ErrorObject*> errorObj(context(), &obj->as<ErrorObject>());
   4087      MOZ_ASSERT(objState[objStateIdx].first() == obj);
   4088      bool state = objState[objStateIdx].second();
   4089      if (!readErrorFields(errorObj, key, &state)) {
   4090        return false;
   4091      }
   4092      objState[objStateIdx].second() = state;
   4093    } else {
   4094      MOZ_ASSERT(expectKeyValuePairs);
   4095      // Everything else uses a series of key,value,key,value,... Value
   4096      // objects.
   4097      if (!readObjectField(obj, key)) {
   4098        return false;
   4099      }
   4100    }
   4101  }
   4102 
   4103  allObjs.clear();
   4104 
   4105  // For fuzzing, it is convenient to allow extra data at the end
   4106  // of the input buffer so that more possible inputs are considered
   4107  // valid.
   4108 #ifndef FUZZING
   4109  bool extraData;
   4110  if (tailStartPos.isSome()) {
   4111    // in.tell() is the end of the main data. If "tail" data was consumed,
   4112    // then check whether there's any data between the main data and the
   4113    // beginning of the tail, or after the last read point in the tail.
   4114    extraData = (in.tell() != *tailStartPos || !tailEndPos->done());
   4115  } else {
   4116    extraData = !in.tell().done();
   4117  }
   4118  if (extraData) {
   4119    JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
   4120                              JSMSG_SC_BAD_SERIALIZED_DATA,
   4121                              "extra data after end");
   4122    return false;
   4123  }
   4124 #endif
   4125  return true;
   4126 }
   4127 
   4128 JS_PUBLIC_API bool JS_ReadStructuredClone(
   4129    JSContext* cx, const JSStructuredCloneData& buf, uint32_t version,
   4130    JS::StructuredCloneScope scope, MutableHandleValue vp,
   4131    const JS::CloneDataPolicy& cloneDataPolicy,
   4132    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   4133  AssertHeapIsIdle();
   4134  CHECK_THREAD(cx);
   4135 
   4136  if (version > JS_STRUCTURED_CLONE_VERSION) {
   4137    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4138                              JSMSG_SC_BAD_CLONE_VERSION);
   4139    return false;
   4140  }
   4141  const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
   4142  return ReadStructuredClone(cx, buf, scope, vp, cloneDataPolicy, callbacks,
   4143                             closure);
   4144 }
   4145 
   4146 JS_PUBLIC_API bool JS_WriteStructuredClone(
   4147    JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
   4148    JS::StructuredCloneScope scope, const JS::CloneDataPolicy& cloneDataPolicy,
   4149    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure,
   4150    HandleValue transferable) {
   4151  AssertHeapIsIdle();
   4152  CHECK_THREAD(cx);
   4153  cx->check(value);
   4154 
   4155  const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
   4156  return WriteStructuredClone(cx, value, bufp, scope, cloneDataPolicy,
   4157                              callbacks, closure, transferable);
   4158 }
   4159 
   4160 JS_PUBLIC_API bool JS_StructuredCloneHasTransferables(
   4161    JSStructuredCloneData& data, bool* hasTransferable) {
   4162  *hasTransferable = StructuredCloneHasTransferObjects(data);
   4163  return true;
   4164 }
   4165 
   4166 JS_PUBLIC_API bool JS_StructuredClone(
   4167    JSContext* cx, HandleValue value, MutableHandleValue vp,
   4168    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   4169  AssertHeapIsIdle();
   4170  CHECK_THREAD(cx);
   4171 
   4172  // Strings are associated with zones, not compartments,
   4173  // so we copy the string by wrapping it.
   4174  if (value.isString()) {
   4175    RootedString strValue(cx, value.toString());
   4176    if (!cx->compartment()->wrap(cx, &strValue)) {
   4177      return false;
   4178    }
   4179    vp.setString(strValue);
   4180    return true;
   4181  }
   4182 
   4183  const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
   4184 
   4185  JSAutoStructuredCloneBuffer buf(JS::StructuredCloneScope::SameProcess,
   4186                                  callbacks, closure);
   4187  {
   4188    if (value.isObject()) {
   4189      RootedObject obj(cx, &value.toObject());
   4190      obj = CheckedUnwrapStatic(obj);
   4191      if (!obj) {
   4192        ReportAccessDenied(cx);
   4193        return false;
   4194      }
   4195      AutoRealm ar(cx, obj);
   4196      RootedValue unwrappedVal(cx, ObjectValue(*obj));
   4197      if (!buf.write(cx, unwrappedVal, callbacks, closure)) {
   4198        return false;
   4199      }
   4200    } else {
   4201      if (!buf.write(cx, value, callbacks, closure)) {
   4202        return false;
   4203      }
   4204    }
   4205  }
   4206 
   4207  return buf.read(cx, vp, JS::CloneDataPolicy(), callbacks, closure);
   4208 }
   4209 
   4210 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(
   4211    JSAutoStructuredCloneBuffer&& other)
   4212    : data_(other.scope()) {
   4213  version_ = other.version_;
   4214  other.giveTo(&data_);
   4215 }
   4216 
   4217 JSAutoStructuredCloneBuffer& JSAutoStructuredCloneBuffer::operator=(
   4218    JSAutoStructuredCloneBuffer&& other) {
   4219  MOZ_ASSERT(&other != this);
   4220  MOZ_ASSERT(scope() == other.scope());
   4221  clear();
   4222  version_ = other.version_;
   4223  other.giveTo(&data_);
   4224  return *this;
   4225 }
   4226 
   4227 void JSAutoStructuredCloneBuffer::clear() {
   4228  data_.discardTransferables();
   4229  data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
   4230  data_.refsHeld_.releaseAll();
   4231  data_.stringBufferRefsHeld_.clear();
   4232  data_.Clear();
   4233  version_ = 0;
   4234 }
   4235 
   4236 void JSAutoStructuredCloneBuffer::adopt(
   4237    JSStructuredCloneData&& data, uint32_t version,
   4238    const JSStructuredCloneCallbacks* callbacks, void* closure) {
   4239  clear();
   4240  data_ = std::move(data);
   4241  version_ = version;
   4242  data_.setCallbacks(callbacks, closure,
   4243                     OwnTransferablePolicy::OwnsTransferablesIfAny);
   4244 }
   4245 
   4246 void JSAutoStructuredCloneBuffer::giveTo(JSStructuredCloneData* data) {
   4247  *data = std::move(data_);
   4248  version_ = 0;
   4249  data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
   4250  data_.Clear();
   4251 }
   4252 
   4253 bool JSAutoStructuredCloneBuffer::read(
   4254    JSContext* cx, MutableHandleValue vp,
   4255    const JS::CloneDataPolicy& cloneDataPolicy,
   4256    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   4257  MOZ_ASSERT(cx);
   4258  return !!JS_ReadStructuredClone(
   4259      cx, data_, version_, data_.scope(), vp, cloneDataPolicy,
   4260      optionalCallbacks ? optionalCallbacks : data_.callbacks_,
   4261      optionalCallbacks ? closure : data_.closure_);
   4262 }
   4263 
   4264 bool JSAutoStructuredCloneBuffer::write(
   4265    JSContext* cx, HandleValue value,
   4266    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   4267  HandleValue transferable = UndefinedHandleValue;
   4268  return write(cx, value, transferable, JS::CloneDataPolicy(),
   4269               optionalCallbacks ? optionalCallbacks : data_.callbacks_,
   4270               optionalCallbacks ? closure : data_.closure_);
   4271 }
   4272 
   4273 bool JSAutoStructuredCloneBuffer::write(
   4274    JSContext* cx, HandleValue value, HandleValue transferable,
   4275    const JS::CloneDataPolicy& cloneDataPolicy,
   4276    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure) {
   4277  clear();
   4278  bool ok = JS_WriteStructuredClone(
   4279      cx, value, &data_, data_.scopeForInternalWriting(), cloneDataPolicy,
   4280      optionalCallbacks ? optionalCallbacks : data_.callbacks_,
   4281      optionalCallbacks ? closure : data_.closure_, transferable);
   4282  if (!ok) {
   4283    version_ = JS_STRUCTURED_CLONE_VERSION;
   4284  }
   4285  return ok;
   4286 }
   4287 
   4288 JS_PUBLIC_API bool JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1,
   4289                                     uint32_t* p2) {
   4290  return r->input().readPair((uint32_t*)p1, (uint32_t*)p2);
   4291 }
   4292 
   4293 JS_PUBLIC_API bool JS_ReadBytes(JSStructuredCloneReader* r, void* p,
   4294                                size_t len) {
   4295  return r->input().readBytes(p, len);
   4296 }
   4297 
   4298 JS_PUBLIC_API bool JS_ReadString(JSStructuredCloneReader* r,
   4299                                 MutableHandleString str) {
   4300  uint32_t tag, data;
   4301  if (!r->input().readPair(&tag, &data)) {
   4302    return false;
   4303  }
   4304 
   4305  if (tag == SCTAG_STRING) {
   4306    if (JSString* s =
   4307            r->readString(data, JSStructuredCloneReader::DontAtomizeStrings)) {
   4308      str.set(s);
   4309      return true;
   4310    }
   4311    return false;
   4312  }
   4313 
   4314  JS_ReportErrorNumberASCII(r->context(), GetErrorMessage, nullptr,
   4315                            JSMSG_SC_BAD_SERIALIZED_DATA, "expected string");
   4316  return false;
   4317 }
   4318 
   4319 JS_PUBLIC_API bool JS_ReadDouble(JSStructuredCloneReader* r, double* v) {
   4320  return r->input().readDouble(v);
   4321 }
   4322 
   4323 JS_PUBLIC_API bool JS_ReadTypedArray(JSStructuredCloneReader* r,
   4324                                     MutableHandleValue vp) {
   4325  uint32_t tag, data;
   4326  if (!r->input().readPair(&tag, &data)) {
   4327    return false;
   4328  }
   4329 
   4330  if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
   4331    return r->readTypedArray(TagToV1ArrayType(tag), data, vp, true);
   4332  }
   4333 
   4334  if (tag == SCTAG_TYPED_ARRAY_OBJECT_V2) {
   4335    // V2 stores the length (nelems) in |data| and the arrayType separately.
   4336    uint64_t arrayType;
   4337    if (!r->input().read(&arrayType)) {
   4338      return false;
   4339    }
   4340    uint64_t nelems = data;
   4341    return r->readTypedArray(arrayType, nelems, vp);
   4342  }
   4343 
   4344  if (tag == SCTAG_TYPED_ARRAY_OBJECT) {
   4345    // The current version stores the array type in |data| and the length
   4346    // (nelems) separately to support large TypedArrays.
   4347    uint32_t arrayType = data;
   4348    uint64_t nelems;
   4349    if (!r->input().read(&nelems)) {
   4350      return false;
   4351    }
   4352    return r->readTypedArray(arrayType, nelems, vp);
   4353  }
   4354 
   4355  JS_ReportErrorNumberASCII(r->context(), GetErrorMessage, nullptr,
   4356                            JSMSG_SC_BAD_SERIALIZED_DATA,
   4357                            "expected type array");
   4358  return false;
   4359 }
   4360 
   4361 JS_PUBLIC_API bool JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag,
   4362                                      uint32_t data) {
   4363  return w->output().writePair(tag, data);
   4364 }
   4365 
   4366 JS_PUBLIC_API bool JS_WriteBytes(JSStructuredCloneWriter* w, const void* p,
   4367                                 size_t len) {
   4368  return w->output().writeBytes(p, len);
   4369 }
   4370 
   4371 JS_PUBLIC_API bool JS_WriteString(JSStructuredCloneWriter* w,
   4372                                  HandleString str) {
   4373  return w->writeString(SCTAG_STRING, str);
   4374 }
   4375 
   4376 JS_PUBLIC_API bool JS_WriteDouble(JSStructuredCloneWriter* w, double v) {
   4377  return w->output().writeDouble(v);
   4378 }
   4379 
   4380 JS_PUBLIC_API bool JS_WriteTypedArray(JSStructuredCloneWriter* w,
   4381                                      HandleValue v) {
   4382  MOZ_ASSERT(v.isObject());
   4383  w->context()->check(v);
   4384  RootedObject obj(w->context(), &v.toObject());
   4385 
   4386  // startWrite can write everything, thus we should check here
   4387  // and report error if the user passes a wrong type.
   4388  if (!obj->canUnwrapAs<TypedArrayObject>()) {
   4389    ReportAccessDenied(w->context());
   4390    return false;
   4391  }
   4392 
   4393  // We should use startWrite instead of writeTypedArray, because
   4394  // typed array is an object, we should add it to the |memory|
   4395  // (allObjs) list. Directly calling writeTypedArray won't add it.
   4396  return w->startWrite(v);
   4397 }
   4398 
   4399 JS_PUBLIC_API bool JS_ObjectNotWritten(JSStructuredCloneWriter* w,
   4400                                       HandleObject obj) {
   4401  w->memory.remove(w->memory.lookup(obj));
   4402 
   4403  return true;
   4404 }
   4405 
   4406 JS_PUBLIC_API JS::StructuredCloneScope JS_GetStructuredCloneScope(
   4407    JSStructuredCloneWriter* w) {
   4408  return w->output().scope();
   4409 }