LCOV - code coverage report
Current view: top level - js/src/vm - Xdr.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 51 178 28.7 %
Date: 2017-07-14 16:53:18 Functions: 11 24 45.8 %
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             : #include "vm/Xdr.h"
       8             : 
       9             : #include "mozilla/PodOperations.h"
      10             : 
      11             : #include <string.h>
      12             : 
      13             : #include "jsapi.h"
      14             : #include "jscntxt.h"
      15             : #include "jsscript.h"
      16             : 
      17             : #include "vm/Debugger.h"
      18             : #include "vm/EnvironmentObject.h"
      19             : #include "vm/TraceLogging.h"
      20             : 
      21             : using namespace js;
      22             : using mozilla::PodEqual;
      23             : 
      24             : template<XDRMode mode>
      25             : LifoAlloc&
      26          95 : XDRState<mode>::lifoAlloc() const {
      27          95 :     return buf.cx()->tempLifoAlloc();
      28             : }
      29             : 
      30             : template<XDRMode mode>
      31             : void
      32           0 : XDRState<mode>::postProcessContextErrors(JSContext* cx)
      33             : {
      34           0 :     if (!cx->helperThread() && cx->isExceptionPending()) {
      35           0 :         MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok ||
      36             :                    resultCode_ == JS::TranscodeResult_Throw);
      37           0 :         resultCode_ = JS::TranscodeResult_Throw;
      38             :     }
      39           0 : }
      40             : 
      41             : template<XDRMode mode>
      42             : bool
      43       44010 : XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
      44             : {
      45             :     static_assert(sizeof(Latin1Char) == sizeof(uint8_t), "Latin1Char must fit in 1 byte");
      46             : 
      47           0 :     MOZ_ASSERT(mode == XDR_ENCODE);
      48             : 
      49       44010 :     if (nchars == 0)
      50          98 :         return true;
      51       43912 :     uint8_t* ptr = buf.write(nchars);
      52       43912 :     if (!ptr)
      53           0 :         return fail(JS::TranscodeResult_Throw);
      54             : 
      55       43912 :     mozilla::PodCopy(ptr, chars, nchars);
      56       43912 :     return true;
      57             : }
      58             : 
      59             : template<XDRMode mode>
      60             : bool
      61           0 : XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
      62             : {
      63           0 :     if (nchars == 0)
      64           0 :         return true;
      65           0 :     size_t nbytes = nchars * sizeof(char16_t);
      66             :     if (mode == XDR_ENCODE) {
      67           0 :         uint8_t* ptr = buf.write(nbytes);
      68           0 :         if (!ptr)
      69           0 :             return fail(JS::TranscodeResult_Throw);
      70           0 :         mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
      71             :     } else {
      72           0 :         const uint8_t* ptr = buf.read(nbytes);
      73           0 :         mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
      74             :     }
      75           0 :     return true;
      76             : }
      77             : 
      78             : template<XDRMode mode>
      79             : static bool
      80        1432 : VersionCheck(XDRState<mode>* xdr)
      81             : {
      82        2864 :     JS::BuildIdCharVector buildId;
      83        1432 :     if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
      84           0 :         return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
      85        1432 :     MOZ_ASSERT(!buildId.empty());
      86             : 
      87             :     uint32_t buildIdLength;
      88             :     if (mode == XDR_ENCODE)
      89          82 :         buildIdLength = buildId.length();
      90             : 
      91        1432 :     if (!xdr->codeUint32(&buildIdLength))
      92           0 :         return false;
      93             : 
      94        1432 :     if (mode == XDR_DECODE && buildIdLength != buildId.length())
      95           0 :         return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
      96             : 
      97             :     if (mode == XDR_ENCODE) {
      98          82 :         if (!xdr->codeBytes(buildId.begin(), buildIdLength))
      99           0 :             return false;
     100             :     } else {
     101        2700 :         JS::BuildIdCharVector decodedBuildId;
     102             : 
     103             :         // buildIdLength is already checked against the length of current
     104             :         // buildId.
     105        1350 :         if (!decodedBuildId.resize(buildIdLength)) {
     106           0 :             ReportOutOfMemory(xdr->cx());
     107           0 :             return xdr->fail(JS::TranscodeResult_Throw);
     108             :         }
     109             : 
     110        1350 :         if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
     111           0 :             return false;
     112             : 
     113             :         // We do not provide binary compatibility with older scripts.
     114        1350 :         if (!PodEqual(decodedBuildId.begin(), buildId.begin(), buildIdLength))
     115           0 :             return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
     116             :     }
     117             : 
     118        1432 :     return true;
     119             : }
     120             : 
     121             : template<XDRMode mode>
     122             : bool
     123        1091 : XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
     124             : {
     125        1091 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx());
     126             :     TraceLoggerTextId event =
     127        1091 :         mode == XDR_DECODE ? TraceLogger_DecodeFunction : TraceLogger_EncodeFunction;
     128        2182 :     AutoTraceLog tl(logger, event);
     129             : 
     130        2182 :     RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
     131             :     if (mode == XDR_DECODE) {
     132        1091 :         MOZ_ASSERT(!sourceObject);
     133        1091 :         funp.set(nullptr);
     134           0 :     } else if (getTreeKey(funp) != AutoXDRTree::noKey) {
     135           0 :         MOZ_ASSERT(sourceObject);
     136           0 :         scope = funp->nonLazyScript()->enclosingScope();
     137             :     } else {
     138           0 :         MOZ_ASSERT(!sourceObject);
     139           0 :         MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
     140             :     }
     141             : 
     142        1091 :     if (!VersionCheck(this)) {
     143           0 :         postProcessContextErrors(cx());
     144           0 :         return false;
     145             :     }
     146             : 
     147        1091 :     if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
     148           0 :         postProcessContextErrors(cx());
     149           0 :         funp.set(nullptr);
     150           0 :         return false;
     151             :     }
     152             : 
     153        1091 :     return true;
     154             : }
     155             : 
     156             : template<XDRMode mode>
     157             : bool
     158         341 : XDRState<mode>::codeScript(MutableHandleScript scriptp)
     159             : {
     160         341 :     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx());
     161             :     TraceLoggerTextId event =
     162         341 :         mode == XDR_DECODE ? TraceLogger_DecodeScript : TraceLogger_EncodeScript;
     163         680 :     AutoTraceLog tl(logger, event);
     164             : 
     165         680 :     AutoXDRTree scriptTree(this, getTopLevelTreeKey());
     166             : 
     167             :     if (mode == XDR_DECODE)
     168         259 :         scriptp.set(nullptr);
     169             :     else
     170          82 :         MOZ_ASSERT(!scriptp->enclosingScope());
     171             : 
     172         341 :     if (!VersionCheck(this)) {
     173           0 :         postProcessContextErrors(cx());
     174           0 :         return false;
     175             :     }
     176             : 
     177         341 :     if (!XDRScript(this, nullptr, nullptr, nullptr, scriptp)) {
     178           0 :         postProcessContextErrors(cx());
     179           0 :         scriptp.set(nullptr);
     180           0 :         return false;
     181             :     }
     182             : 
     183         339 :     return true;
     184             : }
     185             : 
     186             : template<XDRMode mode>
     187             : bool
     188       22496 : XDRState<mode>::codeConstValue(MutableHandleValue vp)
     189             : {
     190       22496 :     return XDRScriptConst(this, vp);
     191             : }
     192             : 
     193             : template class js::XDRState<XDR_ENCODE>;
     194             : template class js::XDRState<XDR_DECODE>;
     195             : 
     196       13619 : AutoXDRTree::AutoXDRTree(XDRCoderBase* xdr, AutoXDRTree::Key key)
     197             :   : key_(key),
     198             :     parent_(this),
     199       13619 :     xdr_(xdr)
     200             : {
     201       13619 :     if (key_ != AutoXDRTree::noKey)
     202           0 :         xdr->createOrReplaceSubTree(this);
     203       13619 : }
     204             : 
     205       27234 : AutoXDRTree::~AutoXDRTree()
     206             : {
     207       13617 :     if (key_ != AutoXDRTree::noKey)
     208           0 :         xdr_->endSubTree();
     209       13617 : }
     210             : 
     211             : constexpr AutoXDRTree::Key AutoXDRTree::noKey;
     212             : constexpr AutoXDRTree::Key AutoXDRTree::noSubTree;
     213             : constexpr AutoXDRTree::Key AutoXDRTree::topLevel;
     214             : 
     215             : AutoXDRTree::Key
     216           0 : XDRIncrementalEncoder::getTopLevelTreeKey() const
     217             : {
     218           0 :     return AutoXDRTree::topLevel;
     219             : }
     220             : 
     221             : AutoXDRTree::Key
     222           0 : XDRIncrementalEncoder::getTreeKey(JSFunction* fun) const
     223             : {
     224           0 :     if (fun->isInterpretedLazy()) {
     225             :         static_assert(sizeof(fun->lazyScript()->begin()) == 4 ||
     226             :                       sizeof(fun->lazyScript()->end()) == 4,
     227             :                       "AutoXDRTree key requires LazyScripts positions to be uint32");
     228           0 :         return uint64_t(fun->lazyScript()->begin()) << 32 | fun->lazyScript()->end();
     229             :     }
     230             : 
     231           0 :     if (fun->isInterpreted()) {
     232             :         static_assert(sizeof(fun->nonLazyScript()->sourceStart()) == 4 ||
     233             :                       sizeof(fun->nonLazyScript()->sourceEnd()) == 4,
     234             :                       "AutoXDRTree key requires JSScripts positions to be uint32");
     235           0 :         return uint64_t(fun->nonLazyScript()->sourceStart()) << 32 | fun->nonLazyScript()->sourceEnd();
     236             :     }
     237             : 
     238           0 :     return AutoXDRTree::noKey;
     239             : }
     240             : 
     241             : bool
     242           0 : XDRIncrementalEncoder::init()
     243             : {
     244           0 :     if (!tree_.init())
     245           0 :         return false;
     246           0 :     return true;
     247             : }
     248             : 
     249             : void
     250           0 : XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child)
     251             : {
     252           0 :     AutoXDRTree* parent = scope_;
     253           0 :     child->parent_ = parent;
     254           0 :     scope_ = child;
     255           0 :     if (oom_)
     256           0 :         return;
     257             : 
     258           0 :     size_t cursor = buf.cursor();
     259             : 
     260             :     // End the parent slice here, set the key to the child.
     261           0 :     if (parent) {
     262           0 :         Slice& last = node_->back();
     263           0 :         last.sliceLength = cursor - last.sliceBegin;
     264           0 :         last.child = child->key_;
     265           0 :         MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
     266             :                       uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
     267             :                       uint32_t(child->key_) <= uint32_t(parent->key_));
     268             :     }
     269             : 
     270             :     // Create or replace the part with what is going to be encoded next.
     271           0 :     SlicesTree::AddPtr p = tree_.lookupForAdd(child->key_);
     272           0 :     SlicesNode tmp;
     273           0 :     if (!p) {
     274             :         // Create a new sub-tree node.
     275           0 :         if (!tree_.add(p, child->key_, mozilla::Move(tmp))) {
     276           0 :             oom_ = true;
     277           0 :             return;
     278             :         }
     279             :     } else {
     280             :         // Replace an exisiting sub-tree.
     281           0 :         p->value() = mozilla::Move(tmp);
     282             :     }
     283           0 :     node_ = &p->value();
     284             : 
     285             :     // Add content to the root of the new sub-tree,
     286             :     // i-e an empty slice with no children.
     287           0 :     if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree }))
     288           0 :         MOZ_CRASH("SlicesNode have a reserved space of 1.");
     289             : }
     290             : 
     291             : void
     292           0 : XDRIncrementalEncoder::endSubTree()
     293             : {
     294           0 :     AutoXDRTree* child = scope_;
     295           0 :     AutoXDRTree* parent = child->parent_;
     296           0 :     scope_ = parent;
     297           0 :     if (oom_)
     298           0 :         return;
     299             : 
     300           0 :     size_t cursor = buf.cursor();
     301             : 
     302             :     // End the child sub-tree.
     303           0 :     Slice& last = node_->back();
     304           0 :     last.sliceLength = cursor - last.sliceBegin;
     305           0 :     MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
     306             : 
     307             :     // Stop at the top-level.
     308           0 :     if (!parent) {
     309           0 :         node_ = nullptr;
     310           0 :         return;
     311             :     }
     312             : 
     313             :     // Restore the parent node.
     314           0 :     SlicesTree::Ptr p = tree_.lookup(parent->key_);
     315           0 :     node_ = &p->value();
     316             : 
     317             :     // Append the new slice in the parent node.
     318           0 :     if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree })) {
     319           0 :         oom_ = true;
     320           0 :         return;
     321             :     }
     322             : }
     323             : 
     324             : bool
     325           0 : XDRIncrementalEncoder::linearize(JS::TranscodeBuffer& buffer)
     326             : {
     327           0 :     if (oom_) {
     328           0 :         ReportOutOfMemory(cx());
     329           0 :         return fail(JS::TranscodeResult_Throw);
     330             :     }
     331             : 
     332             :     // Do not linearize while we are currently adding bytes.
     333           0 :     MOZ_ASSERT(scope_ == nullptr);
     334             : 
     335             :     // Visit the tree parts in a depth first order, to linearize the bits.
     336           0 :     Vector<SlicesNode::ConstRange> depthFirst(cx());
     337             : 
     338           0 :     SlicesTree::Ptr p = tree_.lookup(AutoXDRTree::topLevel);
     339           0 :     MOZ_ASSERT(p);
     340             : 
     341           0 :     if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
     342           0 :         ReportOutOfMemory(cx());
     343           0 :         return fail(JS::TranscodeResult_Throw);
     344             :     }
     345             : 
     346           0 :     while (!depthFirst.empty()) {
     347           0 :         SlicesNode::ConstRange& iter = depthFirst.back();
     348           0 :         Slice slice = iter.popCopyFront();
     349             :         // These fields have different meaning, but they should be correlated if
     350             :         // the tree is well formatted.
     351           0 :         MOZ_ASSERT_IF(slice.child == AutoXDRTree::noSubTree, iter.empty());
     352           0 :         if (iter.empty())
     353           0 :             depthFirst.popBack();
     354             : 
     355             :         // Copy the bytes associated with the current slice to the transcode
     356             :         // buffer which would be serialized.
     357           0 :         MOZ_ASSERT(slice.sliceBegin <= slices_.length());
     358           0 :         MOZ_ASSERT(slice.sliceBegin + slice.sliceLength <= slices_.length());
     359           0 :         if (!buffer.append(slices_.begin() + slice.sliceBegin, slice.sliceLength)) {
     360           0 :             ReportOutOfMemory(cx());
     361           0 :             return fail(JS::TranscodeResult_Throw);
     362             :         }
     363             : 
     364             :         // If we are at the end, go to back to the parent script.
     365           0 :         if (slice.child == AutoXDRTree::noSubTree)
     366           0 :             continue;
     367             : 
     368             :         // Visit the sub-parts before visiting the rest of the current slice.
     369           0 :         SlicesTree::Ptr p = tree_.lookup(slice.child);
     370           0 :         MOZ_ASSERT(p);
     371           0 :         if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
     372           0 :             ReportOutOfMemory(cx());
     373           0 :             return fail(JS::TranscodeResult_Throw);
     374             :         }
     375             :     }
     376             : 
     377           0 :     tree_.finish();
     378           0 :     slices_.clearAndFree();
     379           0 :     return true;
     380             : }

Generated by: LCOV version 1.13