LCOV - code coverage report
Current view: top level - js/src/vm - Xdr.h (source / functions) Hit Total Coverage
Test: output.info Lines: 114 142 80.3 %
Date: 2017-07-14 16:53:18 Functions: 47 64 73.4 %
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             : #ifndef vm_Xdr_h
       8             : #define vm_Xdr_h
       9             : 
      10             : #include "mozilla/EndianUtils.h"
      11             : #include "mozilla/TypeTraits.h"
      12             : 
      13             : #include "jsatom.h"
      14             : #include "jsfriendapi.h"
      15             : 
      16             : namespace js {
      17             : 
      18             : class XDRBufferBase
      19             : {
      20             :   public:
      21        1435 :     explicit XDRBufferBase(JSContext* cx, size_t cursor = 0)
      22        1435 :       : context_(cx), cursor_(cursor) { }
      23             : 
      24      315704 :     JSContext* cx() const {
      25      315704 :         return context_;
      26             :     }
      27             : 
      28           0 :     size_t cursor() const {
      29           0 :         return cursor_;
      30             :     }
      31             : 
      32             :   protected:
      33             :     JSContext* const context_;
      34             :     size_t cursor_;
      35             : };
      36             : 
      37             : template <XDRMode mode>
      38             : class XDRBuffer;
      39             : 
      40             : template <>
      41             : class XDRBuffer<XDR_ENCODE> : public XDRBufferBase
      42             : {
      43             :   public:
      44          82 :     XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
      45          82 :       : XDRBufferBase(cx, cursor),
      46          82 :         buffer_(buffer) { }
      47             : 
      48      254659 :     uint8_t* write(size_t n) {
      49      254659 :         MOZ_ASSERT(n != 0);
      50      254659 :         if (!buffer_.growByUninitialized(n)) {
      51           0 :             ReportOutOfMemory(cx());
      52           0 :             return nullptr;
      53             :         }
      54      254659 :         uint8_t* ptr = &buffer_[cursor_];
      55      254659 :         cursor_ += n;
      56      254659 :         return ptr;
      57             :     }
      58             : 
      59             :     const char* readCString() {
      60             :         MOZ_CRASH("Should never read in encode mode");
      61             :         return nullptr;
      62             :     }
      63             : 
      64             :     const uint8_t* read(size_t n) {
      65             :         MOZ_CRASH("Should never read in encode mode");
      66             :         return nullptr;
      67             :     }
      68             : 
      69             :   private:
      70             :     JS::TranscodeBuffer& buffer_;
      71             : };
      72             : 
      73             : template <>
      74             : class XDRBuffer<XDR_DECODE> : public XDRBufferBase
      75             : {
      76             :   public:
      77         198 :     XDRBuffer(JSContext* cx, const JS::TranscodeRange& range)
      78         198 :       : XDRBufferBase(cx),
      79         198 :         buffer_(range) { }
      80             : 
      81        1155 :     XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
      82        1155 :       : XDRBufferBase(cx, cursor),
      83        1155 :         buffer_(buffer.begin(), buffer.length()) { }
      84             : 
      85        1352 :     const char* readCString() {
      86        1352 :         char* ptr = reinterpret_cast<char*>(&buffer_[cursor_]);
      87        1352 :         uint8_t* end = reinterpret_cast<uint8_t*>(strchr(ptr, '\0')) + 1;
      88        1352 :         MOZ_ASSERT(buffer_.begin().get() < end);
      89        1352 :         MOZ_ASSERT(end <= buffer_.end().get());
      90        1352 :         cursor_ = end - buffer_.begin().get();
      91        1352 :         return ptr;
      92             :     }
      93             : 
      94      817906 :     const uint8_t* read(size_t n) {
      95      817906 :         MOZ_ASSERT(cursor_ < buffer_.length());
      96      817917 :         uint8_t* ptr = &buffer_[cursor_];
      97      817920 :         cursor_ += n;
      98      817920 :         return ptr;
      99             :     }
     100             : 
     101             :     uint8_t* write(size_t n) {
     102             :         MOZ_CRASH("Should never write in decode mode");
     103             :         return nullptr;
     104             :     }
     105             : 
     106             :   private:
     107             :     const JS::TranscodeRange buffer_;
     108             : };
     109             : 
     110             : class XDRCoderBase;
     111             : class XDRIncrementalEncoder;
     112             : 
     113             : // An AutoXDRTree is used to identify section encoded by an XDRIncrementalEncoder.
     114             : //
     115             : // Its primary goal is to identify functions, such that we can first encode them
     116             : // as LazyScript, and later replaced by them by their corresponding bytecode
     117             : // once delazified.
     118             : //
     119             : // As a convenience, this is also used to identify the top-level of the content
     120             : // encoded by an XDRIncrementalEncoder.
     121             : //
     122             : // Sections can be encoded any number of times in an XDRIncrementalEncoder, and
     123             : // the latest encoded version would replace all the previous one.
     124             : class MOZ_RAII AutoXDRTree
     125             : {
     126             :   public:
     127             :     // For a JSFunction, a tree key is defined as being:
     128             :     //     script()->begin << 32 | script()->end
     129             :     //
     130             :     // Based on the invariant that |begin <= end|, we can make special
     131             :     // keys, such as the top-level script.
     132             :     using Key = uint64_t;
     133             : 
     134             :     AutoXDRTree(XDRCoderBase* xdr, Key key);
     135             :     ~AutoXDRTree();
     136             : 
     137             :     // Indicate the lack of a key for the current tree.
     138             :     static constexpr Key noKey = 0;
     139             : 
     140             :     // Used to end the slices when there is no children.
     141             :     static constexpr Key noSubTree = Key(1) << 32;
     142             : 
     143             :     // Used as the root key of the tree in the hash map.
     144             :     static constexpr Key topLevel = Key(2) << 32;
     145             : 
     146             :   private:
     147             :     friend class XDRIncrementalEncoder;
     148             : 
     149             :     Key key_;
     150             :     AutoXDRTree* parent_;
     151             :     XDRCoderBase* xdr_;
     152             : };
     153             : 
     154             : class XDRCoderBase
     155             : {
     156             :   protected:
     157        1435 :     XDRCoderBase() {}
     158             : 
     159             :   public:
     160         344 :     virtual AutoXDRTree::Key getTopLevelTreeKey() const { return AutoXDRTree::noKey; }
     161       13320 :     virtual AutoXDRTree::Key getTreeKey(JSFunction* fun) const { return AutoXDRTree::noKey; }
     162           0 :     virtual void createOrReplaceSubTree(AutoXDRTree* child) {};
     163           0 :     virtual void endSubTree() {};
     164             : };
     165             : 
     166             : /*
     167             :  * XDR serialization state.  All data is encoded in little endian.
     168             :  */
     169             : template <XDRMode mode>
     170             : class XDRState : public XDRCoderBase
     171             : {
     172             :   public:
     173             :     XDRBuffer<mode> buf;
     174             :   private:
     175             :     JS::TranscodeResult resultCode_;
     176             : 
     177             :   public:
     178        1237 :     XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
     179             :       : buf(cx, buffer, cursor),
     180        1237 :         resultCode_(JS::TranscodeResult_Ok)
     181             :     {
     182        1237 :     }
     183             : 
     184             :     template <typename RangeType>
     185         198 :     XDRState(JSContext* cx, const RangeType& range)
     186             :       : buf(cx, range),
     187         198 :         resultCode_(JS::TranscodeResult_Ok)
     188             :     {
     189         198 :     }
     190             : 
     191        1434 :     virtual ~XDRState() {};
     192             : 
     193      315611 :     JSContext* cx() const {
     194      315611 :         return buf.cx();
     195             :     }
     196             :     virtual LifoAlloc& lifoAlloc() const;
     197             : 
     198        6580 :     virtual bool hasOptions() const { return false; }
     199           0 :     virtual const ReadOnlyCompileOptions& options() {
     200           0 :         MOZ_CRASH("does not have options");
     201             :     }
     202        1173 :     virtual bool hasScriptSourceObjectOut() const { return false; }
     203           0 :     virtual ScriptSourceObject** scriptSourceObjectOut() {
     204           0 :         MOZ_CRASH("does not have scriptSourceObjectOut.");
     205             :     }
     206             : 
     207             :     // Record logical failures of XDR.
     208             :     void postProcessContextErrors(JSContext* cx);
     209        2868 :     JS::TranscodeResult resultCode() const {
     210        2868 :         return resultCode_;
     211             :     }
     212           0 :     bool fail(JS::TranscodeResult code) {
     213           0 :         MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok);
     214           0 :         resultCode_ = code;
     215           0 :         return false;
     216             :     }
     217             : 
     218       83348 :     bool codeUint8(uint8_t* n) {
     219             :         if (mode == XDR_ENCODE) {
     220       18473 :             uint8_t* ptr = buf.write(sizeof(*n));
     221       18473 :             if (!ptr)
     222           0 :                 return fail(JS::TranscodeResult_Throw);
     223       18473 :             *ptr = *n;
     224             :         } else {
     225       64875 :             *n = *buf.read(sizeof(*n));
     226             :         }
     227       83348 :         return true;
     228             :     }
     229             : 
     230       23602 :     bool codeUint16(uint16_t* n) {
     231             :         if (mode == XDR_ENCODE) {
     232        5622 :             uint8_t* ptr = buf.write(sizeof(*n));
     233        5622 :             if (!ptr)
     234           0 :                 return fail(JS::TranscodeResult_Throw);
     235        5622 :             mozilla::LittleEndian::writeUint16(ptr, *n);
     236             :         } else {
     237       17980 :             const uint8_t* ptr = buf.read(sizeof(*n));
     238       17980 :             *n = mozilla::LittleEndian::readUint16(ptr);
     239             :         }
     240       23602 :         return true;
     241             :     }
     242             : 
     243      750320 :     bool codeUint32(uint32_t* n) {
     244             :         if (mode == XDR_ENCODE) {
     245      180124 :             uint8_t* ptr = buf.write(sizeof(*n));
     246      180124 :             if (!ptr)
     247           0 :                 return fail(JS::TranscodeResult_Throw);
     248      180124 :             mozilla::LittleEndian::writeUint32(ptr, *n);
     249             :         } else {
     250      570196 :             const uint8_t* ptr = buf.read(sizeof(*n));
     251      570201 :             *n = mozilla::LittleEndian::readUint32(ptr);
     252             :         }
     253      750324 :         return true;
     254             :     }
     255             : 
     256        2732 :     bool codeUint64(uint64_t* n) {
     257             :         if (mode == XDR_ENCODE) {
     258         560 :             uint8_t* ptr = buf.write(sizeof(*n));
     259         560 :             if (!ptr)
     260           0 :                 return fail(JS::TranscodeResult_Throw);
     261         560 :             mozilla::LittleEndian::writeUint64(ptr, *n);
     262             :         } else {
     263        2172 :             const uint8_t* ptr = buf.read(sizeof(*n));
     264        2172 :             *n = mozilla::LittleEndian::readUint64(ptr);
     265             :         }
     266        2732 :         return true;
     267             :     }
     268             : 
     269             :     /*
     270             :      * Use SFINAE to refuse any specialization which is not an enum.  Uses of
     271             :      * this function do not have to specialize the type of the enumerated field
     272             :      * as C++ will extract the parameterized from the argument list.
     273             :      */
     274             :     template <typename T>
     275       38419 :     bool codeEnum32(T* val, typename mozilla::EnableIf<mozilla::IsEnum<T>::value, T>::Type * = NULL)
     276             :     {
     277             :         uint32_t tmp;
     278             :         if (mode == XDR_ENCODE)
     279        9612 :             tmp = uint32_t(*val);
     280       38419 :         if (!codeUint32(&tmp))
     281           0 :             return false;
     282             :         if (mode == XDR_DECODE)
     283       28807 :             *val = T(tmp);
     284       38419 :         return true;
     285             :     }
     286             : 
     287          53 :     bool codeDouble(double* dp) {
     288             :         union DoublePun {
     289             :             double d;
     290             :             uint64_t u;
     291             :         } pun;
     292             :         if (mode == XDR_ENCODE)
     293          14 :             pun.d = *dp;
     294          53 :         if (!codeUint64(&pun.u))
     295           0 :             return false;
     296             :         if (mode == XDR_DECODE)
     297          39 :             *dp = pun.d;
     298          53 :         return true;
     299             :     }
     300             : 
     301       26930 :     bool codeBytes(void* bytes, size_t len) {
     302       26930 :         if (len == 0)
     303           0 :             return true;
     304             :         if (mode == XDR_ENCODE) {
     305        5886 :             uint8_t* ptr = buf.write(len);
     306        5886 :             if (!ptr)
     307           0 :                 return fail(JS::TranscodeResult_Throw);
     308        5886 :             memcpy(ptr, bytes, len);
     309             :         } else {
     310       21044 :             memcpy(bytes, buf.read(len), len);
     311             :         }
     312       26930 :         return true;
     313             :     }
     314             : 
     315             :     /*
     316             :      * During encoding the string is written into the buffer together with its
     317             :      * terminating '\0'. During decoding the method returns a pointer into the
     318             :      * decoding buffer and the caller must copy the string if it will outlive
     319             :      * the decoding buffer.
     320             :      */
     321        1434 :     bool codeCString(const char** sp) {
     322             :         if (mode == XDR_ENCODE) {
     323          82 :             size_t n = strlen(*sp) + 1;
     324          82 :             uint8_t* ptr = buf.write(n);
     325          82 :             if (!ptr)
     326           0 :                 return fail(JS::TranscodeResult_Throw);
     327          82 :             memcpy(ptr, *sp, n);
     328             :         } else {
     329        1352 :             *sp = buf.readCString();
     330             :         }
     331        1434 :         return true;
     332             :     }
     333             : 
     334             :     bool codeChars(const JS::Latin1Char* chars, size_t nchars);
     335             :     bool codeChars(char16_t* chars, size_t nchars);
     336             : 
     337             :     bool codeFunction(JS::MutableHandleFunction objp, HandleScriptSource sourceObject = nullptr);
     338             :     bool codeScript(MutableHandleScript scriptp);
     339             :     bool codeConstValue(MutableHandleValue vp);
     340             : };
     341             : 
     342             : using XDREncoder = XDRState<XDR_ENCODE>;
     343             : using XDRDecoder = XDRState<XDR_DECODE>;
     344             : 
     345         177 : class XDROffThreadDecoder : public XDRDecoder
     346             : {
     347             :     const ReadOnlyCompileOptions* options_;
     348             :     ScriptSourceObject** sourceObjectOut_;
     349             :     LifoAlloc& alloc_;
     350             : 
     351             :   public:
     352             :     // Note, when providing an JSContext, where isJSContext is false,
     353             :     // then the initialization of the ScriptSourceObject would remain
     354             :     // incomplete. Thus, the sourceObjectOut must be used to finish the
     355             :     // initialization with ScriptSourceObject::initFromOptions after the
     356             :     // decoding.
     357             :     //
     358             :     // When providing a sourceObjectOut pointer, you have to ensure that it is
     359             :     // marked by the GC to avoid dangling pointers.
     360         178 :     XDROffThreadDecoder(JSContext* cx, LifoAlloc& alloc,
     361             :                         const ReadOnlyCompileOptions* options,
     362             :                         ScriptSourceObject** sourceObjectOut,
     363             :                         const JS::TranscodeRange& range)
     364         178 :       : XDRDecoder(cx, range),
     365             :         options_(options),
     366             :         sourceObjectOut_(sourceObjectOut),
     367         178 :         alloc_(alloc)
     368             :     {
     369         178 :         MOZ_ASSERT(options);
     370         178 :         MOZ_ASSERT(sourceObjectOut);
     371         178 :         MOZ_ASSERT(*sourceObjectOut == nullptr);
     372         178 :     }
     373             : 
     374         157 :     LifoAlloc& lifoAlloc() const override {
     375         157 :         return alloc_;
     376             :     }
     377             : 
     378        5631 :     bool hasOptions() const override { return true; }
     379         335 :     const ReadOnlyCompileOptions& options() override {
     380         335 :         return *options_;
     381             :     }
     382         178 :     bool hasScriptSourceObjectOut() const override { return true; }
     383         178 :     ScriptSourceObject** scriptSourceObjectOut() override {
     384         178 :         return sourceObjectOut_;
     385             :     }
     386             : };
     387             : 
     388             : class XDRIncrementalEncoder : public XDREncoder
     389             : {
     390             :     // The incremental encoder encodes the content of scripts and functions in
     391             :     // the XDRBuffer. It can be used to encode multiple times the same AutoXDRTree,
     392             :     // and uses its key to identify which part to replace.
     393             :     //
     394             :     // Internally, this encoder keeps a tree representation of the scopes. Each
     395             :     // node is composed of a vector of slices which are interleaved by child
     396             :     // nodes.
     397             :     //
     398             :     // A slice corresponds to an index and a length within the content of the
     399             :     // slices_ buffer. The index is updated when a slice is created, and the
     400             :     // length is updated when the slice is ended, either by creating a new scope
     401             :     // child, or by closing the scope and going back to the parent.
     402             :     //
     403             :     //                  +---+---+---+
     404             :     //        begin     |   |   |   |
     405             :     //        length    |   |   |   |
     406             :     //        child     | . | . | . |
     407             :     //                  +-|-+-|-+---+
     408             :     //                    |   |
     409             :     //          +---------+   +---------+
     410             :     //          |                       |
     411             :     //          v                       v
     412             :     //      +---+---+                 +---+
     413             :     //      |   |   |                 |   |
     414             :     //      |   |   |                 |   |
     415             :     //      | . | . |                 | . |
     416             :     //      +-|-+---+                 +---+
     417             :     //        |
     418             :     //        |
     419             :     //        |
     420             :     //        v
     421             :     //      +---+
     422             :     //      |   |
     423             :     //      |   |
     424             :     //      | . |
     425             :     //      +---+
     426             :     //
     427             :     //
     428             :     // The tree key is used to identify the child nodes, and to make them
     429             :     // easily replaceable.
     430             :     //
     431             :     // The tree is rooted at the |topLevel| key.
     432             :     //
     433             : 
     434             :     struct Slice {
     435             :         size_t sliceBegin;
     436             :         size_t sliceLength;
     437             :         AutoXDRTree::Key child;
     438             :     };
     439             : 
     440             :     using SlicesNode = Vector<Slice, 1, SystemAllocPolicy>;
     441             :     using SlicesTree = HashMap<AutoXDRTree::Key, SlicesNode, DefaultHasher<AutoXDRTree::Key>,
     442             :                                SystemAllocPolicy>;
     443             : 
     444             :     // Last opened XDR-tree on the stack.
     445             :     AutoXDRTree* scope_;
     446             :     // Node corresponding to the opened scope.
     447             :     SlicesNode* node_;
     448             :     // Tree of slices.
     449             :     SlicesTree tree_;
     450             :     JS::TranscodeBuffer slices_;
     451             :     bool oom_;
     452             : 
     453             :   public:
     454           0 :     explicit XDRIncrementalEncoder(JSContext* cx)
     455           0 :       : XDREncoder(cx, slices_, 0),
     456             :         scope_(nullptr),
     457             :         node_(nullptr),
     458           0 :         oom_(false)
     459             :     {
     460           0 :     }
     461             : 
     462           0 :     virtual ~XDRIncrementalEncoder() {}
     463             : 
     464             :     AutoXDRTree::Key getTopLevelTreeKey() const override;
     465             :     AutoXDRTree::Key getTreeKey(JSFunction* fun) const override;
     466             : 
     467             :     MOZ_MUST_USE bool init();
     468             : 
     469             :     void createOrReplaceSubTree(AutoXDRTree* child) override;
     470             :     void endSubTree() override;
     471             : 
     472             :     // Append the content collected during the incremental encoding into the
     473             :     // buffer given as argument.
     474             :     MOZ_MUST_USE bool linearize(JS::TranscodeBuffer& buffer);
     475             : };
     476             : 
     477             : } /* namespace js */
     478             : 
     479             : #endif /* vm_Xdr_h */

Generated by: LCOV version 1.13