LCOV - code coverage report
Current view: top level - js/src/vm - StructuredClone.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 347 1199 28.9 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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/CheckedInt.h"
      32             : #include "mozilla/EndianUtils.h"
      33             : #include "mozilla/FloatingPoint.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <utility>
      37             : 
      38             : #include "jsapi.h"
      39             : #include "jsdate.h"
      40             : 
      41             : #include "builtin/DataViewObject.h"
      42             : #include "builtin/MapObject.h"
      43             : #include "js/Date.h"
      44             : #include "js/GCHashTable.h"
      45             : #include "js/Wrapper.h"
      46             : #include "vm/JSContext.h"
      47             : #include "vm/RegExpObject.h"
      48             : #include "vm/SavedFrame.h"
      49             : #include "vm/SharedArrayObject.h"
      50             : #include "vm/TypedArrayObject.h"
      51             : #include "vm/WrapperObject.h"
      52             : #include "wasm/WasmJS.h"
      53             : 
      54             : #include "vm/JSContext-inl.h"
      55             : #include "vm/JSObject-inl.h"
      56             : 
      57             : using namespace js;
      58             : 
      59             : using mozilla::BitwiseCast;
      60             : using mozilla::NativeEndian;
      61             : using mozilla::NumbersAreIdentical;
      62             : using JS::CanonicalizeNaN;
      63             : 
      64             : // When you make updates here, make sure you consider whether you need to bump the
      65             : // value of JS_STRUCTURED_CLONE_VERSION in js/public/StructuredClone.h.  You will
      66             : // likely need to increment the version if anything at all changes in the serialization
      67             : // format.
      68             : //
      69             : // Note that SCTAG_END_OF_KEYS is written into the serialized form and should have
      70             : // a stable ID, it need not be at the end of the list and should not be used for
      71             : // sizing data structures.
      72             : 
      73             : enum StructuredDataType : uint32_t {
      74             :     // Structured data types provided by the engine
      75             :     SCTAG_FLOAT_MAX = 0xFFF00000,
      76             :     SCTAG_HEADER = 0xFFF10000,
      77             :     SCTAG_NULL = 0xFFFF0000,
      78             :     SCTAG_UNDEFINED,
      79             :     SCTAG_BOOLEAN,
      80             :     SCTAG_INT32,
      81             :     SCTAG_STRING,
      82             :     SCTAG_DATE_OBJECT,
      83             :     SCTAG_REGEXP_OBJECT,
      84             :     SCTAG_ARRAY_OBJECT,
      85             :     SCTAG_OBJECT_OBJECT,
      86             :     SCTAG_ARRAY_BUFFER_OBJECT,
      87             :     SCTAG_BOOLEAN_OBJECT,
      88             :     SCTAG_STRING_OBJECT,
      89             :     SCTAG_NUMBER_OBJECT,
      90             :     SCTAG_BACK_REFERENCE_OBJECT,
      91             :     SCTAG_DO_NOT_USE_1, // Required for backwards compatibility
      92             :     SCTAG_DO_NOT_USE_2, // Required for backwards compatibility
      93             :     SCTAG_TYPED_ARRAY_OBJECT,
      94             :     SCTAG_MAP_OBJECT,
      95             :     SCTAG_SET_OBJECT,
      96             :     SCTAG_END_OF_KEYS,
      97             :     SCTAG_DO_NOT_USE_3, // Required for backwards compatibility
      98             :     SCTAG_DATA_VIEW_OBJECT,
      99             :     SCTAG_SAVED_FRAME_OBJECT,
     100             : 
     101             :     // No new tags before principals.
     102             :     SCTAG_JSPRINCIPALS,
     103             :     SCTAG_NULL_JSPRINCIPALS,
     104             :     SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM,
     105             :     SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM,
     106             : 
     107             :     SCTAG_SHARED_ARRAY_BUFFER_OBJECT,
     108             :     SCTAG_SHARED_WASM_MEMORY_OBJECT,
     109             : 
     110             :     SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
     111             :     SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
     112             :     SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
     113             :     SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int16,
     114             :     SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint16,
     115             :     SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int32,
     116             :     SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint32,
     117             :     SCTAG_TYPED_ARRAY_V1_FLOAT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float32,
     118             :     SCTAG_TYPED_ARRAY_V1_FLOAT64 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Float64,
     119             :     SCTAG_TYPED_ARRAY_V1_UINT8_CLAMPED = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8Clamped,
     120             :     SCTAG_TYPED_ARRAY_V1_MAX = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::MaxTypedArrayViewType - 1,
     121             : 
     122             :     // Define a separate range of numbers for Transferable-only tags, since
     123             :     // they are not used for persistent clone buffers and therefore do not
     124             :     // require bumping JS_STRUCTURED_CLONE_VERSION.
     125             :     SCTAG_TRANSFER_MAP_HEADER = 0xFFFF0200,
     126             :     SCTAG_TRANSFER_MAP_PENDING_ENTRY,
     127             :     SCTAG_TRANSFER_MAP_ARRAY_BUFFER,
     128             :     SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER,
     129             :     SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES,
     130             : 
     131             :     SCTAG_END_OF_BUILTIN_TYPES
     132             : };
     133             : 
     134             : /*
     135             :  * Format of transfer map:
     136             :  *   <SCTAG_TRANSFER_MAP_HEADER, TransferableMapHeader(UNREAD|TRANSFERRED)>
     137             :  *   numTransferables (64 bits)
     138             :  *   array of:
     139             :  *     <SCTAG_TRANSFER_MAP_*, TransferableOwnership>
     140             :  *     pointer (64 bits)
     141             :  *     extraData (64 bits), eg byte length for ArrayBuffers
     142             :  */
     143             : 
     144             : // Data associated with an SCTAG_TRANSFER_MAP_HEADER that tells whether the
     145             : // contents have been read out yet or not.
     146             : enum TransferableMapHeader {
     147             :     SCTAG_TM_UNREAD = 0,
     148             :     SCTAG_TM_TRANSFERRED
     149             : };
     150             : 
     151             : static inline uint64_t
     152             : PairToUInt64(uint32_t tag, uint32_t data)
     153             : {
     154       27582 :     return uint64_t(data) | (uint64_t(tag) << 32);
     155             : }
     156             : 
     157             : namespace js {
     158             : 
     159             : template<typename T, typename AllocPolicy>
     160             : struct BufferIterator {
     161             :     typedef mozilla::BufferList<AllocPolicy> BufferList;
     162             : 
     163             :     explicit BufferIterator(const BufferList& buffer)
     164             :         : mBuffer(buffer)
     165             :         , mIter(buffer.Iter())
     166             :     {
     167             :         JS_STATIC_ASSERT(8 % sizeof(T) == 0);
     168             :     }
     169             : 
     170         347 :     explicit BufferIterator(const JSStructuredCloneData& data)
     171             :       : mBuffer(data.bufList_)
     172         694 :       , mIter(data.Start())
     173             :     {
     174             :     }
     175             : 
     176           0 :     BufferIterator& operator=(const BufferIterator& other)
     177             :     {
     178           0 :         MOZ_ASSERT(&mBuffer == &other.mBuffer);
     179           0 :         mIter = other.mIter;
     180           0 :         return *this;
     181             :     }
     182             : 
     183           0 :     BufferIterator operator++(int) {
     184           0 :         BufferIterator ret = *this;
     185           1 :         if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
     186           0 :             MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
     187             :         }
     188           3 :         return ret;
     189             :     }
     190             : 
     191           0 :     BufferIterator& operator+=(size_t size) {
     192           1 :         if (!mIter.AdvanceAcrossSegments(mBuffer, size)) {
     193           0 :             MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
     194             :         }
     195       16251 :         return *this;
     196             :     }
     197             : 
     198           0 :     size_t operator-(const BufferIterator& other) {
     199           0 :         MOZ_ASSERT(&mBuffer == &other.mBuffer);
     200           0 :         return mBuffer.RangeLength(other.mIter, mIter);
     201             :     }
     202             : 
     203           0 :     void next() {
     204           1 :         if (!mIter.AdvanceAcrossSegments(mBuffer, sizeof(T))) {
     205           0 :             MOZ_ASSERT(false, "Failed to read StructuredCloneData. Data incomplete");
     206             :         }
     207       31258 :     }
     208             : 
     209             :     bool done() const {
     210         248 :         return mIter.Done();
     211             :     }
     212             : 
     213             :     MOZ_MUST_USE bool readBytes(char* outData, size_t size) {
     214       16251 :         return mBuffer.ReadBytes(mIter, outData, size);
     215             :     }
     216             : 
     217           0 :     void write(const T& data) {
     218           0 :         MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
     219           0 :         *reinterpret_cast<T*>(mIter.Data()) = data;
     220           5 :     }
     221             : 
     222           0 :     T peek() const {
     223           0 :         MOZ_ASSERT(mIter.HasRoomFor(sizeof(T)));
     224       49449 :         return *reinterpret_cast<T*>(mIter.Data());
     225             :     }
     226             : 
     227             :     bool canPeek() const {
     228       49446 :         return mIter.HasRoomFor(sizeof(T));
     229             :     }
     230             : 
     231             :     const BufferList& mBuffer;
     232             :     typename BufferList::IterImpl mIter;
     233             : };
     234             : 
     235             : SharedArrayRawBufferRefs&
     236         269 : SharedArrayRawBufferRefs::operator=(SharedArrayRawBufferRefs&& other)
     237             : {
     238           0 :     takeOwnership(std::move(other));
     239         269 :     return *this;
     240             : }
     241             : 
     242        1106 : SharedArrayRawBufferRefs::~SharedArrayRawBufferRefs()
     243             : {
     244           0 :     releaseAll();
     245         426 : }
     246             : 
     247             : bool
     248           0 : SharedArrayRawBufferRefs::acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf)
     249             : {
     250           0 :     if (!refs_.append(rawbuf)) {
     251           0 :         ReportOutOfMemory(cx);
     252           0 :         return false;
     253             :     }
     254             : 
     255           0 :     if (!rawbuf->addReference()) {
     256           0 :         refs_.popBack();
     257           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
     258           0 :         return false;
     259             :     }
     260             : 
     261             :     return true;
     262             : }
     263             : 
     264             : bool
     265           0 : SharedArrayRawBufferRefs::acquireAll(JSContext* cx, const SharedArrayRawBufferRefs& that)
     266             : {
     267           0 :     if (!refs_.reserve(refs_.length() + that.refs_.length())) {
     268           0 :         ReportOutOfMemory(cx);
     269           0 :         return false;
     270             :     }
     271             : 
     272           0 :     for (auto ref : that.refs_) {
     273           0 :         if (!ref->addReference()) {
     274           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
     275           0 :             return false;
     276             :         }
     277           0 :         MOZ_ALWAYS_TRUE(refs_.append(ref));
     278             :     }
     279             : 
     280           0 :     return true;
     281             : }
     282             : 
     283             : void
     284         324 : SharedArrayRawBufferRefs::takeOwnership(SharedArrayRawBufferRefs&& other)
     285             : {
     286           0 :     MOZ_ASSERT(refs_.empty());
     287           0 :     refs_ = std::move(other.refs_);
     288         324 : }
     289             : 
     290             : void
     291        1092 : SharedArrayRawBufferRefs::releaseAll()
     292             : {
     293           0 :     for (auto ref : refs_)
     294           0 :         ref->dropReference();
     295           0 :     refs_.clear();
     296        1092 : }
     297             : 
     298             : // SCOutput provides an interface to write raw data -- eg uint64_ts, doubles,
     299             : // arrays of bytes -- into a structured clone data output stream. It also knows
     300             : // how to free any transferable data within that stream.
     301             : //
     302             : // Note that it contains a full JSStructuredCloneData object, which holds the
     303             : // callbacks necessary to read/write/transfer/free the data. For the purpose of
     304             : // this class, only the freeTransfer callback is relevant; the rest of the callbacks
     305             : // are used by the higher-level JSStructuredCloneWriter interface.
     306         234 : struct SCOutput {
     307             :   public:
     308             :     using Iter = BufferIterator<uint64_t, SystemAllocPolicy>;
     309             : 
     310             :     SCOutput(JSContext* cx, JS::StructuredCloneScope scope);
     311             : 
     312             :     JSContext* context() const { return cx; }
     313         235 :     JS::StructuredCloneScope scope() const { return buf.scope(); }
     314             : 
     315             :     MOZ_MUST_USE bool write(uint64_t u);
     316             :     MOZ_MUST_USE bool writePair(uint32_t tag, uint32_t data);
     317             :     MOZ_MUST_USE bool writeDouble(double d);
     318             :     MOZ_MUST_USE bool writeBytes(const void* p, size_t nbytes);
     319             :     MOZ_MUST_USE bool writeChars(const Latin1Char* p, size_t nchars);
     320             :     MOZ_MUST_USE bool writeChars(const char16_t* p, size_t nchars);
     321             :     MOZ_MUST_USE bool writePtr(const void*);
     322             : 
     323             :     template <class T>
     324             :     MOZ_MUST_USE bool writeArray(const T* p, size_t nbytes);
     325             : 
     326             :     void setCallbacks(const JSStructuredCloneCallbacks* callbacks,
     327             :                       void* closure,
     328             :                       OwnTransferablePolicy policy)
     329             :     {
     330         468 :         buf.setCallbacks(callbacks, closure, policy);
     331             :     }
     332         234 :     void extractBuffer(JSStructuredCloneData* data) { *data = std::move(buf); }
     333             :     void discardTransferables();
     334             : 
     335           0 :     uint64_t tell() const { return buf.Size(); }
     336           0 :     uint64_t count() const { return buf.Size() / sizeof(uint64_t); }
     337           2 :     Iter iter() { return Iter(buf); }
     338             : 
     339             :     size_t offset(Iter dest) {
     340           0 :         return dest - iter();
     341             :     }
     342             : 
     343             :     JSContext* cx;
     344             :     JSStructuredCloneData buf;
     345             : };
     346             : 
     347             : class SCInput {
     348             :     typedef js::BufferIterator<uint64_t, SystemAllocPolicy> BufferIterator;
     349             : 
     350             :   public:
     351             :     SCInput(JSContext* cx, JSStructuredCloneData& data);
     352             : 
     353             :     JSContext* context() const { return cx; }
     354             : 
     355             :     static void getPtr(uint64_t data, void** ptr);
     356             :     static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
     357             : 
     358             :     MOZ_MUST_USE bool read(uint64_t* p);
     359             :     MOZ_MUST_USE bool readNativeEndian(uint64_t* p);
     360             :     MOZ_MUST_USE bool readPair(uint32_t* tagp, uint32_t* datap);
     361             :     MOZ_MUST_USE bool readDouble(double* p);
     362             :     MOZ_MUST_USE bool readBytes(void* p, size_t nbytes);
     363             :     MOZ_MUST_USE bool readChars(Latin1Char* p, size_t nchars);
     364             :     MOZ_MUST_USE bool readChars(char16_t* p, size_t nchars);
     365             :     MOZ_MUST_USE bool readPtr(void**);
     366             : 
     367             :     MOZ_MUST_USE bool get(uint64_t* p);
     368             :     MOZ_MUST_USE bool getPair(uint32_t* tagp, uint32_t* datap);
     369             : 
     370             :     const BufferIterator& tell() const { return point; }
     371           0 :     void seekTo(const BufferIterator& pos) { point = pos; }
     372           0 :     void seekBy(size_t pos) { point += pos; }
     373             : 
     374             :     template <class T>
     375             :     MOZ_MUST_USE bool readArray(T* p, size_t nelems);
     376             : 
     377             :     bool reportTruncated() {
     378             :          JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
     379           0 :                                    "truncated");
     380             :          return false;
     381             :     }
     382             : 
     383             :   private:
     384             :     void staticAssertions() {
     385             :         JS_STATIC_ASSERT(sizeof(char16_t) == 2);
     386             :         JS_STATIC_ASSERT(sizeof(uint32_t) == 4);
     387             :     }
     388             : 
     389             :     JSContext* cx;
     390             :     BufferIterator point;
     391             : };
     392             : 
     393             : } // namespace js
     394             : 
     395         669 : struct JSStructuredCloneReader {
     396             :   public:
     397             :     explicit JSStructuredCloneReader(SCInput& in, JS::StructuredCloneScope scope,
     398             :                                      const JSStructuredCloneCallbacks* cb,
     399             :                                      void* cbClosure)
     400           0 :         : in(in), allowedScope(scope), objs(in.context()), allObjs(in.context()),
     401         223 :           callbacks(cb), closure(cbClosure) { }
     402             : 
     403             :     SCInput& input() { return in; }
     404             :     bool read(MutableHandleValue vp);
     405             : 
     406             :   private:
     407      118975 :     JSContext* context() { return in.context(); }
     408             : 
     409             :     bool readHeader();
     410             :     bool readTransferMap();
     411             : 
     412             :     template <typename CharT>
     413             :     JSString* readStringImpl(uint32_t nchars);
     414             :     JSString* readString(uint32_t data);
     415             : 
     416             :     bool checkDouble(double d);
     417             :     MOZ_MUST_USE bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
     418             :                                      bool v1Read = false);
     419             :     MOZ_MUST_USE bool readDataView(uint32_t byteLength, MutableHandleValue vp);
     420             :     MOZ_MUST_USE bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     421             :     MOZ_MUST_USE bool readSharedArrayBuffer(MutableHandleValue vp);
     422             :     MOZ_MUST_USE bool readSharedWasmMemory(uint32_t nbytes, MutableHandleValue vp);
     423             :     MOZ_MUST_USE bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     424             :     JSObject* readSavedFrame(uint32_t principalsTag);
     425             :     MOZ_MUST_USE bool startRead(MutableHandleValue vp);
     426             : 
     427             :     SCInput& in;
     428             : 
     429             :     // The widest scope that the caller will accept, where
     430             :     // SameProcessSameThread is the widest (it can store anything it wants) and
     431             :     // DifferentProcess is the narrowest (it cannot contain pointers and must
     432             :     // be valid cross-process.)
     433             :     JS::StructuredCloneScope allowedScope;
     434             : 
     435             :     // Stack of objects with properties remaining to be read.
     436             :     AutoValueVector objs;
     437             : 
     438             :     // Array of all objects read during this deserialization, for resolving
     439             :     // backreferences.
     440             :     //
     441             :     // For backreferences to work correctly, objects must be added to this
     442             :     // array in exactly the order expected by the version of the Writer that
     443             :     // created the serialized data, even across years and format versions. This
     444             :     // is usually no problem, since both algorithms do a single linear pass
     445             :     // over the serialized data. There is one hitch; see readTypedArray.
     446             :     //
     447             :     // The values in this vector are objects, except it can temporarily have
     448             :     // one `undefined` placeholder value (the readTypedArray hack).
     449             :     AutoValueVector allObjs;
     450             : 
     451             :     // The user defined callbacks that will be used for cloning.
     452             :     const JSStructuredCloneCallbacks* callbacks;
     453             : 
     454             :     // Any value passed to JS_ReadStructuredClone.
     455             :     void* closure;
     456             : 
     457             :     friend bool JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp);
     458             : };
     459             : 
     460             : struct JSStructuredCloneWriter {
     461             :   public:
     462         234 :     explicit JSStructuredCloneWriter(JSContext* cx,
     463             :                                      JS::StructuredCloneScope scope,
     464             :                                      JS::CloneDataPolicy cloneDataPolicy,
     465             :                                      const JSStructuredCloneCallbacks* cb,
     466             :                                      void* cbClosure,
     467             :                                      const Value& tVal)
     468         234 :         : out(cx, scope), objs(out.context()),
     469             :           counts(out.context()), entries(out.context()),
     470           0 :           memory(out.context()),
     471           0 :           transferable(out.context(), tVal),
     472           0 :           transferableObjects(out.context(), GCHashSet<JSObject*>(cx)),
     473        1872 :           cloneDataPolicy(cloneDataPolicy)
     474             :     {
     475           0 :         out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables);
     476         234 :     }
     477             : 
     478             :     ~JSStructuredCloneWriter();
     479             : 
     480           0 :     bool init() {
     481           0 :         if (!memory.init()) {
     482           0 :             ReportOutOfMemory(context());
     483           0 :             return false;
     484             :         }
     485         468 :         return parseTransferable() && writeHeader() && writeTransferMap();
     486             :     }
     487             : 
     488             :     bool write(HandleValue v);
     489             : 
     490         113 :     SCOutput& output() { return out; }
     491             : 
     492             :     void extractBuffer(JSStructuredCloneData* newData) {
     493         468 :         out.extractBuffer(newData);
     494             :     }
     495             : 
     496             :   private:
     497             :     JSStructuredCloneWriter() = delete;
     498             :     JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
     499             : 
     500      218760 :     JSContext* context() { return out.context(); }
     501             : 
     502             :     bool writeHeader();
     503             :     bool writeTransferMap();
     504             : 
     505             :     bool writeString(uint32_t tag, JSString* str);
     506             :     bool writeArrayBuffer(HandleObject obj);
     507             :     bool writeTypedArray(HandleObject obj);
     508             :     bool writeDataView(HandleObject obj);
     509             :     bool writeSharedArrayBuffer(HandleObject obj);
     510             :     bool writeSharedWasmMemory(HandleObject obj);
     511             :     bool startObject(HandleObject obj, bool* backref);
     512             :     bool startWrite(HandleValue v);
     513             :     bool traverseObject(HandleObject obj);
     514             :     bool traverseMap(HandleObject obj);
     515             :     bool traverseSet(HandleObject obj);
     516             :     bool traverseSavedFrame(HandleObject obj);
     517             : 
     518             :     bool reportDataCloneError(uint32_t errorId);
     519             : 
     520             :     bool parseTransferable();
     521             :     bool transferOwnership();
     522             : 
     523             :     inline void checkStack();
     524             : 
     525             :     SCOutput out;
     526             : 
     527             :     // Vector of objects with properties remaining to be written.
     528             :     //
     529             :     // NB: These can span multiple compartments, so the compartment must be
     530             :     // entered before any manipulation is performed.
     531             :     AutoValueVector objs;
     532             : 
     533             :     // counts[i] is the number of entries of objs[i] remaining to be written.
     534             :     // counts.length() == objs.length() and sum(counts) == entries.length().
     535             :     Vector<size_t> counts;
     536             : 
     537             :     // For JSObject: Property IDs as value
     538             :     // For Map: Key followed by value
     539             :     // For Set: Key
     540             :     // For SavedFrame: parent SavedFrame
     541             :     AutoValueVector entries;
     542             : 
     543             :     // The "memory" list described in the HTML5 internal structured cloning
     544             :     // algorithm.  memory is a superset of objs; items are never removed from
     545             :     // Memory until a serialization operation is finished
     546             :     using CloneMemory = GCHashMap<JSObject*,
     547             :                                   uint32_t,
     548             :                                   MovableCellHasher<JSObject*>,
     549             :                                   SystemAllocPolicy>;
     550             :     Rooted<CloneMemory> memory;
     551             : 
     552             :     // Set of transferable objects
     553             :     RootedValue transferable;
     554             :     Rooted<GCHashSet<JSObject*>> transferableObjects;
     555             : 
     556             :     const JS::CloneDataPolicy cloneDataPolicy;
     557             : 
     558             :     friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
     559             :     friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
     560             :     friend bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj);
     561             : };
     562             : 
     563             : JS_FRIEND_API(uint64_t)
     564           0 : js::GetSCOffset(JSStructuredCloneWriter* writer)
     565             : {
     566           0 :     MOZ_ASSERT(writer);
     567           0 :     return writer->output().count() * sizeof(uint64_t);
     568             : }
     569             : 
     570             : JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
     571             : JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
     572             : JS_STATIC_ASSERT(Scalar::Int8 == 0);
     573             : 
     574             : static void
     575           0 : ReportDataCloneError(JSContext* cx,
     576             :                      const JSStructuredCloneCallbacks* callbacks,
     577             :                      uint32_t errorId)
     578             : {
     579           0 :     if (callbacks && callbacks->reportError) {
     580           0 :         callbacks->reportError(cx, errorId);
     581           0 :         return;
     582             :     }
     583             : 
     584           0 :     switch (errorId) {
     585             :       case JS_SCERR_DUP_TRANSFERABLE:
     586           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_DUP_TRANSFERABLE);
     587           0 :         break;
     588             : 
     589             :       case JS_SCERR_TRANSFERABLE:
     590           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_NOT_TRANSFERABLE);
     591           0 :         break;
     592             : 
     593             :       case JS_SCERR_UNSUPPORTED_TYPE:
     594           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_UNSUPPORTED_TYPE);
     595           0 :         break;
     596             : 
     597             :       case JS_SCERR_SHMEM_TRANSFERABLE:
     598           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SHMEM_TRANSFERABLE);
     599           0 :         break;
     600             : 
     601             :       default:
     602           0 :         MOZ_CRASH("Unkown errorId");
     603             :         break;
     604             :     }
     605             : }
     606             : 
     607             : bool
     608         234 : WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp,
     609             :                      JS::StructuredCloneScope scope,
     610             :                      JS::CloneDataPolicy cloneDataPolicy,
     611             :                      const JSStructuredCloneCallbacks* cb, void* cbClosure,
     612             :                      const Value& transferable)
     613             : {
     614           0 :     JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable);
     615         234 :     if (!w.init())
     616             :         return false;
     617         234 :     if (!w.write(v))
     618             :         return false;
     619           0 :     w.extractBuffer(bufp);
     620         234 :     return true;
     621             : }
     622             : 
     623             : bool
     624         223 : ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data,
     625             :                     JS::StructuredCloneScope scope, MutableHandleValue vp,
     626             :                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
     627             : {
     628           0 :     SCInput in(cx, data);
     629           0 :     JSStructuredCloneReader r(in, scope, cb, cbClosure);
     630         446 :     return r.read(vp);
     631             : }
     632             : 
     633             : static bool
     634           0 : StructuredCloneHasTransferObjects(const JSStructuredCloneData& data)
     635             : {
     636           0 :     if (data.Size() < sizeof(uint64_t))
     637             :         return false;
     638             : 
     639             :     uint64_t u;
     640           0 :     BufferIterator<uint64_t, SystemAllocPolicy> iter(data);
     641           0 :     MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u)));
     642           0 :     uint32_t tag = uint32_t(u >> 32);
     643           0 :     return (tag == SCTAG_TRANSFER_MAP_HEADER);
     644             : }
     645             : 
     646             : namespace js {
     647             : 
     648           0 : SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data)
     649         446 :     : cx(cx), point(data)
     650             : {
     651             : 
     652             :     static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0,
     653             :                   "structured clone buffer reads should be aligned");
     654           0 :     MOZ_ASSERT(data.Size() % 8 == 0);
     655         223 : }
     656             : 
     657             : bool
     658       31008 : SCInput::read(uint64_t* p)
     659             : {
     660           1 :     if (!point.canPeek()) {
     661           0 :         *p = 0;  // initialize to shut GCC up
     662           0 :         return reportTruncated();
     663             :     }
     664           0 :     *p = NativeEndian::swapFromLittleEndian(point.peek());
     665           0 :     point.next();
     666       31008 :     return true;
     667             : }
     668             : 
     669             : bool
     670           1 : SCInput::readNativeEndian(uint64_t* p)
     671             : {
     672           1 :     if (!point.canPeek()) {
     673           0 :         *p = 0;  // initialize to shut GCC up
     674           0 :         return reportTruncated();
     675             :     }
     676           0 :     *p = point.peek();
     677           0 :     point.next();
     678           1 :     return true;
     679             : }
     680             : 
     681             : bool
     682           0 : SCInput::readPair(uint32_t* tagp, uint32_t* datap)
     683             : {
     684             :     uint64_t u;
     685           0 :     bool ok = read(&u);
     686           0 :     if (ok) {
     687           0 :         *tagp = uint32_t(u >> 32);
     688       25972 :         *datap = uint32_t(u);
     689             :     }
     690           0 :     return ok;
     691             : }
     692             : 
     693             : bool
     694       18188 : SCInput::get(uint64_t* p)
     695             : {
     696           0 :     if (!point.canPeek())
     697           0 :         return reportTruncated();
     698           0 :     *p = NativeEndian::swapFromLittleEndian(point.peek());
     699       18188 :     return true;
     700             : }
     701             : 
     702             : bool
     703           0 : SCInput::getPair(uint32_t* tagp, uint32_t* datap)
     704             : {
     705           0 :     uint64_t u = 0;
     706       18188 :     if (!get(&u))
     707             :         return false;
     708             : 
     709           0 :     *tagp = uint32_t(u >> 32);
     710           1 :     *datap = uint32_t(u);
     711           0 :     return true;
     712             : }
     713             : 
     714             : void
     715           0 : SCInput::getPair(uint64_t data, uint32_t* tagp, uint32_t* datap)
     716             : {
     717           0 :     uint64_t u = NativeEndian::swapFromLittleEndian(data);
     718           0 :     *tagp = uint32_t(u >> 32);
     719           1 :     *datap = uint32_t(u);
     720           0 : }
     721             : 
     722             : bool
     723          48 : SCInput::readDouble(double* p)
     724             : {
     725             :     union {
     726             :         uint64_t u;
     727             :         double d;
     728             :     } pun;
     729          48 :     if (!read(&pun.u))
     730             :         return false;
     731           0 :     *p = CanonicalizeNaN(pun.d);
     732          48 :     return true;
     733             : }
     734             : 
     735             : template <typename T>
     736             : static void
     737             : swapFromLittleEndianInPlace(T* ptr, size_t nelems)
     738             : {
     739             :     if (nelems > 0)
     740          44 :         NativeEndian::swapFromLittleEndianInPlace(ptr, nelems);
     741             : }
     742             : 
     743             : template <>
     744             : void
     745             : swapFromLittleEndianInPlace(uint8_t* ptr, size_t nelems)
     746             : {}
     747             : 
     748             : // Data is packed into an integral number of uint64_t words. Compute the
     749             : // padding required to finish off the final word.
     750             : static size_t
     751             : ComputePadding(size_t nelems, size_t elemSize)
     752             : {
     753             :     // We want total length mod 8, where total length is nelems * sizeof(T),
     754             :     // but that might overflow. So reduce nelems to nelems mod 8, since we are
     755             :     // going to be doing a mod 8 later anyway.
     756           0 :     size_t leftoverLength = (nelems % sizeof(uint64_t)) * elemSize;
     757       33788 :     return (-leftoverLength) & (sizeof(uint64_t) - 1);
     758             : }
     759             : 
     760             : template <class T>
     761             : bool
     762       16274 : SCInput::readArray(T* p, size_t nelems)
     763             : {
     764       16274 :     if (!nelems)
     765             :         return true;
     766             : 
     767             :     JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
     768             : 
     769             :     // Fail if nelems is so huge that computing the full size will overflow.
     770           0 :     mozilla::CheckedInt<size_t> size = mozilla::CheckedInt<size_t>(nelems) * sizeof(T);
     771           1 :     if (!size.isValid())
     772           0 :         return reportTruncated();
     773             : 
     774       32502 :     if (!point.readBytes(reinterpret_cast<char*>(p), size.value()))
     775             :         return false;
     776             : 
     777       16251 :     swapFromLittleEndianInPlace(p, nelems);
     778             : 
     779       16251 :     point += ComputePadding(nelems, sizeof(T));
     780             : 
     781       16251 :     return true;
     782             : }
     783             : 
     784             : bool
     785           0 : SCInput::readBytes(void* p, size_t nbytes)
     786             : {
     787       16230 :     return readArray((uint8_t*) p, nbytes);
     788             : }
     789             : 
     790             : bool
     791           0 : SCInput::readChars(Latin1Char* p, size_t nchars)
     792             : {
     793             :     static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
     794       16208 :     return readBytes(p, nchars);
     795             : }
     796             : 
     797             : bool
     798           0 : SCInput::readChars(char16_t* p, size_t nchars)
     799             : {
     800             :     MOZ_ASSERT(sizeof(char16_t) == sizeof(uint16_t));
     801          44 :     return readArray((uint16_t*) p, nchars);
     802             : }
     803             : 
     804             : void
     805           0 : SCInput::getPtr(uint64_t data, void** ptr)
     806             : {
     807             :     // No endianness conversion is used for pointers, since they are not sent
     808             :     // across address spaces anyway.
     809           0 :     *ptr = reinterpret_cast<void*>(data);
     810           0 : }
     811             : 
     812             : bool
     813           0 : SCInput::readPtr(void** p)
     814             : {
     815             :     uint64_t u;
     816           1 :     if (!readNativeEndian(&u))
     817             :         return false;
     818           1 :     *p = reinterpret_cast<void*>(NativeEndian::swapFromLittleEndian(u));
     819           0 :     return true;
     820             : }
     821             : 
     822           0 : SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope)
     823         234 :   : cx(cx), buf(scope)
     824             : {
     825           0 : }
     826             : 
     827             : bool
     828       33440 : SCOutput::write(uint64_t u)
     829             : {
     830           0 :     uint64_t v = NativeEndian::swapToLittleEndian(u);
     831           0 :     if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) {
     832           0 :         ReportOutOfMemory(context());
     833           0 :         return false;
     834             :     }
     835             :     return true;
     836             : }
     837             : 
     838             : bool
     839           0 : SCOutput::writePair(uint32_t tag, uint32_t data)
     840             : {
     841             :     // As it happens, the tag word appears after the data word in the output.
     842             :     // This is because exponents occupy the last 2 bytes of doubles on the
     843             :     // little-endian platforms we care most about.
     844             :     //
     845             :     // For example, TrueValue() is written using writePair(SCTAG_BOOLEAN, 1).
     846             :     // PairToUInt64 produces the number 0xFFFF000200000001.
     847             :     // That is written out as the bytes 01 00 00 00 02 00 FF FF.
     848       33353 :     return write(PairToUInt64(tag, data));
     849             : }
     850             : 
     851             : static inline double
     852             : ReinterpretPairAsDouble(uint32_t tag, uint32_t data)
     853             : {
     854          98 :     return BitwiseCast<double>(PairToUInt64(tag, data));
     855             : }
     856             : 
     857             : bool
     858           0 : SCOutput::writeDouble(double d)
     859             : {
     860         164 :     return write(BitwiseCast<uint64_t>(CanonicalizeNaN(d)));
     861             : }
     862             : 
     863             : template <class T>
     864             : bool
     865          87 : SCOutput::writeArray(const T* p, size_t nelems)
     866             : {
     867             :     JS_STATIC_ASSERT(8 % sizeof(T) == 0);
     868             :     JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
     869             : 
     870          87 :     if (nelems == 0)
     871             :         return true;
     872             : 
     873           0 :     for (size_t i = 0; i < nelems; i++) {
     874           0 :         T value = NativeEndian::swapToLittleEndian(p[i]);
     875           1 :         if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value)))
     876           0 :             return false;
     877             :     }
     878             : 
     879             :     // Zero-pad to 8 bytes boundary.
     880           0 :     size_t padbytes = ComputePadding(nelems, sizeof(T));
     881           0 :     char zeroes[sizeof(uint64_t)] = { 0 };
     882          87 :     if (!buf.AppendBytes(zeroes, padbytes))
     883             :         return false;
     884             : 
     885          87 :     return true;
     886             : }
     887             : 
     888             : template <>
     889             : bool
     890       17467 : SCOutput::writeArray<uint8_t>(const uint8_t* p, size_t nelems)
     891             : {
     892       17467 :     if (nelems == 0)
     893             :         return true;
     894             : 
     895       17450 :     if (!buf.AppendBytes(reinterpret_cast<const char*>(p), nelems))
     896             :         return false;
     897             : 
     898             :     // zero-pad to 8 bytes boundary
     899           0 :     size_t padbytes = ComputePadding(nelems, 1);
     900           0 :     char zeroes[sizeof(uint64_t)] = { 0 };
     901       17450 :     if (!buf.AppendBytes(zeroes, padbytes))
     902             :         return false;
     903             : 
     904       17450 :     return true;
     905             : }
     906             : 
     907             : bool
     908           0 : SCOutput::writeBytes(const void* p, size_t nbytes)
     909             : {
     910       17467 :     return writeArray((const uint8_t*) p, nbytes);
     911             : }
     912             : 
     913             : bool
     914           0 : SCOutput::writeChars(const char16_t* p, size_t nchars)
     915             : {
     916             :     static_assert(sizeof(char16_t) == sizeof(uint16_t),
     917             :                   "required so that treating char16_t[] memory as uint16_t[] "
     918             :                   "memory is permissible");
     919          87 :     return writeArray((const uint16_t*) p, nchars);
     920             : }
     921             : 
     922             : bool
     923           0 : SCOutput::writeChars(const Latin1Char* p, size_t nchars)
     924             : {
     925             :     static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
     926       17418 :     return writeBytes(p, nchars);
     927             : }
     928             : 
     929             : bool
     930           0 : SCOutput::writePtr(const void* p)
     931             : {
     932           1 :     return write(reinterpret_cast<uint64_t>(p));
     933             : }
     934             : 
     935             : void
     936           0 : SCOutput::discardTransferables()
     937             : {
     938           0 :     buf.discardTransferables();
     939           0 : }
     940             : 
     941             : } // namespace js
     942             : 
     943             : 
     944             : // If the buffer contains Transferables, free them. Note that custom
     945             : // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
     946             : // delete their transferables.
     947             : void
     948        1504 : JSStructuredCloneData::discardTransferables()
     949             : {
     950           0 :     if (!Size())
     951        1504 :         return;
     952             : 
     953         326 :     if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny)
     954             :         return;
     955             : 
     956             :     // DifferentProcess clones cannot contain pointers, so nothing needs to be
     957             :     // released.
     958         123 :     if (scope_ == JS::StructuredCloneScope::DifferentProcess)
     959             :         return;
     960             : 
     961           0 :     FreeTransferStructuredCloneOp freeTransfer = nullptr;
     962           0 :     if (callbacks_)
     963         103 :         freeTransfer = callbacks_->freeTransfer;
     964             : 
     965           0 :     auto point = BufferIterator<uint64_t, SystemAllocPolicy>(*this);
     966         123 :     if (point.done())
     967             :         return; // Empty buffer
     968             : 
     969             :     uint32_t tag, data;
     970           0 :     MOZ_RELEASE_ASSERT(point.canPeek());
     971           0 :     SCInput::getPair(point.peek(), &tag, &data);
     972         123 :     point.next();
     973             : 
     974           0 :     if (tag == SCTAG_HEADER) {
     975         123 :         if (point.done())
     976             :             return;
     977             : 
     978           0 :         MOZ_RELEASE_ASSERT(point.canPeek());
     979           0 :         SCInput::getPair(point.peek(), &tag, &data);
     980         123 :         point.next();
     981             :     }
     982             : 
     983         123 :     if (tag != SCTAG_TRANSFER_MAP_HEADER)
     984             :         return;
     985             : 
     986           1 :     if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
     987             :         return;
     988             : 
     989             :     // freeTransfer should not GC
     990           0 :     JS::AutoSuppressGCAnalysis nogc;
     991             : 
     992           0 :     if (point.done())
     993             :         return;
     994             : 
     995           0 :     uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek());
     996           0 :     point.next();
     997           0 :     while (numTransferables--) {
     998           0 :         if (!point.canPeek())
     999             :             return;
    1000             : 
    1001             :         uint32_t ownership;
    1002           0 :         SCInput::getPair(point.peek(), &tag, &ownership);
    1003           0 :         point.next();
    1004           0 :         MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY);
    1005           0 :         if (!point.canPeek())
    1006             :             return;
    1007             : 
    1008             :         void* content;
    1009           0 :         SCInput::getPtr(point.peek(), &content);
    1010           0 :         point.next();
    1011           0 :         if (!point.canPeek())
    1012             :             return;
    1013             : 
    1014           0 :         uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek());
    1015           0 :         point.next();
    1016             : 
    1017           0 :         if (ownership < JS::SCTAG_TMO_FIRST_OWNED)
    1018             :             continue;
    1019             : 
    1020           0 :         if (ownership == JS::SCTAG_TMO_ALLOC_DATA) {
    1021           0 :             js_free(content);
    1022           0 :         } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) {
    1023           0 :             JS_ReleaseMappedArrayBufferContents(content, extraData);
    1024           0 :         } else if (freeTransfer) {
    1025           0 :             freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, closure_);
    1026             :         } else {
    1027           0 :             MOZ_ASSERT(false, "unknown ownership");
    1028             :         }
    1029             :     }
    1030             : }
    1031             : 
    1032             : JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
    1033             : 
    1034        1170 : JSStructuredCloneWriter::~JSStructuredCloneWriter()
    1035             : {
    1036             :     // Free any transferable data left lying around in the buffer
    1037           0 :     if (out.count())
    1038           0 :         out.discardTransferables();
    1039         234 : }
    1040             : 
    1041             : bool
    1042         234 : JSStructuredCloneWriter::parseTransferable()
    1043             : {
    1044             :     // NOTE: The transferables set is tested for non-emptiness at various
    1045             :     //       junctures in structured cloning, so this set must be initialized
    1046             :     //       by this method in all non-error cases.
    1047         468 :     MOZ_ASSERT(!transferableObjects.initialized(),
    1048             :                "parseTransferable called with stale data");
    1049             : 
    1050           0 :     if (transferable.isNull() || transferable.isUndefined())
    1051         466 :         return transferableObjects.init(0);
    1052             : 
    1053           1 :     if (!transferable.isObject())
    1054           0 :         return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1055             : 
    1056           0 :     JSContext* cx = context();
    1057           3 :     RootedObject array(cx, &transferable.toObject());
    1058             :     bool isArray;
    1059           1 :     if (!JS_IsArrayObject(cx, array, &isArray))
    1060             :         return false;
    1061           1 :     if (!isArray)
    1062           0 :         return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1063             : 
    1064             :     uint32_t length;
    1065           1 :     if (!JS_GetArrayLength(cx, array, &length))
    1066             :         return false;
    1067             : 
    1068             :     // Initialize the set for the provided array's length.
    1069           2 :     if (!transferableObjects.init(length))
    1070             :         return false;
    1071             : 
    1072           1 :     if (length == 0)
    1073             :         return true;
    1074             : 
    1075           0 :     RootedValue v(context());
    1076           3 :     RootedObject tObj(context());
    1077             : 
    1078           0 :     for (uint32_t i = 0; i < length; ++i) {
    1079           0 :         if (!CheckForInterrupt(cx))
    1080           0 :             return false;
    1081             : 
    1082           2 :         if (!JS_GetElement(cx, array, i, &v))
    1083             :             return false;
    1084             : 
    1085           0 :         if (!v.isObject())
    1086           0 :             return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1087           2 :         tObj = &v.toObject();
    1088             : 
    1089           0 :         RootedObject unwrappedObj(cx, CheckedUnwrap(tObj));
    1090           1 :         if (!unwrappedObj) {
    1091           0 :             ReportAccessDenied(cx);
    1092           0 :             return false;
    1093             :         }
    1094             : 
    1095             :         // Shared memory cannot be transferred because it is not possible (nor
    1096             :         // desirable) to detach the memory in agents that already hold a
    1097             :         // reference to it.
    1098             : 
    1099           1 :         if (unwrappedObj->is<SharedArrayBufferObject>())
    1100           0 :             return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
    1101             : 
    1102           0 :         else if (unwrappedObj->is<WasmMemoryObject>()) {
    1103           0 :             if (unwrappedObj->as<WasmMemoryObject>().isShared())
    1104           0 :                 return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE);
    1105             :         }
    1106             : 
    1107             :         // External array buffers may be able to be transferred in the future,
    1108             :         // but that is not currently implemented.
    1109             : 
    1110           0 :         else if (unwrappedObj->is<ArrayBufferObject>()) {
    1111           1 :             if (unwrappedObj->as<ArrayBufferObject>().isExternal())
    1112           0 :                 return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1113             :         }
    1114             : 
    1115             :         else  {
    1116           0 :             if (!out.buf.callbacks_ || !out.buf.callbacks_->canTransfer)
    1117           0 :                 return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1118             : 
    1119           0 :             JSAutoRealm ar(cx, unwrappedObj);
    1120           0 :             if (!out.buf.callbacks_->canTransfer(cx, unwrappedObj, out.buf.closure_))
    1121           0 :                 return false;
    1122             :         }
    1123             : 
    1124             :         // No duplicates allowed
    1125           0 :         auto p = transferableObjects.lookupForAdd(tObj);
    1126           0 :         if (p)
    1127           0 :             return reportDataCloneError(JS_SCERR_DUP_TRANSFERABLE);
    1128             : 
    1129           2 :         if (!transferableObjects.add(p, tObj))
    1130             :             return false;
    1131             :     }
    1132             : 
    1133             :     return true;
    1134             : }
    1135             : 
    1136             : bool
    1137           0 : JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId)
    1138             : {
    1139           0 :     ReportDataCloneError(context(), out.buf.callbacks_, errorId);
    1140           0 :     return false;
    1141             : }
    1142             : 
    1143             : bool
    1144       17505 : JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str)
    1145             : {
    1146           0 :     JSLinearString* linear = str->ensureLinear(context());
    1147       17505 :     if (!linear)
    1148             :         return false;
    1149             : 
    1150             :     static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
    1151             : 
    1152           0 :     uint32_t length = linear->length();
    1153           0 :     uint32_t lengthAndEncoding = length | (uint32_t(linear->hasLatin1Chars()) << 31);
    1154       35010 :     if (!out.writePair(tag, lengthAndEncoding))
    1155             :         return false;
    1156             : 
    1157           0 :     JS::AutoCheckCannotGC nogc;
    1158           0 :     return linear->hasLatin1Chars()
    1159           0 :            ? out.writeChars(linear->latin1Chars(nogc), length)
    1160       17679 :            : out.writeChars(linear->twoByteChars(nogc), length);
    1161             : }
    1162             : 
    1163             : inline void
    1164       19632 : JSStructuredCloneWriter::checkStack()
    1165             : {
    1166             : #ifdef DEBUG
    1167             :     // To avoid making serialization O(n^2), limit stack-checking at 10.
    1168       19632 :     const size_t MAX = 10;
    1169             : 
    1170           0 :     size_t limit = Min(counts.length(), MAX);
    1171       39264 :     MOZ_ASSERT(objs.length() == counts.length());
    1172             :     size_t total = 0;
    1173           0 :     for (size_t i = 0; i < limit; i++) {
    1174           0 :         MOZ_ASSERT(total + counts[i] >= total);
    1175      103378 :         total += counts[i];
    1176             :     }
    1177           0 :     if (counts.length() <= MAX)
    1178       38954 :         MOZ_ASSERT(total == entries.length());
    1179             :     else
    1180         310 :         MOZ_ASSERT(total <= entries.length());
    1181             : 
    1182           0 :     size_t j = objs.length();
    1183           0 :     for (size_t i = 0; i < limit; i++) {
    1184           0 :         --j;
    1185      310134 :         MOZ_ASSERT(memory.has(&objs[j].toObject()));
    1186             :     }
    1187             : #endif
    1188       19632 : }
    1189             : 
    1190             : /*
    1191             :  * Write out a typed array. Note that post-v1 structured clone buffers do not
    1192             :  * perform endianness conversion on stored data, so multibyte typed arrays
    1193             :  * cannot be deserialized into a different endianness machine. Endianness
    1194             :  * conversion would prevent sharing ArrayBuffers: if you have Int8Array and
    1195             :  * Int16Array views of the same ArrayBuffer, should the data bytes be
    1196             :  * byte-swapped when writing or not? The Int8Array requires them to not be
    1197             :  * swapped; the Int16Array requires that they are.
    1198             :  */
    1199             : bool
    1200           1 : JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
    1201             : {
    1202           0 :     Rooted<TypedArrayObject*> tarr(context(), &CheckedUnwrap(obj)->as<TypedArrayObject>());
    1203           4 :     JSAutoRealm ar(context(), tarr);
    1204             : 
    1205           2 :     if (!TypedArrayObject::ensureHasBuffer(context(), tarr))
    1206             :         return false;
    1207             : 
    1208           2 :     if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length()))
    1209             :         return false;
    1210           0 :     uint64_t type = tarr->type();
    1211           1 :     if (!out.write(type))
    1212             :         return false;
    1213             : 
    1214             :     // Write out the ArrayBuffer tag and contents
    1215           0 :     RootedValue val(context(), TypedArrayObject::bufferValue(tarr));
    1216           1 :     if (!startWrite(val))
    1217             :         return false;
    1218             : 
    1219           2 :     return out.write(tarr->byteOffset());
    1220             : }
    1221             : 
    1222             : bool
    1223           0 : JSStructuredCloneWriter::writeDataView(HandleObject obj)
    1224             : {
    1225           0 :     Rooted<DataViewObject*> view(context(), &CheckedUnwrap(obj)->as<DataViewObject>());
    1226           0 :     JSAutoRealm ar(context(), view);
    1227             : 
    1228           0 :     if (!out.writePair(SCTAG_DATA_VIEW_OBJECT, view->byteLength()))
    1229             :         return false;
    1230             : 
    1231             :     // Write out the ArrayBuffer tag and contents
    1232           0 :     RootedValue val(context(), DataViewObject::bufferValue(view));
    1233           0 :     if (!startWrite(val))
    1234             :         return false;
    1235             : 
    1236           0 :     return out.write(view->byteOffset());
    1237             : }
    1238             : 
    1239             : bool
    1240           0 : JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
    1241             : {
    1242           0 :     Rooted<ArrayBufferObject*> buffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
    1243           0 :     JSAutoRealm ar(context(), buffer);
    1244             : 
    1245           0 :     return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer->byteLength()) &&
    1246           0 :            out.writeBytes(buffer->dataPointer(), buffer->byteLength());
    1247             : }
    1248             : 
    1249             : bool
    1250           0 : JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
    1251             : {
    1252           0 :     MOZ_ASSERT(CheckedUnwrap(obj) && CheckedUnwrap(obj)->is<SharedArrayBufferObject>());
    1253             : 
    1254           0 :     if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
    1255           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE,
    1256           0 :                                   "SharedArrayBuffer");
    1257           0 :         return false;
    1258             :     }
    1259             : 
    1260             :     // We must not transmit SAB pointers (including for WebAssembly.Memory)
    1261             :     // cross-process.  The cloneDataPolicy should have guarded against this;
    1262             :     // since it did not then throw, with a very explicit message.
    1263             : 
    1264           0 :     if (output().scope() > JS::StructuredCloneScope::SameProcessDifferentThread) {
    1265           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SHMEM_POLICY);
    1266           0 :         return false;
    1267             :     }
    1268             : 
    1269           0 :     Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
    1270           0 :     SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
    1271             : 
    1272           0 :     if (!out.buf.refsHeld_.acquire(context(), rawbuf))
    1273             :         return false;
    1274             : 
    1275             :     // We must serialize the length so that the buffer object arrives in the
    1276             :     // receiver with the same length, and not with the length read from the
    1277             :     // rawbuf - that length can be different, and it can change at any time.
    1278             : 
    1279           0 :     intptr_t p = reinterpret_cast<intptr_t>(rawbuf);
    1280           0 :     uint32_t byteLength = sharedArrayBuffer->byteLength();
    1281           0 :     return out.writePair(SCTAG_SHARED_ARRAY_BUFFER_OBJECT, static_cast<uint32_t>(sizeof(p))) &&
    1282           0 :            out.writeBytes(&byteLength, sizeof(byteLength)) &&
    1283           0 :            out.writeBytes(&p, sizeof(p));
    1284             : }
    1285             : 
    1286             : bool
    1287           0 : JSStructuredCloneWriter::writeSharedWasmMemory(HandleObject obj)
    1288             : {
    1289           0 :     MOZ_ASSERT(CheckedUnwrap(obj) && CheckedUnwrap(obj)->is<WasmMemoryObject>());
    1290             : 
    1291             :     // Check the policy here so that we can report a sane error.
    1292           0 :     if (!cloneDataPolicy.isSharedArrayBufferAllowed()) {
    1293           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE,
    1294           0 :                                   "WebAssembly.Memory");
    1295           0 :         return false;
    1296             :     }
    1297             : 
    1298             :     // If this changes, might need to change what we write.
    1299             :     MOZ_ASSERT(WasmMemoryObject::RESERVED_SLOTS == 2);
    1300             : 
    1301           0 :     Rooted<WasmMemoryObject*> memoryObj(context(), &CheckedUnwrap(obj)->as<WasmMemoryObject>());
    1302           0 :     Rooted<SharedArrayBufferObject*> sab(context(), &memoryObj->buffer().as<SharedArrayBufferObject>());
    1303             : 
    1304           0 :     return out.writePair(SCTAG_SHARED_WASM_MEMORY_OBJECT, 0) &&
    1305           0 :            writeSharedArrayBuffer(sab);
    1306             : }
    1307             : 
    1308             : bool
    1309        5492 : JSStructuredCloneWriter::startObject(HandleObject obj, bool* backref)
    1310             : {
    1311             :     // Handle cycles in the object graph.
    1312           0 :     CloneMemory::AddPtr p = memory.lookupForAdd(obj);
    1313           0 :     if ((*backref = p.found()))
    1314           0 :         return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value());
    1315           0 :     if (!memory.add(p, obj, memory.count())) {
    1316           0 :         ReportOutOfMemory(context());
    1317           0 :         return false;
    1318             :     }
    1319             : 
    1320           1 :     if (memory.count() == UINT32_MAX) {
    1321           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_NEED_DIET,
    1322           0 :                                   "object graph to serialize");
    1323           0 :         return false;
    1324             :     }
    1325             : 
    1326             :     return true;
    1327             : }
    1328             : 
    1329             : bool
    1330        5289 : JSStructuredCloneWriter::traverseObject(HandleObject obj)
    1331             : {
    1332             :     // Get enumerable property ids and put them in reverse order so that they
    1333             :     // will come off the stack in forward order.
    1334           0 :     AutoIdVector properties(context());
    1335        5289 :     if (!GetPropertyKeys(context(), obj, JSITER_OWNONLY, &properties))
    1336             :         return false;
    1337             : 
    1338           0 :     for (size_t i = properties.length(); i > 0; --i) {
    1339           0 :         MOZ_ASSERT(JSID_IS_STRING(properties[i - 1]) || JSID_IS_INT(properties[i - 1]));
    1340           0 :         RootedValue val(context(), IdToValue(properties[i - 1]));
    1341           1 :         if (!entries.append(val))
    1342           0 :             return false;
    1343             :     }
    1344             : 
    1345             :     // Push obj and count to the stack.
    1346       26445 :     if (!objs.append(ObjectValue(*obj)) || !counts.append(properties.length()))
    1347             :         return false;
    1348             : 
    1349        5289 :     checkStack();
    1350             : 
    1351             :     // Write the header for obj.
    1352             :     ESClass cls;
    1353        5289 :     if (!GetBuiltinClass(context(), obj, &cls))
    1354             :         return false;
    1355       10578 :     return out.writePair(cls == ESClass::Array ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
    1356             : }
    1357             : 
    1358             : bool
    1359          65 : JSStructuredCloneWriter::traverseMap(HandleObject obj)
    1360             : {
    1361         390 :     Rooted<GCVector<Value>> newEntries(context(), GCVector<Value>(context()));
    1362             :     {
    1363             :         // If there is no wrapper, the compartment munging is a no-op.
    1364           0 :         RootedObject unwrapped(context(), CheckedUnwrap(obj));
    1365           0 :         MOZ_ASSERT(unwrapped);
    1366           0 :         JSAutoRealm ar(context(), unwrapped);
    1367           1 :         if (!MapObject::getKeysAndValuesInterleaved(unwrapped, &newEntries))
    1368           0 :             return false;
    1369             :     }
    1370         260 :     if (!context()->compartment()->wrap(context(), &newEntries))
    1371             :         return false;
    1372             : 
    1373           0 :     for (size_t i = newEntries.length(); i > 0; --i) {
    1374        2706 :         if (!entries.append(newEntries[i - 1]))
    1375             :             return false;
    1376             :     }
    1377             : 
    1378             :     // Push obj and count to the stack.
    1379         325 :     if (!objs.append(ObjectValue(*obj)) || !counts.append(newEntries.length()))
    1380             :         return false;
    1381             : 
    1382          65 :     checkStack();
    1383             : 
    1384             :     // Write the header for obj.
    1385         130 :     return out.writePair(SCTAG_MAP_OBJECT, 0);
    1386             : }
    1387             : 
    1388             : bool
    1389          56 : JSStructuredCloneWriter::traverseSet(HandleObject obj)
    1390             : {
    1391         336 :     Rooted<GCVector<Value>> keys(context(), GCVector<Value>(context()));
    1392             :     {
    1393             :         // If there is no wrapper, the compartment munging is a no-op.
    1394           0 :         RootedObject unwrapped(context(), CheckedUnwrap(obj));
    1395           0 :         MOZ_ASSERT(unwrapped);
    1396           0 :         JSAutoRealm ar(context(), unwrapped);
    1397           1 :         if (!SetObject::keys(context(), unwrapped, &keys))
    1398           0 :             return false;
    1399             :     }
    1400         224 :     if (!context()->compartment()->wrap(context(), &keys))
    1401             :         return false;
    1402             : 
    1403           0 :     for (size_t i = keys.length(); i > 0; --i) {
    1404         234 :         if (!entries.append(keys[i - 1]))
    1405             :             return false;
    1406             :     }
    1407             : 
    1408             :     // Push obj and count to the stack.
    1409         280 :     if (!objs.append(ObjectValue(*obj)) || !counts.append(keys.length()))
    1410             :         return false;
    1411             : 
    1412          56 :     checkStack();
    1413             : 
    1414             :     // Write the header for obj.
    1415         112 :     return out.writePair(SCTAG_SET_OBJECT, 0);
    1416             : }
    1417             : 
    1418             : // Objects are written as a "preorder" traversal of the object graph: object
    1419             : // "headers" (the class tag and any data needed for initial construction) are
    1420             : // visited first, then the children are recursed through (where children are
    1421             : // properties, Set or Map entries, etc.). So for example
    1422             : //
    1423             : //     m = new Map();
    1424             : //     m.set(key1 = {}, value1 = {})
    1425             : //
    1426             : // would be stored as
    1427             : //
    1428             : //     <Map tag>
    1429             : //     <key1 class tag>
    1430             : //     <value1 class tag>
    1431             : //     <end-of-children marker for key1>
    1432             : //     <end-of-children marker for value1>
    1433             : //     <end-of-children marker for Map>
    1434             : //
    1435             : // Notice how the end-of-children marker for key1 is sandwiched between the
    1436             : // value1 beginning and end.
    1437             : bool
    1438           0 : JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj)
    1439             : {
    1440           0 :     RootedObject unwrapped(context(), js::CheckedUnwrap(obj));
    1441           0 :     MOZ_ASSERT(unwrapped && unwrapped->is<SavedFrame>());
    1442             : 
    1443           0 :     RootedSavedFrame savedFrame(context(), &unwrapped->as<SavedFrame>());
    1444             : 
    1445           0 :     RootedObject parent(context(), savedFrame->getParent());
    1446           0 :     if (!context()->compartment()->wrap(context(), &parent))
    1447             :         return false;
    1448             : 
    1449           0 :     if (!objs.append(ObjectValue(*obj)) ||
    1450           0 :         !entries.append(parent ? ObjectValue(*parent) : NullValue()) ||
    1451           0 :         !counts.append(1))
    1452             :     {
    1453             :         return false;
    1454             :     }
    1455             : 
    1456           0 :     checkStack();
    1457             : 
    1458             :     // Write the SavedFrame tag and the SavedFrame's principals.
    1459             : 
    1460           0 :     if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsSystem) {
    1461           0 :         if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
    1462             :                            SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM))
    1463             :         {
    1464             :             return false;
    1465             :         };
    1466           0 :     } else if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsNotSystem) {
    1467           0 :         if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
    1468             :                            SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM))
    1469             :         {
    1470             :             return false;
    1471             :         }
    1472             :     } else {
    1473           0 :         if (auto principals = savedFrame->getPrincipals()) {
    1474           0 :             if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) ||
    1475           0 :                 !principals->write(context(), this))
    1476             :             {
    1477             :                 return false;
    1478             :             }
    1479             :         } else {
    1480           0 :             if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS))
    1481             :                 return false;
    1482             :         }
    1483             :     }
    1484             : 
    1485             :     // Write the SavedFrame's reserved slots, except for the parent, which is
    1486             :     // queued on objs for further traversal.
    1487             : 
    1488           0 :     RootedValue val(context());
    1489             : 
    1490           0 :     context()->markAtom(savedFrame->getSource());
    1491           0 :     val = StringValue(savedFrame->getSource());
    1492           0 :     if (!startWrite(val))
    1493             :         return false;
    1494             : 
    1495           0 :     val = NumberValue(savedFrame->getLine());
    1496           0 :     if (!startWrite(val))
    1497             :         return false;
    1498             : 
    1499           0 :     val = NumberValue(savedFrame->getColumn());
    1500           0 :     if (!startWrite(val))
    1501             :         return false;
    1502             : 
    1503           0 :     auto name = savedFrame->getFunctionDisplayName();
    1504           0 :     if (name)
    1505           0 :         context()->markAtom(name);
    1506           0 :     val = name ? StringValue(name) : NullValue();
    1507           0 :     if (!startWrite(val))
    1508             :         return false;
    1509             : 
    1510           0 :     auto cause = savedFrame->getAsyncCause();
    1511           0 :     if (cause)
    1512           0 :         context()->markAtom(cause);
    1513           0 :     val = cause ? StringValue(cause) : NullValue();
    1514           0 :     if (!startWrite(val))
    1515             :         return false;
    1516             : 
    1517           0 :     return true;
    1518             : }
    1519             : 
    1520             : bool
    1521       27699 : JSStructuredCloneWriter::startWrite(HandleValue v)
    1522             : {
    1523       27699 :     assertSameCompartment(context(), v);
    1524             : 
    1525           0 :     if (v.isString()) {
    1526           0 :         return writeString(SCTAG_STRING, v.toString());
    1527           0 :     } else if (v.isInt32()) {
    1528           0 :         return out.writePair(SCTAG_INT32, v.toInt32());
    1529           0 :     } else if (v.isDouble()) {
    1530           0 :         return out.writeDouble(v.toDouble());
    1531           0 :     } else if (v.isBoolean()) {
    1532           0 :         return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
    1533           0 :     } else if (v.isNull()) {
    1534           0 :         return out.writePair(SCTAG_NULL, 0);
    1535           0 :     } else if (v.isUndefined()) {
    1536           0 :         return out.writePair(SCTAG_UNDEFINED, 0);
    1537           0 :     } else if (v.isObject()) {
    1538       10984 :         RootedObject obj(context(), &v.toObject());
    1539             : 
    1540             :         bool backref;
    1541           0 :         if (!startObject(obj, &backref))
    1542           0 :             return false;
    1543        5492 :         if (backref)
    1544             :             return true;
    1545             : 
    1546             :         ESClass cls;
    1547       10962 :         if (!GetBuiltinClass(context(), obj, &cls))
    1548             :             return false;
    1549             : 
    1550           0 :         if (cls == ESClass::RegExp) {
    1551           0 :             RegExpShared* re = RegExpToShared(context(), obj);
    1552           0 :             if (!re)
    1553             :                 return false;
    1554           0 :             return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
    1555           0 :                    writeString(SCTAG_STRING, re->getSource());
    1556           0 :         } else if (cls == ESClass::Date) {
    1557           0 :             RootedValue unboxed(context());
    1558         144 :             if (!Unbox(context(), obj, &unboxed))
    1559             :                 return false;
    1560           0 :             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
    1561           0 :         } else if (JS_IsTypedArrayObject(obj)) {
    1562           0 :             return writeTypedArray(obj);
    1563           0 :         } else if (JS_IsDataViewObject(obj)) {
    1564           0 :             return writeDataView(obj);
    1565           0 :         } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
    1566           0 :             return writeArrayBuffer(obj);
    1567           0 :         } else if (JS_IsSharedArrayBufferObject(obj)) {
    1568           0 :             return writeSharedArrayBuffer(obj);
    1569           0 :         } else if (wasm::IsSharedWasmMemoryObject(obj)) {
    1570           0 :             return writeSharedWasmMemory(obj);
    1571           0 :         } else if (cls == ESClass::Object) {
    1572           0 :             return traverseObject(obj);
    1573           0 :         } else if (cls == ESClass::Array) {
    1574           0 :             return traverseObject(obj);
    1575           0 :         } else if (cls == ESClass::Boolean) {
    1576           0 :             RootedValue unboxed(context());
    1577           0 :             if (!Unbox(context(), obj, &unboxed))
    1578             :                 return false;
    1579           0 :             return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
    1580           0 :         } else if (cls == ESClass::Number) {
    1581           0 :             RootedValue unboxed(context());
    1582           0 :             if (!Unbox(context(), obj, &unboxed))
    1583             :                 return false;
    1584           0 :             return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
    1585           0 :         } else if (cls == ESClass::String) {
    1586           0 :             RootedValue unboxed(context());
    1587           0 :             if (!Unbox(context(), obj, &unboxed))
    1588             :                 return false;
    1589           0 :             return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
    1590           0 :         } else if (cls == ESClass::Map) {
    1591           0 :             return traverseMap(obj);
    1592           0 :         } else if (cls == ESClass::Set) {
    1593           0 :             return traverseSet(obj);
    1594           0 :         } else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
    1595           0 :             return traverseSavedFrame(obj);
    1596             :         }
    1597             : 
    1598           0 :         if (out.buf.callbacks_ && out.buf.callbacks_->write)
    1599          66 :             return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_);
    1600             :         // else fall through
    1601             :     }
    1602             : 
    1603           0 :     return reportDataCloneError(JS_SCERR_UNSUPPORTED_TYPE);
    1604             : }
    1605             : 
    1606             : bool
    1607           0 : JSStructuredCloneWriter::writeHeader()
    1608             : {
    1609         702 :     return out.writePair(SCTAG_HEADER, (uint32_t)output().scope());
    1610             : }
    1611             : 
    1612             : bool
    1613         234 : JSStructuredCloneWriter::writeTransferMap()
    1614             : {
    1615         468 :     if (transferableObjects.empty())
    1616             :         return true;
    1617             : 
    1618           2 :     if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
    1619             :         return false;
    1620             : 
    1621           2 :     if (!out.write(transferableObjects.count()))
    1622             :         return false;
    1623             : 
    1624           0 :     RootedObject obj(context());
    1625           0 :     for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
    1626           0 :         obj = tr.front();
    1627           1 :         if (!memory.put(obj, memory.count())) {
    1628           0 :             ReportOutOfMemory(context());
    1629           0 :             return false;
    1630             :         }
    1631             : 
    1632             :         // Emit a placeholder pointer.  We defer stealing the data until later
    1633             :         // (and, if necessary, detaching this object if it's an ArrayBuffer).
    1634           2 :         if (!out.writePair(SCTAG_TRANSFER_MAP_PENDING_ENTRY, JS::SCTAG_TMO_UNFILLED))
    1635             :             return false;
    1636           2 :         if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents.
    1637             :             return false;
    1638           1 :         if (!out.write(0)) // extraData
    1639             :             return false;
    1640             :     }
    1641             : 
    1642           1 :     return true;
    1643             : }
    1644             : 
    1645             : bool
    1646         234 : JSStructuredCloneWriter::transferOwnership()
    1647             : {
    1648         468 :     if (transferableObjects.empty())
    1649             :         return true;
    1650             : 
    1651             :     // Walk along the transferables and the transfer map at the same time,
    1652             :     // grabbing out pointers from the transferables and stuffing them into the
    1653             :     // transfer map.
    1654           0 :     auto point = out.iter();
    1655           0 :     MOZ_RELEASE_ASSERT(point.canPeek());
    1656           0 :     MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_HEADER);
    1657           0 :     point++;
    1658           0 :     MOZ_RELEASE_ASSERT(point.canPeek());
    1659           0 :     MOZ_ASSERT(uint32_t(NativeEndian::swapFromLittleEndian(point.peek()) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
    1660           0 :     point++;
    1661           0 :     MOZ_RELEASE_ASSERT(point.canPeek());
    1662           0 :     MOZ_ASSERT(NativeEndian::swapFromLittleEndian(point.peek()) == transferableObjects.count());
    1663           1 :     point++;
    1664             : 
    1665           0 :     JSContext* cx = context();
    1666           0 :     RootedObject obj(cx);
    1667           0 :     JS::StructuredCloneScope scope = output().scope();
    1668           0 :     for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
    1669           2 :         obj = tr.front();
    1670             : 
    1671             :         uint32_t tag;
    1672             :         JS::TransferableOwnership ownership;
    1673             :         void* content;
    1674             :         uint64_t extraData;
    1675             : 
    1676             : #if DEBUG
    1677           0 :         SCInput::getPair(point.peek(), &tag, (uint32_t*) &ownership);
    1678           0 :         MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
    1679           1 :         MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
    1680             : #endif
    1681             : 
    1682             :         ESClass cls;
    1683           0 :         if (!GetBuiltinClass(cx, obj, &cls))
    1684           0 :             return false;
    1685             : 
    1686           0 :         if (cls == ESClass::ArrayBuffer) {
    1687           1 :             tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
    1688             : 
    1689             :             // The current setup of the array buffer inheritance hierarchy doesn't
    1690             :             // lend itself well to generic manipulation via proxies.
    1691           0 :             Rooted<ArrayBufferObject*> arrayBuffer(cx, &CheckedUnwrap(obj)->as<ArrayBufferObject>());
    1692           3 :             JSAutoRealm ar(cx, arrayBuffer);
    1693             : 
    1694           0 :             if (arrayBuffer->isDetached()) {
    1695           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
    1696           0 :                 return false;
    1697             :             }
    1698             : 
    1699           1 :             size_t nbytes = arrayBuffer->byteLength();
    1700             : 
    1701           0 :             if (arrayBuffer->isWasm() || arrayBuffer->isPreparedForAsmJS()) {
    1702           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
    1703           0 :                 return false;
    1704             :             }
    1705             : 
    1706           1 :             if (arrayBuffer->isDetached()) {
    1707           0 :                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
    1708           0 :                 return false;
    1709             :             }
    1710             : 
    1711           1 :             if (scope == JS::StructuredCloneScope::DifferentProcess ||
    1712             :                 scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
    1713             :             {
    1714             :                 // Write Transferred ArrayBuffers in DifferentProcess scope at
    1715             :                 // the end of the clone buffer, and store the offset within the
    1716             :                 // buffer to where the ArrayBuffer was written. Note that this
    1717             :                 // will invalidate the current position iterator.
    1718             : 
    1719           0 :                 size_t pointOffset = out.offset(point);
    1720           0 :                 tag = SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER;
    1721           0 :                 ownership = JS::SCTAG_TMO_UNOWNED;
    1722           0 :                 content = nullptr;
    1723           0 :                 extraData = out.tell() - pointOffset; // Offset from tag to current end of buffer
    1724           0 :                 if (!writeArrayBuffer(arrayBuffer)) {
    1725           0 :                     ReportOutOfMemory(cx);
    1726           0 :                     return false;
    1727             :                 }
    1728             : 
    1729             :                 // Must refresh the point iterator after its collection has
    1730             :                 // been modified.
    1731           0 :                 point = out.iter();
    1732           0 :                 point += pointOffset;
    1733             : 
    1734           0 :                 if (!JS_DetachArrayBuffer(cx, arrayBuffer))
    1735             :                     return false;
    1736             :             } else {
    1737           1 :                 bool hasStealableContents = arrayBuffer->hasStealableContents();
    1738             : 
    1739             :                 ArrayBufferObject::BufferContents bufContents =
    1740           0 :                     ArrayBufferObject::stealContents(cx, arrayBuffer, hasStealableContents);
    1741           1 :                 if (!bufContents)
    1742           0 :                     return false; // out of memory
    1743             : 
    1744           0 :                 content = bufContents.data();
    1745           0 :                 if (bufContents.kind() == ArrayBufferObject::MAPPED)
    1746           0 :                     ownership = JS::SCTAG_TMO_MAPPED_DATA;
    1747             :                 else
    1748           0 :                     ownership = JS::SCTAG_TMO_ALLOC_DATA;
    1749           1 :                 extraData = nbytes;
    1750             :             }
    1751             :         } else {
    1752           0 :             if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer)
    1753           0 :                 return reportDataCloneError(JS_SCERR_TRANSFERABLE);
    1754           0 :             if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData))
    1755             :                 return false;
    1756           0 :             MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY);
    1757             :         }
    1758             : 
    1759           0 :         point.write(NativeEndian::swapToLittleEndian(PairToUInt64(tag, ownership)));
    1760           0 :         point.next();
    1761           0 :         point.write(NativeEndian::swapToLittleEndian(reinterpret_cast<uint64_t>(content)));
    1762           0 :         point.next();
    1763           0 :         point.write(NativeEndian::swapToLittleEndian(extraData));
    1764           1 :         point.next();
    1765             :     }
    1766             : 
    1767             : #if DEBUG
    1768             :     // Make sure there aren't any more transfer map entries after the expected
    1769             :     // number we read out.
    1770           1 :     if (!point.done()) {
    1771             :         uint32_t tag, data;
    1772           0 :         SCInput::getPair(point.peek(), &tag, &data);
    1773           1 :         MOZ_ASSERT(tag < SCTAG_TRANSFER_MAP_HEADER || tag >= SCTAG_TRANSFER_MAP_END_OF_BUILTIN_TYPES);
    1774             :     }
    1775             : #endif
    1776             :     return true;
    1777             : }
    1778             : 
    1779             : bool
    1780         234 : JSStructuredCloneWriter::write(HandleValue v)
    1781             : {
    1782         234 :     if (!startWrite(v))
    1783             :         return false;
    1784             : 
    1785           0 :     while (!counts.empty()) {
    1786           0 :         RootedObject obj(context(), &objs.back().toObject());
    1787           0 :         AutoRealm ar(context(), obj);
    1788           0 :         if (counts.back()) {
    1789           0 :             counts.back()--;
    1790           0 :             RootedValue key(context(), entries.back());
    1791           0 :             entries.popBack();
    1792       13771 :             checkStack();
    1793             : 
    1794             :             ESClass cls;
    1795           0 :             if (!GetBuiltinClass(context(), obj, &cls))
    1796           0 :                 return false;
    1797             : 
    1798           0 :             if (cls == ESClass::Map) {
    1799           0 :                 counts.back()--;
    1800           0 :                 RootedValue val(context(), entries.back());
    1801           0 :                 entries.popBack();
    1802         451 :                 checkStack();
    1803             : 
    1804           1 :                 if (!startWrite(key) || !startWrite(val))
    1805           0 :                     return false;
    1806           0 :             } else if (cls == ESClass::Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
    1807          78 :                 if (!startWrite(key))
    1808             :                     return false;
    1809             :             } else {
    1810           0 :                 RootedId id(context());
    1811           0 :                 if (!ValueToId<CanGC>(context(), key, &id))
    1812           0 :                   return false;
    1813       31666 :                 MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
    1814             : 
    1815             :                 // If obj still has an own property named id, write it out.
    1816             :                 // The cost of re-checking could be avoided by using
    1817             :                 // NativeIterators.
    1818             :                 bool found;
    1819       39726 :                 if (!HasOwnProperty(context(), obj, id, &found))
    1820             :                     return false;
    1821             : 
    1822           0 :                 if (found) {
    1823           0 :                     RootedValue val(context());
    1824           0 :                     if (!startWrite(key) ||
    1825           0 :                         !GetProperty(context(), obj, obj, id, &val) ||
    1826       13242 :                         !startWrite(val))
    1827             :                     {
    1828           0 :                         return false;
    1829             :                     }
    1830             :                 }
    1831             :             }
    1832             :         } else {
    1833       10820 :             if (!out.writePair(SCTAG_END_OF_KEYS, 0))
    1834             :                 return false;
    1835           0 :             objs.popBack();
    1836        5410 :             counts.popBack();
    1837             :         }
    1838             :     }
    1839             : 
    1840           0 :     memory.clear();
    1841         234 :     return transferOwnership();
    1842             : }
    1843             : 
    1844             : bool
    1845          97 : JSStructuredCloneReader::checkDouble(double d)
    1846             : {
    1847           1 :     if (!JS::IsCanonicalized(d)) {
    1848           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    1849           0 :                                   "unrecognized NaN");
    1850           0 :         return false;
    1851             :     }
    1852             :     return true;
    1853             : }
    1854             : 
    1855             : namespace {
    1856             : 
    1857             : template <typename CharT>
    1858             : class Chars {
    1859             :     JSContext* cx;
    1860             :     CharT* p;
    1861             :   public:
    1862           0 :     explicit Chars(JSContext* cx) : cx(cx), p(nullptr) {}
    1863       32504 :     ~Chars() { js_free(p); }
    1864             : 
    1865           0 :     bool allocate(size_t len) {
    1866       16252 :         MOZ_ASSERT(!p);
    1867             :         // We're going to null-terminate!
    1868           0 :         p = cx->pod_malloc<CharT>(len + 1);
    1869           0 :         if (p) {
    1870           0 :             p[len] = CharT(0);
    1871       16252 :             return true;
    1872             :         }
    1873             :         return false;
    1874             :     }
    1875             :     CharT* get() { return p; }
    1876       16252 :     void forget() { p = nullptr; }
    1877             : };
    1878             : 
    1879             : } // anonymous namespace
    1880             : 
    1881             : template <typename CharT>
    1882             : JSString*
    1883       16252 : JSStructuredCloneReader::readStringImpl(uint32_t nchars)
    1884             : {
    1885           1 :     if (nchars > JSString::MAX_LENGTH) {
    1886           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    1887             :                                   "string length");
    1888           0 :         return nullptr;
    1889             :     }
    1890           0 :     Chars<CharT> chars(context());
    1891       32504 :     if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
    1892             :         return nullptr;
    1893           0 :     JSString* str = NewString<CanGC>(context(), chars.get(), nchars);
    1894           0 :     if (str)
    1895       16252 :         chars.forget();
    1896             :     return str;
    1897             : }
    1898             : 
    1899             : JSString*
    1900       16252 : JSStructuredCloneReader::readString(uint32_t data)
    1901             : {
    1902           0 :     uint32_t nchars = data & JS_BITMASK(31);
    1903           0 :     bool latin1 = data & (1 << 31);
    1904       16252 :     return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<char16_t>(nchars);
    1905             : }
    1906             : 
    1907             : static uint32_t
    1908           0 : TagToV1ArrayType(uint32_t tag)
    1909             : {
    1910           0 :     MOZ_ASSERT(tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX);
    1911           0 :     return tag - SCTAG_TYPED_ARRAY_V1_MIN;
    1912             : }
    1913             : 
    1914             : bool
    1915           1 : JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
    1916             :                                         bool v1Read)
    1917             : {
    1918           1 :     if (arrayType > Scalar::Uint8Clamped) {
    1919           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    1920           0 :                                   "unhandled typed array element type");
    1921           0 :         return false;
    1922             :     }
    1923             : 
    1924             :     // Push a placeholder onto the allObjs list to stand in for the typed array.
    1925           0 :     uint32_t placeholderIndex = allObjs.length();
    1926           0 :     Value dummy = UndefinedValue();
    1927           2 :     if (!allObjs.append(dummy))
    1928             :         return false;
    1929             : 
    1930             :     // Read the ArrayBuffer object and its contents (but no properties)
    1931           4 :     RootedValue v(context());
    1932             :     uint32_t byteOffset;
    1933           1 :     if (v1Read) {
    1934           0 :         if (!readV1ArrayBuffer(arrayType, nelems, &v))
    1935             :             return false;
    1936             :         byteOffset = 0;
    1937             :     } else {
    1938           1 :         if (!startRead(&v))
    1939           0 :             return false;
    1940             :         uint64_t n;
    1941           1 :         if (!in.read(&n))
    1942             :             return false;
    1943           1 :         byteOffset = n;
    1944             :     }
    1945           1 :     if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
    1946           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    1947           0 :                                   "typed array must be backed by an ArrayBuffer");
    1948           0 :         return false;
    1949             :     }
    1950             : 
    1951           0 :     RootedObject buffer(context(), &v.toObject());
    1952           4 :     RootedObject obj(context(), nullptr);
    1953             : 
    1954           1 :     switch (arrayType) {
    1955             :       case Scalar::Int8:
    1956           0 :         obj = JS_NewInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1957           0 :         break;
    1958             :       case Scalar::Uint8:
    1959           0 :         obj = JS_NewUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1960           1 :         break;
    1961             :       case Scalar::Int16:
    1962           0 :         obj = JS_NewInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1963           0 :         break;
    1964             :       case Scalar::Uint16:
    1965           0 :         obj = JS_NewUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1966           0 :         break;
    1967             :       case Scalar::Int32:
    1968           0 :         obj = JS_NewInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1969           0 :         break;
    1970             :       case Scalar::Uint32:
    1971           0 :         obj = JS_NewUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1972           0 :         break;
    1973             :       case Scalar::Float32:
    1974           0 :         obj = JS_NewFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1975           0 :         break;
    1976             :       case Scalar::Float64:
    1977           0 :         obj = JS_NewFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1978           0 :         break;
    1979             :       case Scalar::Uint8Clamped:
    1980           0 :         obj = JS_NewUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
    1981           0 :         break;
    1982             :       default:
    1983           0 :         MOZ_CRASH("Can't happen: arrayType range checked above");
    1984             :     }
    1985             : 
    1986           1 :     if (!obj)
    1987             :         return false;
    1988           2 :     vp.setObject(*obj);
    1989             : 
    1990           2 :     allObjs[placeholderIndex].set(vp);
    1991             : 
    1992           1 :     return true;
    1993             : }
    1994             : 
    1995             : bool
    1996           0 : JSStructuredCloneReader::readDataView(uint32_t byteLength, MutableHandleValue vp)
    1997             : {
    1998             :     // Push a placeholder onto the allObjs list to stand in for the DataView.
    1999           0 :     uint32_t placeholderIndex = allObjs.length();
    2000           0 :     Value dummy = UndefinedValue();
    2001           0 :     if (!allObjs.append(dummy))
    2002             :         return false;
    2003             : 
    2004             :     // Read the ArrayBuffer object and its contents (but no properties).
    2005           0 :     RootedValue v(context());
    2006           0 :     if (!startRead(&v))
    2007             :         return false;
    2008           0 :     if (!v.isObject() || !v.toObject().is<ArrayBufferObjectMaybeShared>()) {
    2009           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2010           0 :                                   "DataView must be backed by an ArrayBuffer");
    2011           0 :         return false;
    2012             :     }
    2013             : 
    2014             :     // Read byteOffset.
    2015             :     uint64_t n;
    2016           0 :     if (!in.read(&n))
    2017             :         return false;
    2018           0 :     uint32_t byteOffset = n;
    2019             : 
    2020           0 :     RootedObject buffer(context(), &v.toObject());
    2021           0 :     RootedObject obj(context(), JS_NewDataView(context(), buffer, byteOffset, byteLength));
    2022           0 :     if (!obj)
    2023             :         return false;
    2024           0 :     vp.setObject(*obj);
    2025             : 
    2026           0 :     allObjs[placeholderIndex].set(vp);
    2027             : 
    2028           0 :     return true;
    2029             : }
    2030             : 
    2031             : bool
    2032           0 : JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, MutableHandleValue vp)
    2033             : {
    2034           0 :     JSObject* obj = ArrayBufferObject::create(context(), nbytes);
    2035           0 :     if (!obj)
    2036             :         return false;
    2037           0 :     vp.setObject(*obj);
    2038           0 :     ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
    2039           0 :     MOZ_ASSERT(buffer.byteLength() == nbytes);
    2040           0 :     return in.readArray(buffer.dataPointer(), nbytes);
    2041             : }
    2042             : 
    2043             : bool
    2044           0 : JSStructuredCloneReader::readSharedArrayBuffer(MutableHandleValue vp)
    2045             : {
    2046             :     uint32_t byteLength;
    2047           0 :     if (!in.readBytes(&byteLength, sizeof(byteLength)))
    2048           0 :         return in.reportTruncated();
    2049             : 
    2050             :     intptr_t p;
    2051           0 :     if (!in.readBytes(&p, sizeof(p)))
    2052           0 :         return in.reportTruncated();
    2053             : 
    2054           0 :     SharedArrayRawBuffer* rawbuf = reinterpret_cast<SharedArrayRawBuffer*>(p);
    2055             : 
    2056             :     // There's no guarantee that the receiving agent has enabled shared memory
    2057             :     // even if the transmitting agent has done so.  Ideally we'd check at the
    2058             :     // transmission point, but that's tricky, and it will be a very rare problem
    2059             :     // in any case.  Just fail at the receiving end if we can't handle it.
    2060             : 
    2061           0 :     if (!context()->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
    2062           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_DISABLED);
    2063           0 :         return false;
    2064             :     }
    2065             : 
    2066             :     // The new object will have a new reference to the rawbuf.
    2067             : 
    2068           0 :     if (!rawbuf->addReference()) {
    2069           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
    2070           0 :         return false;
    2071             :     }
    2072             : 
    2073           0 :     JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf, byteLength);
    2074           0 :     if (!obj) {
    2075           0 :         rawbuf->dropReference();
    2076           0 :         return false;
    2077             :     }
    2078             : 
    2079           0 :     vp.setObject(*obj);
    2080           0 :     return true;
    2081             : }
    2082             : 
    2083             : bool
    2084           0 : JSStructuredCloneReader::readSharedWasmMemory(uint32_t nbytes, MutableHandleValue vp)
    2085             : {
    2086           0 :     if (nbytes != 0) {
    2087           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2088           0 :                                   "invalid shared wasm memory tag");
    2089           0 :         return false;
    2090             :     }
    2091             : 
    2092           0 :     JSContext* cx = context();
    2093             : 
    2094             :     // Read the SharedArrayBuffer object.
    2095           0 :     RootedValue payload(cx);
    2096           0 :     if (!startRead(&payload))
    2097             :         return false;
    2098           0 :     if (!payload.isObject() || !payload.toObject().is<SharedArrayBufferObject>()) {
    2099           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2100           0 :                                   "shared wasm memory must be backed by a SharedArrayBuffer");
    2101           0 :         return false;
    2102             :     }
    2103             : 
    2104             :     Rooted<ArrayBufferObjectMaybeShared*> sab(
    2105           0 :         cx, &payload.toObject().as<SharedArrayBufferObject>());
    2106             : 
    2107             :     // Construct the memory.
    2108           0 :     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
    2109           0 :     RootedObject memory(cx, WasmMemoryObject::create(cx, sab, proto));
    2110           0 :     if (!memory)
    2111             :         return false;
    2112             : 
    2113           0 :     vp.setObject(*memory);
    2114           0 :     return true;
    2115             : }
    2116             : 
    2117             : /*
    2118             :  * Read in the data for a structured clone version 1 ArrayBuffer, performing
    2119             :  * endianness-conversion while reading.
    2120             :  */
    2121             : bool
    2122           0 : JSStructuredCloneReader::readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems,
    2123             :                                            MutableHandleValue vp)
    2124             : {
    2125           0 :     if (arrayType > Scalar::Uint8Clamped) {
    2126           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2127           0 :                                   "invalid TypedArray type");
    2128           0 :         return false;
    2129             :     }
    2130             : 
    2131             :     mozilla::CheckedInt<size_t> nbytes =
    2132           0 :         mozilla::CheckedInt<size_t>(nelems) *
    2133           0 :         TypedArrayElemSize(static_cast<Scalar::Type>(arrayType));
    2134           0 :     if (!nbytes.isValid() || nbytes.value() > UINT32_MAX) {
    2135           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2136             :                                   JSMSG_SC_BAD_SERIALIZED_DATA,
    2137           0 :                                   "invalid typed array size");
    2138           0 :         return false;
    2139             :     }
    2140             : 
    2141           0 :     JSObject* obj = ArrayBufferObject::create(context(), nbytes.value());
    2142           0 :     if (!obj)
    2143             :         return false;
    2144           0 :     vp.setObject(*obj);
    2145           0 :     ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
    2146           0 :     MOZ_ASSERT(buffer.byteLength() == nbytes);
    2147             : 
    2148           0 :     switch (arrayType) {
    2149             :       case Scalar::Int8:
    2150             :       case Scalar::Uint8:
    2151             :       case Scalar::Uint8Clamped:
    2152           0 :         return in.readArray((uint8_t*) buffer.dataPointer(), nelems);
    2153             :       case Scalar::Int16:
    2154             :       case Scalar::Uint16:
    2155           0 :         return in.readArray((uint16_t*) buffer.dataPointer(), nelems);
    2156             :       case Scalar::Int32:
    2157             :       case Scalar::Uint32:
    2158             :       case Scalar::Float32:
    2159           0 :         return in.readArray((uint32_t*) buffer.dataPointer(), nelems);
    2160             :       case Scalar::Float64:
    2161           0 :         return in.readArray((uint64_t*) buffer.dataPointer(), nelems);
    2162             :       default:
    2163           0 :         MOZ_CRASH("Can't happen: arrayType range checked by caller");
    2164             :     }
    2165             : }
    2166             : 
    2167             : static bool
    2168           0 : PrimitiveToObject(JSContext* cx, MutableHandleValue vp)
    2169             : {
    2170           0 :     JSObject* obj = js::PrimitiveToObject(cx, vp);
    2171           0 :     if (!obj)
    2172             :         return false;
    2173             : 
    2174           0 :     vp.setObject(*obj);
    2175           0 :     return true;
    2176             : }
    2177             : 
    2178             : bool
    2179       25727 : JSStructuredCloneReader::startRead(MutableHandleValue vp)
    2180             : {
    2181             :     uint32_t tag, data;
    2182             : 
    2183       51454 :     if (!in.readPair(&tag, &data))
    2184             :         return false;
    2185             : 
    2186       25727 :     switch (tag) {
    2187             :       case SCTAG_NULL:
    2188             :         vp.setNull();
    2189             :         break;
    2190             : 
    2191             :       case SCTAG_UNDEFINED:
    2192             :         vp.setUndefined();
    2193             :         break;
    2194             : 
    2195             :       case SCTAG_INT32:
    2196        2781 :         vp.setInt32(data);
    2197             :         break;
    2198             : 
    2199             :       case SCTAG_BOOLEAN:
    2200             :       case SCTAG_BOOLEAN_OBJECT:
    2201           0 :         vp.setBoolean(!!data);
    2202        1376 :         if (tag == SCTAG_BOOLEAN_OBJECT && !PrimitiveToObject(context(), vp))
    2203             :             return false;
    2204             :         break;
    2205             : 
    2206             :       case SCTAG_STRING:
    2207             :       case SCTAG_STRING_OBJECT: {
    2208           0 :         JSString* str = readString(data);
    2209       16252 :         if (!str)
    2210             :             return false;
    2211           0 :         vp.setString(str);
    2212       16252 :         if (tag == SCTAG_STRING_OBJECT && !PrimitiveToObject(context(), vp))
    2213             :             return false;
    2214             :         break;
    2215             :       }
    2216             : 
    2217             :       case SCTAG_NUMBER_OBJECT: {
    2218             :         double d;
    2219           0 :         if (!in.readDouble(&d) || !checkDouble(d))
    2220           0 :             return false;
    2221           0 :         vp.setDouble(d);
    2222           0 :         if (!PrimitiveToObject(context(), vp))
    2223             :             return false;
    2224           0 :         break;
    2225             :       }
    2226             : 
    2227             :       case SCTAG_DATE_OBJECT: {
    2228             :         double d;
    2229           1 :         if (!in.readDouble(&d) || !checkDouble(d))
    2230           0 :             return false;
    2231           0 :         JS::ClippedTime t = JS::TimeClip(d);
    2232           1 :         if (!NumbersAreIdentical(d, t.toDouble())) {
    2233           0 :             JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2234             :                                       JSMSG_SC_BAD_SERIALIZED_DATA,
    2235           0 :                                       "date");
    2236           0 :             return false;
    2237             :         }
    2238           0 :         JSObject* obj = NewDateObjectMsec(context(), t);
    2239          48 :         if (!obj)
    2240             :             return false;
    2241           0 :         vp.setObject(*obj);
    2242          48 :         break;
    2243             :       }
    2244             : 
    2245             :       case SCTAG_REGEXP_OBJECT: {
    2246           0 :         RegExpFlag flags = RegExpFlag(data);
    2247             :         uint32_t tag2, stringData;
    2248           0 :         if (!in.readPair(&tag2, &stringData))
    2249           0 :             return false;
    2250           0 :         if (tag2 != SCTAG_STRING) {
    2251           0 :             JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2252             :                                       JSMSG_SC_BAD_SERIALIZED_DATA,
    2253           0 :                                       "regexp");
    2254           0 :             return false;
    2255             :         }
    2256           0 :         JSString* str = readString(stringData);
    2257           0 :         if (!str)
    2258             :             return false;
    2259             : 
    2260           0 :         RootedAtom atom(context(), AtomizeString(context(), str));
    2261           0 :         if (!atom)
    2262           0 :             return false;
    2263             : 
    2264           0 :         RegExpObject* reobj = RegExpObject::create(context(), atom, flags,
    2265           0 :                                                    context()->tempLifoAlloc(), GenericObject);
    2266           0 :         if (!reobj)
    2267             :             return false;
    2268           0 :         vp.setObject(*reobj);
    2269           0 :         break;
    2270             :       }
    2271             : 
    2272             :       case SCTAG_ARRAY_OBJECT:
    2273             :       case SCTAG_OBJECT_OBJECT: {
    2274             :         JSObject* obj = (tag == SCTAG_ARRAY_OBJECT)
    2275           0 :                         ? (JSObject*) NewDenseEmptyArray(context())
    2276           0 :                         : (JSObject*) NewBuiltinClassInstance<PlainObject>(context());
    2277       14913 :         if (!obj || !objs.append(ObjectValue(*obj)))
    2278             :             return false;
    2279             :         vp.setObject(*obj);
    2280             :         break;
    2281             :       }
    2282             : 
    2283             :       case SCTAG_BACK_REFERENCE_OBJECT: {
    2284           1 :         if (data >= allObjs.length() || !allObjs[data].isObject()) {
    2285           0 :             JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2286             :                                       JSMSG_SC_BAD_SERIALIZED_DATA,
    2287           0 :                                       "invalid back reference in input");
    2288           0 :             return false;
    2289             :         }
    2290           0 :         vp.set(allObjs[data]);
    2291           9 :         return true;
    2292             :       }
    2293             : 
    2294             :       case SCTAG_TRANSFER_MAP_HEADER:
    2295             :       case SCTAG_TRANSFER_MAP_PENDING_ENTRY:
    2296             :         // We should be past all the transfer map tags.
    2297           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
    2298           0 :                                   "invalid input");
    2299           0 :         return false;
    2300             : 
    2301             :       case SCTAG_ARRAY_BUFFER_OBJECT:
    2302           0 :         if (!readArrayBuffer(data, vp))
    2303             :             return false;
    2304             :         break;
    2305             : 
    2306             :       case SCTAG_SHARED_ARRAY_BUFFER_OBJECT:
    2307           0 :         if (!readSharedArrayBuffer(vp))
    2308             :             return false;
    2309             :         break;
    2310             : 
    2311             :       case SCTAG_SHARED_WASM_MEMORY_OBJECT:
    2312           0 :         if (!readSharedWasmMemory(data, vp))
    2313             :             return false;
    2314             :         break;
    2315             : 
    2316             :       case SCTAG_TYPED_ARRAY_OBJECT: {
    2317             :         // readTypedArray adds the array to allObjs.
    2318             :         uint64_t arrayType;
    2319           1 :         if (!in.read(&arrayType))
    2320             :             return false;
    2321           1 :         return readTypedArray(arrayType, data, vp);
    2322             :       }
    2323             : 
    2324             :       case SCTAG_DATA_VIEW_OBJECT: {
    2325             :         // readDataView adds the array to allObjs.
    2326           0 :         return readDataView(data, vp);
    2327             :       }
    2328             : 
    2329             :       case SCTAG_MAP_OBJECT: {
    2330           0 :         JSObject* obj = MapObject::create(context());
    2331          27 :         if (!obj || !objs.append(ObjectValue(*obj)))
    2332             :             return false;
    2333             :         vp.setObject(*obj);
    2334             :         break;
    2335             :       }
    2336             : 
    2337             :       case SCTAG_SET_OBJECT: {
    2338           0 :         JSObject* obj = SetObject::create(context());
    2339          12 :         if (!obj || !objs.append(ObjectValue(*obj)))
    2340             :             return false;
    2341             :         vp.setObject(*obj);
    2342             :         break;
    2343             :       }
    2344             : 
    2345             :       case SCTAG_SAVED_FRAME_OBJECT: {
    2346           0 :         auto obj = readSavedFrame(data);
    2347           0 :         if (!obj || !objs.append(ObjectValue(*obj)))
    2348             :             return false;
    2349             :         vp.setObject(*obj);
    2350             :         break;
    2351             :       }
    2352             : 
    2353             :       default: {
    2354           0 :         if (tag <= SCTAG_FLOAT_MAX) {
    2355           0 :             double d = ReinterpretPairAsDouble(tag, data);
    2356          49 :             if (!checkDouble(d))
    2357             :                 return false;
    2358             :             vp.setNumber(d);
    2359             :             break;
    2360             :         }
    2361             : 
    2362          11 :         if (SCTAG_TYPED_ARRAY_V1_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
    2363             :             // A v1-format typed array
    2364             :             // readTypedArray adds the array to allObjs
    2365           0 :             return readTypedArray(TagToV1ArrayType(tag), data, vp, true);
    2366             :         }
    2367             : 
    2368           1 :         if (!callbacks || !callbacks->read) {
    2369           0 :             JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2370             :                                       JSMSG_SC_BAD_SERIALIZED_DATA,
    2371           0 :                                       "unsupported type");
    2372           0 :             return false;
    2373             :         }
    2374           0 :         JSObject* obj = callbacks->read(context(), this, tag, data, closure);
    2375          11 :         if (!obj)
    2376             :             return false;
    2377             :         vp.setObject(*obj);
    2378             :       }
    2379             :     }
    2380             : 
    2381       30760 :     if (vp.isObject() && !allObjs.append(vp))
    2382             :         return false;
    2383             : 
    2384       25717 :     return true;
    2385             : }
    2386             : 
    2387             : bool
    2388         223 : JSStructuredCloneReader::readHeader()
    2389             : {
    2390             :     uint32_t tag, data;
    2391           1 :     if (!in.getPair(&tag, &data))
    2392           0 :         return in.reportTruncated();
    2393             : 
    2394             :     JS::StructuredCloneScope storedScope;
    2395           0 :     if (tag == SCTAG_HEADER) {
    2396         446 :         MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
    2397             :         storedScope = JS::StructuredCloneScope(data);
    2398             :     } else {
    2399             :         // Old structured clone buffer. We must have read it from disk.
    2400             :         storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB;
    2401             :     }
    2402             : 
    2403         223 :     if (storedScope < JS::StructuredCloneScope::SameProcessSameThread ||
    2404             :         storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB)
    2405             :     {
    2406           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2407           0 :                                   "invalid structured clone scope");
    2408           0 :         return false;
    2409             :     }
    2410             : 
    2411         223 :     if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) {
    2412             :         // Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB
    2413             :         // clones are incorrect. Treat them as if they were DifferentProcess.
    2414           0 :         allowedScope = JS::StructuredCloneScope::DifferentProcess;
    2415           0 :         return true;
    2416             :     }
    2417             : 
    2418           1 :     if (storedScope < allowedScope) {
    2419           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2420             :                                   JSMSG_SC_BAD_SERIALIZED_DATA,
    2421           0 :                                   "incompatible structured clone scope");
    2422           0 :         return false;
    2423             :     }
    2424             : 
    2425             :     return true;
    2426             : }
    2427             : 
    2428             : bool
    2429         223 : JSStructuredCloneReader::readTransferMap()
    2430             : {
    2431           0 :     JSContext* cx = context();
    2432         223 :     auto headerPos = in.tell();
    2433             : 
    2434             :     uint32_t tag, data;
    2435           1 :     if (!in.getPair(&tag, &data))
    2436           0 :         return in.reportTruncated();
    2437             : 
    2438         223 :     if (tag != SCTAG_TRANSFER_MAP_HEADER || TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED)
    2439             :         return true;
    2440             : 
    2441             :     uint64_t numTransferables;
    2442           0 :     MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
    2443           1 :     if (!in.read(&numTransferables))
    2444             :         return false;
    2445             : 
    2446           0 :     for (uint64_t i = 0; i < numTransferables; i++) {
    2447           1 :         auto pos = in.tell();
    2448             : 
    2449           1 :         if (!in.readPair(&tag, &data))
    2450           0 :             return false;
    2451             : 
    2452           0 :         MOZ_ASSERT(tag != SCTAG_TRANSFER_MAP_PENDING_ENTRY);
    2453           2 :         RootedObject obj(cx);
    2454             : 
    2455             :         void* content;
    2456           1 :         if (!in.readPtr(&content))
    2457           0 :             return false;
    2458             : 
    2459             :         uint64_t extraData;
    2460           1 :         if (!in.read(&extraData))
    2461             :             return false;
    2462             : 
    2463           0 :         if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) {
    2464           1 :             if (allowedScope == JS::StructuredCloneScope::DifferentProcess ||
    2465             :                 allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB)
    2466             :             {
    2467             :                 // Transferred ArrayBuffers in a DifferentProcess clone buffer
    2468             :                 // are treated as if they weren't Transferred at all. We should
    2469             :                 // only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER.
    2470           0 :                 ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
    2471           0 :                 return false;
    2472             :             }
    2473             : 
    2474           0 :             size_t nbytes = extraData;
    2475           1 :             MOZ_ASSERT(data == JS::SCTAG_TMO_ALLOC_DATA ||
    2476             :                        data == JS::SCTAG_TMO_MAPPED_DATA);
    2477           0 :             if (data == JS::SCTAG_TMO_ALLOC_DATA)
    2478           0 :                 obj = JS_NewArrayBufferWithContents(cx, nbytes, content);
    2479           0 :             else if (data == JS::SCTAG_TMO_MAPPED_DATA)
    2480           0 :                 obj = JS_NewMappedArrayBufferWithContents(cx, nbytes, content);
    2481           0 :         } else if (tag == SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER) {
    2482           0 :             auto savedPos = in.tell();
    2483             :             auto guard = mozilla::MakeScopeExit([&] {
    2484           0 :                 in.seekTo(savedPos);
    2485           0 :             });
    2486           0 :             in.seekTo(pos);
    2487           0 :             in.seekBy(static_cast<size_t>(extraData));
    2488             : 
    2489             :             uint32_t tag, data;
    2490           0 :             if (!in.readPair(&tag, &data))
    2491           0 :                 return false;
    2492           0 :             MOZ_ASSERT(tag == SCTAG_ARRAY_BUFFER_OBJECT);
    2493           0 :             RootedValue val(cx);
    2494           0 :             if (!readArrayBuffer(data, &val))
    2495           0 :                 return false;
    2496           0 :             obj = &val.toObject();
    2497             :         } else {
    2498           0 :             if (!callbacks || !callbacks->readTransfer) {
    2499           0 :                 ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE);
    2500           0 :                 return false;
    2501             :             }
    2502           0 :             if (!callbacks->readTransfer(cx, this, tag, content, extraData, closure, &obj))
    2503             :                 return false;
    2504           0 :             MOZ_ASSERT(obj);
    2505           0 :             MOZ_ASSERT(!cx->isExceptionPending());
    2506             :         }
    2507             : 
    2508             :         // On failure, the buffer will still own the data (since its ownership
    2509             :         // will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
    2510             :         // DiscardTransferables.
    2511           1 :         if (!obj)
    2512             :             return false;
    2513             : 
    2514             :         // Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
    2515             :         // buffer.
    2516           0 :         pos.write(PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED));
    2517           1 :         MOZ_ASSERT(!pos.done());
    2518             : 
    2519           3 :         if (!allObjs.append(ObjectValue(*obj)))
    2520             :             return false;
    2521             :     }
    2522             : 
    2523             :     // Mark the whole transfer map as consumed.
    2524             : #ifdef DEBUG
    2525           0 :     SCInput::getPair(headerPos.peek(), &tag, &data);
    2526           0 :     MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
    2527           1 :     MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
    2528             : #endif
    2529           1 :     headerPos.write(PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED));
    2530             : 
    2531           1 :     return true;
    2532             : }
    2533             : 
    2534             : JSObject*
    2535           0 : JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag)
    2536             : {
    2537           0 :     RootedSavedFrame savedFrame(context(), SavedFrame::create(context()));
    2538           0 :     if (!savedFrame)
    2539             :         return nullptr;
    2540             : 
    2541             :     JSPrincipals* principals;
    2542           0 :     if (principalsTag == SCTAG_JSPRINCIPALS) {
    2543           0 :         if (!context()->runtime()->readPrincipals) {
    2544           0 :             JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2545           0 :                                       JSMSG_SC_UNSUPPORTED_TYPE);
    2546           0 :             return nullptr;
    2547             :         }
    2548             : 
    2549           0 :         if (!context()->runtime()->readPrincipals(context(), this, &principals))
    2550             :             return nullptr;
    2551           0 :     } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) {
    2552           0 :         principals = &ReconstructedSavedFramePrincipals::IsSystem;
    2553           0 :         principals->refcount++;
    2554           0 :     } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) {
    2555           0 :         principals = &ReconstructedSavedFramePrincipals::IsNotSystem;
    2556           0 :         principals->refcount++;
    2557           0 :     } else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) {
    2558           0 :         principals = nullptr;
    2559             :     } else {
    2560           0 :         JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA,
    2561           0 :                                   "bad SavedFrame principals");
    2562           0 :         return nullptr;
    2563             :     }
    2564           0 :     savedFrame->initPrincipalsAlreadyHeld(principals);
    2565             : 
    2566           0 :     RootedValue source(context());
    2567           0 :     if (!startRead(&source) || !source.isString())
    2568             :         return nullptr;
    2569           0 :     auto atomSource = AtomizeString(context(), source.toString());
    2570           0 :     if (!atomSource)
    2571             :         return nullptr;
    2572           0 :     savedFrame->initSource(atomSource);
    2573             : 
    2574           0 :     RootedValue lineVal(context());
    2575             :     uint32_t line;
    2576           0 :     if (!startRead(&lineVal) || !lineVal.isNumber() || !ToUint32(context(), lineVal, &line))
    2577             :         return nullptr;
    2578           0 :     savedFrame->initLine(line);
    2579             : 
    2580           0 :     RootedValue columnVal(context());
    2581             :     uint32_t column;
    2582           0 :     if (!startRead(&columnVal) || !columnVal.isNumber() || !ToUint32(context(), columnVal, &column))
    2583             :         return nullptr;
    2584           0 :     savedFrame->initColumn(column);
    2585             : 
    2586           0 :     RootedValue name(context());
    2587           0 :     if (!startRead(&name) || !(name.isString() || name.isNull()))
    2588             :         return nullptr;
    2589           0 :     JSAtom* atomName = nullptr;
    2590           0 :     if (name.isString()) {
    2591           0 :         atomName = AtomizeString(context(), name.toString());
    2592           0 :         if (!atomName)
    2593             :             return nullptr;
    2594             :     }
    2595             : 
    2596           0 :     savedFrame->initFunctionDisplayName(atomName);
    2597             : 
    2598           0 :     RootedValue cause(context());
    2599           0 :     if (!startRead(&cause) || !(cause.isString() || cause.isNull()))
    2600             :         return nullptr;
    2601           0 :     JSAtom* atomCause = nullptr;
    2602           0 :     if (cause.isString()) {
    2603           0 :         atomCause = AtomizeString(context(), cause.toString());
    2604           0 :         if (!atomCause)
    2605             :             return nullptr;
    2606             :     }
    2607           0 :     savedFrame->initAsyncCause(atomCause);
    2608             : 
    2609           0 :     return savedFrame;
    2610             : }
    2611             : 
    2612             : // Perform the whole recursive reading procedure.
    2613             : bool
    2614         223 : JSStructuredCloneReader::read(MutableHandleValue vp)
    2615             : {
    2616         223 :     if (!readHeader())
    2617             :         return false;
    2618             : 
    2619         223 :     if (!readTransferMap())
    2620             :         return false;
    2621             : 
    2622             :     // Start out by reading in the main object and pushing it onto the 'objs'
    2623             :     // stack. The data related to this object and its descendants extends from
    2624             :     // here to the SCTAG_END_OF_KEYS at the end of the stream.
    2625         223 :     if (!startRead(vp))
    2626             :         return false;
    2627             : 
    2628             :     // Stop when the stack shows that all objects have been read.
    2629       35930 :     while (objs.length() != 0) {
    2630             :         // What happens depends on the top obj on the objs stack.
    2631       65971 :         RootedObject obj(context(), &objs.back().toObject());
    2632             : 
    2633             :         uint32_t tag, data;
    2634           0 :         if (!in.getPair(&tag, &data))
    2635           0 :             return false;
    2636             : 
    2637       17742 :         if (tag == SCTAG_END_OF_KEYS) {
    2638             :             // Pop the current obj off the stack, since we are done with it and
    2639             :             // its children.
    2640           0 :             MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
    2641           0 :             objs.popBack();
    2642        4997 :             continue;
    2643             :         }
    2644             : 
    2645             :         // The input stream contains a sequence of "child" values, whose
    2646             :         // interpretation depends on the type of obj. These values can be
    2647             :         // anything, and startRead() will push onto 'objs' for any non-leaf
    2648             :         // value (i.e., anything that may contain children).
    2649             :         //
    2650             :         // startRead() will allocate the (empty) object, but note that when
    2651             :         // startRead() returns, 'key' is not yet initialized with any of its
    2652             :         // properties. Those will be filled in by returning to the head of this
    2653             :         // loop, processing the first child obj, and continuing until all
    2654             :         // children have been fully created.
    2655             :         //
    2656             :         // Note that this means the ordering in the stream is a little funky
    2657             :         // for things like Map. See the comment above startWrite() for an
    2658             :         // example.
    2659           0 :         RootedValue key(context());
    2660           0 :         if (!startRead(&key))
    2661           0 :             return false;
    2662             : 
    2663           0 :         if (key.isNull() &&
    2664           0 :             !(obj->is<MapObject>() || obj->is<SetObject>() || obj->is<SavedFrame>()))
    2665             :         {
    2666             :             // Backwards compatibility: Null formerly indicated the end of
    2667             :             // object properties.
    2668           0 :             objs.popBack();
    2669          13 :             continue;
    2670             :         }
    2671             : 
    2672             :         // Set object: the values between obj header (from startRead()) and
    2673             :         // SCTAG_END_OF_KEYS are all interpreted as values to add to the set.
    2674           0 :         if (obj->is<SetObject>()) {
    2675          39 :             if (!SetObject::add(context(), obj, key))
    2676             :                 return false;
    2677             :             continue;
    2678             :         }
    2679             : 
    2680             :         // SavedFrame object: there is one following value, the parent
    2681             :         // SavedFrame, which is either null or another SavedFrame object.
    2682       25490 :         if (obj->is<SavedFrame>()) {
    2683             :             SavedFrame* parentFrame;
    2684           0 :             if (key.isNull())
    2685             :                 parentFrame = nullptr;
    2686           0 :             else if (key.isObject() && key.toObject().is<SavedFrame>())
    2687           0 :                 parentFrame = &key.toObject().as<SavedFrame>();
    2688             :             else
    2689             :                 return false;
    2690             : 
    2691           0 :             obj->as<SavedFrame>().initParent(parentFrame);
    2692           0 :             continue;
    2693             :         }
    2694             : 
    2695             :         // Everything else uses a series of key,value,key,value,... Value
    2696             :         // objects.
    2697           0 :         RootedValue val(context());
    2698           0 :         if (!startRead(&val))
    2699           0 :             return false;
    2700             : 
    2701       25490 :         if (obj->is<MapObject>()) {
    2702             :             // For a Map, store those <key,value> pairs in the contained map
    2703             :             // data structure.
    2704         584 :             if (!MapObject::set(context(), obj, key, val))
    2705             :                 return false;
    2706             :         } else {
    2707             :             // For any other Object, interpret them as plain properties.
    2708       50396 :             RootedId id(context());
    2709             : 
    2710           1 :             if (!key.isString() && !key.isInt32()) {
    2711           0 :                 JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
    2712             :                                           JSMSG_SC_BAD_SERIALIZED_DATA,
    2713           0 :                                           "property key expected");
    2714           0 :                 return false;
    2715             :             }
    2716             : 
    2717       37797 :             if (!ValueToId<CanGC>(context(), key, &id))
    2718             :                 return false;
    2719             : 
    2720       50396 :             if (!DefineDataProperty(context(), obj, id, val))
    2721             :                 return false;
    2722             :          }
    2723             :     }
    2724             : 
    2725         446 :     allObjs.clear();
    2726             : 
    2727         223 :     return true;
    2728             : }
    2729             : 
    2730             : using namespace js;
    2731             : 
    2732             : JS_PUBLIC_API(bool)
    2733         223 : JS_ReadStructuredClone(JSContext* cx, JSStructuredCloneData& buf,
    2734             :                        uint32_t version, JS::StructuredCloneScope scope,
    2735             :                        MutableHandleValue vp,
    2736             :                        const JSStructuredCloneCallbacks* optionalCallbacks,
    2737             :                        void* closure)
    2738             : {
    2739           0 :     AssertHeapIsIdle();
    2740         446 :     CHECK_REQUEST(cx);
    2741             : 
    2742           1 :     if (version > JS_STRUCTURED_CLONE_VERSION) {
    2743           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_BAD_CLONE_VERSION);
    2744           0 :         return false;
    2745             :     }
    2746           0 :     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
    2747         223 :     return ReadStructuredClone(cx, buf, scope, vp, callbacks, closure);
    2748             : }
    2749             : 
    2750             : JS_PUBLIC_API(bool)
    2751         234 : JS_WriteStructuredClone(JSContext* cx, HandleValue value, JSStructuredCloneData* bufp,
    2752             :                         JS::StructuredCloneScope scope,
    2753             :                         JS::CloneDataPolicy cloneDataPolicy,
    2754             :                         const JSStructuredCloneCallbacks* optionalCallbacks,
    2755             :                         void* closure, HandleValue transferable)
    2756             : {
    2757           0 :     AssertHeapIsIdle();
    2758           0 :     CHECK_REQUEST(cx);
    2759         234 :     assertSameCompartment(cx, value);
    2760             : 
    2761           0 :     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
    2762           0 :     return WriteStructuredClone(cx, value, bufp, scope, cloneDataPolicy, callbacks, closure,
    2763         468 :                                 transferable);
    2764             : }
    2765             : 
    2766             : JS_PUBLIC_API(bool)
    2767           0 : JS_StructuredCloneHasTransferables(JSStructuredCloneData& data,
    2768             :                                    bool* hasTransferable)
    2769             : {
    2770           0 :     *hasTransferable = StructuredCloneHasTransferObjects(data);
    2771           0 :     return true;
    2772             : }
    2773             : 
    2774             : JS_PUBLIC_API(bool)
    2775          20 : JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
    2776             :                    const JSStructuredCloneCallbacks* optionalCallbacks,
    2777             :                    void* closure)
    2778             : {
    2779           0 :     AssertHeapIsIdle();
    2780          40 :     CHECK_REQUEST(cx);
    2781             : 
    2782             :     // Strings are associated with zones, not compartments,
    2783             :     // so we copy the string by wrapping it.
    2784           0 :     if (value.isString()) {
    2785           0 :       RootedString strValue(cx, value.toString());
    2786           0 :       if (!cx->compartment()->wrap(cx, &strValue)) {
    2787             :         return false;
    2788             :       }
    2789           0 :       vp.setString(strValue);
    2790           0 :       return true;
    2791             :     }
    2792             : 
    2793          20 :     const JSStructuredCloneCallbacks* callbacks = optionalCallbacks;
    2794             : 
    2795          20 :     JSAutoStructuredCloneBuffer buf(JS::StructuredCloneScope::SameProcessSameThread, callbacks, closure);
    2796             :     {
    2797             :         // If we use Maybe<AutoRealm> here, G++ can't tell that the
    2798             :         // destructor is only called when Maybe::construct was called, and
    2799             :         // we get warnings about using uninitialized variables.
    2800           0 :         if (value.isObject()) {
    2801           0 :             AutoRealm ar(cx, &value.toObject());
    2802           0 :             if (!buf.write(cx, value, callbacks, closure))
    2803           0 :                 return false;
    2804             :         } else {
    2805          18 :             if (!buf.write(cx, value, callbacks, closure))
    2806             :                 return false;
    2807             :         }
    2808             :     }
    2809             : 
    2810          20 :     return buf.read(cx, vp, callbacks, closure);
    2811             : }
    2812             : 
    2813           0 : JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
    2814           0 :   : scope_(other.scope()), data_(other.scope())
    2815             : {
    2816           0 :     data_.ownTransferables_ = other.data_.ownTransferables_;
    2817           0 :     other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
    2818           0 : }
    2819             : 
    2820             : JSAutoStructuredCloneBuffer&
    2821           0 : JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
    2822             : {
    2823           0 :     MOZ_ASSERT(&other != this);
    2824           0 :     MOZ_ASSERT(scope_ == other.scope_);
    2825           0 :     clear();
    2826           0 :     data_.ownTransferables_ = other.data_.ownTransferables_;
    2827           0 :     other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_);
    2828           0 :     return *this;
    2829             : }
    2830             : 
    2831             : void
    2832         412 : JSAutoStructuredCloneBuffer::clear()
    2833             : {
    2834           0 :     data_.discardTransferables();
    2835           0 :     data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
    2836           0 :     data_.refsHeld_.releaseAll();
    2837           0 :     data_.Clear();
    2838           0 :     version_ = 0;
    2839         412 : }
    2840             : 
    2841             : void
    2842           4 : JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version,
    2843             :                                    const JSStructuredCloneCallbacks* callbacks,
    2844             :                                    void* closure)
    2845             : {
    2846           0 :     clear();
    2847           0 :     data_ = std::move(data);
    2848           0 :     version_ = version;
    2849           0 :     data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny);
    2850           4 : }
    2851             : 
    2852             : void
    2853          51 : JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versionp,
    2854             :                                    const JSStructuredCloneCallbacks** callbacks,
    2855             :                                    void** closure)
    2856             : {
    2857           1 :     if (versionp)
    2858           0 :         *versionp = version_;
    2859           1 :     if (callbacks)
    2860           0 :         *callbacks = data_.callbacks_;
    2861           1 :     if (closure)
    2862           0 :         *closure = data_.closure_;
    2863          51 :     *data = std::move(data_);
    2864             : 
    2865           0 :     version_ = 0;
    2866           0 :     data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables);
    2867          51 : }
    2868             : 
    2869             : bool
    2870         179 : JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
    2871             :                                   const JSStructuredCloneCallbacks* optionalCallbacks,
    2872             :                                   void* closure)
    2873             : {
    2874           0 :     MOZ_ASSERT(cx);
    2875           0 :     return !!JS_ReadStructuredClone(cx, data_, version_, scope_, vp,
    2876         179 :                                     optionalCallbacks, closure);
    2877             : }
    2878             : 
    2879             : bool
    2880           0 : JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
    2881             :                                    const JSStructuredCloneCallbacks* optionalCallbacks,
    2882             :                                    void* closure)
    2883             : {
    2884           0 :     HandleValue transferable = UndefinedHandleValue;
    2885          40 :     return write(cx, value, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(), optionalCallbacks, closure);
    2886             : }
    2887             : 
    2888             : bool
    2889         234 : JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
    2890             :                                    HandleValue transferable, JS::CloneDataPolicy cloneDataPolicy,
    2891             :                                    const JSStructuredCloneCallbacks* optionalCallbacks,
    2892             :                                    void* closure)
    2893             : {
    2894           0 :     clear();
    2895           0 :     bool ok = JS_WriteStructuredClone(cx, value, &data_,
    2896         234 :                                       scope_, cloneDataPolicy,
    2897             :                                       optionalCallbacks, closure,
    2898         234 :                                       transferable);
    2899             : 
    2900           0 :     if (ok) {
    2901         234 :         data_.ownTransferables_ = OwnTransferablePolicy::OwnsTransferablesIfAny;
    2902             :     } else {
    2903           0 :         version_ = JS_STRUCTURED_CLONE_VERSION;
    2904           0 :         data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
    2905             :     }
    2906         234 :     return ok;
    2907             : }
    2908             : 
    2909             : JS_PUBLIC_API(bool)
    2910          20 : JS_ReadUint32Pair(JSStructuredCloneReader* r, uint32_t* p1, uint32_t* p2)
    2911             : {
    2912          40 :     return r->input().readPair((uint32_t*) p1, (uint32_t*) p2);
    2913             : }
    2914             : 
    2915             : JS_PUBLIC_API(bool)
    2916          22 : JS_ReadBytes(JSStructuredCloneReader* r, void* p, size_t len)
    2917             : {
    2918          44 :     return r->input().readBytes(p, len);
    2919             : }
    2920             : 
    2921             : JS_PUBLIC_API(bool)
    2922           0 : JS_ReadTypedArray(JSStructuredCloneReader* r, MutableHandleValue vp)
    2923             : {
    2924             :     uint32_t tag, nelems;
    2925           0 :     if (!r->input().readPair(&tag, &nelems))
    2926             :         return false;
    2927           0 :     if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) {
    2928           0 :         return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true);
    2929           0 :     } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) {
    2930             :         uint64_t arrayType;
    2931           0 :         if (!r->input().read(&arrayType))
    2932             :             return false;
    2933           0 :         return r->readTypedArray(arrayType, nelems, vp);
    2934             :     } else {
    2935           0 :         JS_ReportErrorNumberASCII(r->context(), GetErrorMessage, nullptr,
    2936             :                                   JSMSG_SC_BAD_SERIALIZED_DATA,
    2937           0 :                                   "expected type array");
    2938           0 :         return false;
    2939             :     }
    2940             : }
    2941             : 
    2942             : JS_PUBLIC_API(bool)
    2943          64 : JS_WriteUint32Pair(JSStructuredCloneWriter* w, uint32_t tag, uint32_t data)
    2944             : {
    2945         128 :     return w->output().writePair(tag, data);
    2946             : }
    2947             : 
    2948             : JS_PUBLIC_API(bool)
    2949          49 : JS_WriteBytes(JSStructuredCloneWriter* w, const void* p, size_t len)
    2950             : {
    2951          98 :     return w->output().writeBytes(p, len);
    2952             : }
    2953             : 
    2954             : JS_PUBLIC_API(bool)
    2955           0 : JS_WriteString(JSStructuredCloneWriter* w, HandleString str)
    2956             : {
    2957           0 :     return w->writeString(SCTAG_STRING, str);
    2958             : }
    2959             : 
    2960             : JS_PUBLIC_API(bool)
    2961           0 : JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v)
    2962             : {
    2963           0 :     MOZ_ASSERT(v.isObject());
    2964           0 :     assertSameCompartment(w->context(), v);
    2965           0 :     RootedObject obj(w->context(), &v.toObject());
    2966           0 :     return w->writeTypedArray(obj);
    2967             : }
    2968             : 
    2969             : JS_PUBLIC_API(bool)
    2970           0 : JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj)
    2971             : {
    2972           0 :     w->memory.remove(w->memory.lookup(obj));
    2973             : 
    2974           0 :     return true;
    2975             : }
    2976             : 
    2977             : JS_PUBLIC_API(JS::StructuredCloneScope)
    2978           0 : JS_GetStructuredCloneScope(JSStructuredCloneWriter* w)
    2979             : {
    2980             :     return w->output().scope();
    2981             : }

Generated by: LCOV version 1.13-14-ga5dd952