LCOV - code coverage report
Current view: top level - js/public - CallArgs.h (source / functions) Hit Total Coverage
Test: output.info Lines: 75 79 94.9 %
Date: 2017-07-14 16:53:18 Functions: 39 41 95.1 %
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             :  * Helper classes encapsulating access to the callee, |this| value, arguments,
       9             :  * and argument count for a call/construct operation.
      10             :  *
      11             :  * JS::CallArgs encapsulates access to a JSNative's un-abstracted
      12             :  * |unsigned argc, Value* vp| arguments.  The principal way to create a
      13             :  * JS::CallArgs is using JS::CallArgsFromVp:
      14             :  *
      15             :  *   // If provided no arguments or a non-numeric first argument, return zero.
      16             :  *   // Otherwise return |this| exactly as given, without boxing.
      17             :  *   static bool
      18             :  *   Func(JSContext* cx, unsigned argc, JS::Value* vp)
      19             :  *   {
      20             :  *       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
      21             :  *
      22             :  *       // Guard against no arguments or a non-numeric arg0.
      23             :  *       if (args.length() == 0 || !args[0].isNumber()) {
      24             :  *           args.rval().setInt32(0);
      25             :  *           return true;
      26             :  *       }
      27             :  *
      28             :  *       // Access to the callee must occur before accessing/setting
      29             :  *       // the return value.
      30             :  *       JSObject& callee = args.callee();
      31             :  *       args.rval().setObject(callee);
      32             :  *
      33             :  *       // callee() and calleev() will now assert.
      34             :  *
      35             :  *       // It's always fine to access thisv().
      36             :  *       HandleValue thisv = args.thisv();
      37             :  *       args.rval().set(thisv);
      38             :  *
      39             :  *       // As the return value was last set to |this|, returns |this|.
      40             :  *       return true;
      41             :  *   }
      42             :  *
      43             :  * CallArgs is exposed publicly and used internally.  Not all parts of its
      44             :  * public interface are meant to be used by embedders!  See inline comments to
      45             :  * for details.
      46             :  *
      47             :  * It's possible (albeit deprecated) to manually index into |vp| to access the
      48             :  * callee, |this|, and arguments of a function, and to set its return value.
      49             :  * It's also possible to use the supported API of JS_CALLEE, JS_THIS, JS_ARGV,
      50             :  * JS_RVAL, and JS_SET_RVAL to the same ends.
      51             :  *
      52             :  * But neither API has the error-handling or moving-GC correctness of CallArgs.
      53             :  * New code should use CallArgs instead whenever possible.
      54             :  *
      55             :  * The eventual plan is to change JSNative to take |const CallArgs&| directly,
      56             :  * for automatic assertion of correct use and to make calling functions more
      57             :  * efficient.  Embedders should start internally switching away from using
      58             :  * |argc| and |vp| directly, except to create a |CallArgs|.  Then, when an
      59             :  * eventual release making that change occurs, porting efforts will require
      60             :  * changing methods' signatures but won't require invasive changes to the
      61             :  * methods' implementations, potentially under time pressure.
      62             :  */
      63             : 
      64             : #ifndef js_CallArgs_h
      65             : #define js_CallArgs_h
      66             : 
      67             : #include "mozilla/Assertions.h"
      68             : #include "mozilla/Attributes.h"
      69             : #include "mozilla/TypeTraits.h"
      70             : 
      71             : #include "jstypes.h"
      72             : 
      73             : #include "js/RootingAPI.h"
      74             : #include "js/Value.h"
      75             : 
      76             : /* Typedef for native functions called by the JS VM. */
      77             : typedef bool
      78             : (* JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);
      79             : 
      80             : namespace JS {
      81             : 
      82             : extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
      83             : 
      84             : namespace detail {
      85             : 
      86             : /*
      87             :  * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
      88             :  * replacing with the global object as necessary.
      89             :  */
      90             : extern JS_PUBLIC_API(Value)
      91             : ComputeThis(JSContext* cx, JS::Value* vp);
      92             : 
      93             : #ifdef JS_DEBUG
      94             : extern JS_PUBLIC_API(void)
      95             : CheckIsValidConstructible(const Value& v);
      96             : #endif
      97             : 
      98             : class MOZ_STACK_CLASS IncludeUsedRval
      99             : {
     100             :     mutable bool usedRval_;
     101             : 
     102             :   public:
     103        1364 :     bool usedRval() const { return usedRval_; }
     104      218112 :     void setUsedRval() const { usedRval_ = true; }
     105      193932 :     void clearUsedRval() const { usedRval_ = false; }
     106      515266 :     void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
     107             : };
     108             : 
     109             : class MOZ_STACK_CLASS NoUsedRval
     110             : {
     111             :   public:
     112             :     bool usedRval() const { return false; }
     113        1480 :     void setUsedRval() const {}
     114             :     void clearUsedRval() const {}
     115             :     void assertUnusedRval() const {}
     116             : };
     117             : 
     118             : template<class WantUsedRval>
     119       17006 : class MOZ_STACK_CLASS CallArgsBase
     120             : {
     121             :     static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value ||
     122             :                   mozilla::IsSame<WantUsedRval, NoUsedRval>::value,
     123             :                   "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
     124             : 
     125             :   protected:
     126             :     Value* argv_;
     127             :     unsigned argc_;
     128             :     bool constructing_:1;
     129             : 
     130             :     // True if the caller does not use the return value.
     131             :     bool ignoresReturnValue_:1;
     132             : 
     133             : #ifdef JS_DEBUG
     134             :     WantUsedRval wantUsedRval_;
     135        1364 :     bool usedRval() const { return wantUsedRval_.usedRval(); }
     136      219592 :     void setUsedRval() const { wantUsedRval_.setUsedRval(); }
     137      193932 :     void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
     138      515266 :     void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
     139             : #else
     140             :     bool usedRval() const { return false; }
     141             :     void setUsedRval() const {}
     142             :     void clearUsedRval() const {}
     143             :     void assertUnusedRval() const {}
     144             : #endif
     145             : 
     146             :   public:
     147             :     // CALLEE ACCESS
     148             : 
     149             :     /*
     150             :      * Returns the function being called, as a value.  Must not be called after
     151             :      * rval() has been used!
     152             :      */
     153      515266 :     HandleValue calleev() const {
     154      515266 :         this->assertUnusedRval();
     155      515267 :         return HandleValue::fromMarkedLocation(&argv_[-2]);
     156             :     }
     157             : 
     158             :     /*
     159             :      * Returns the function being called, as an object.  Must not be called
     160             :      * after rval() has been used!
     161             :      */
     162      212523 :     JSObject& callee() const {
     163      212523 :         return calleev().toObject();
     164             :     }
     165             : 
     166             :     // CALLING/CONSTRUCTING-DIFFERENTIATIONS
     167             : 
     168        1684 :     bool isConstructing() const {
     169        1684 :         if (!argv_[-1].isMagic())
     170         320 :             return false;
     171             : 
     172             : #ifdef JS_DEBUG
     173        1364 :         if (!this->usedRval())
     174        1364 :             CheckIsValidConstructible(calleev());
     175             : #endif
     176             : 
     177        1364 :         return true;
     178             :     }
     179             : 
     180       26683 :     bool ignoresReturnValue() const {
     181       26683 :         return ignoresReturnValue_;
     182             :     }
     183             : 
     184        3575 :     MutableHandleValue newTarget() const {
     185        3575 :         MOZ_ASSERT(constructing_);
     186        3575 :         return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
     187             :     }
     188             : 
     189             :     /*
     190             :      * Returns the |this| value passed to the function.  This method must not
     191             :      * be called when the function is being called as a constructor via |new|.
     192             :      * The value may or may not be an object: it is the individual function's
     193             :      * responsibility to box the value if needed.
     194             :      */
     195      316634 :     HandleValue thisv() const {
     196             :         // Some internal code uses thisv() in constructing cases, so don't do
     197             :         // this yet.
     198             :         // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
     199      316634 :         return HandleValue::fromMarkedLocation(&argv_[-1]);
     200             :     }
     201             : 
     202           0 :     Value computeThis(JSContext* cx) const {
     203           0 :         if (thisv().isObject())
     204           0 :             return thisv();
     205             : 
     206           0 :         return ComputeThis(cx, base());
     207             :     }
     208             : 
     209             :     // ARGUMENTS
     210             : 
     211             :         /* Returns the number of arguments. */
     212      419762 :     unsigned length() const { return argc_; }
     213             : 
     214             :     /* Returns the i-th zero-indexed argument. */
     215      249903 :     MutableHandleValue operator[](unsigned i) const {
     216      249903 :         MOZ_ASSERT(i < argc_);
     217      249903 :         return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
     218             :     }
     219             : 
     220             :     /*
     221             :      * Returns the i-th zero-indexed argument, or |undefined| if there's no
     222             :      * such argument.
     223             :      */
     224       11479 :     HandleValue get(unsigned i) const {
     225       11479 :         return i < length()
     226       10967 :                ? HandleValue::fromMarkedLocation(&this->argv_[i])
     227       22446 :                : UndefinedHandleValue;
     228             :     }
     229             : 
     230             :     /*
     231             :      * Returns true if the i-th zero-indexed argument is present and is not
     232             :      * |undefined|.
     233             :      */
     234        1851 :     bool hasDefined(unsigned i) const {
     235        1851 :         return i < argc_ && !this->argv_[i].isUndefined();
     236             :     }
     237             : 
     238             :     // RETURN VALUE
     239             : 
     240             :     /*
     241             :      * Returns the currently-set return value.  The initial contents of this
     242             :      * value are unspecified.  Once this method has been called, callee() and
     243             :      * calleev() can no longer be used.  (If you're compiling against a debug
     244             :      * build of SpiderMonkey, these methods will assert to aid debugging.)
     245             :      *
     246             :      * If the method you're implementing succeeds by returning true, you *must*
     247             :      * set this.  (SpiderMonkey doesn't currently assert this, but it will do
     248             :      * so eventually.)  You don't need to use or change this if your method
     249             :      * fails.
     250             :      */
     251      189017 :     MutableHandleValue rval() const {
     252      189017 :         this->setUsedRval();
     253      189017 :         return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
     254             :     }
     255             : 
     256             :   public:
     257             :     // These methods are publicly exposed, but they are *not* to be used when
     258             :     // implementing a JSNative method and encapsulating access to |vp| within
     259             :     // it.  You probably don't want to use these!
     260             : 
     261       20603 :     void setCallee(const Value& aCalleev) const {
     262       20603 :         this->clearUsedRval();
     263       20603 :         argv_[-2] = aCalleev;
     264       20603 :     }
     265             : 
     266       16089 :     void setThis(const Value& aThisv) const {
     267       16089 :         argv_[-1] = aThisv;
     268       16089 :     }
     269             : 
     270       33714 :     MutableHandleValue mutableThisv() const {
     271       33714 :         return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
     272             :     }
     273             : 
     274             :   public:
     275             :     // These methods are publicly exposed, but we're unsure of the interfaces
     276             :     // (because they're hackish and drop assertions).  Avoid using these if you
     277             :     // can.
     278             : 
     279       75624 :     Value* array() const { return argv_; }
     280      190784 :     Value* end() const { return argv_ + argc_ + constructing_; }
     281             : 
     282             :   public:
     283             :     // These methods are only intended for internal use.  Embedders shouldn't
     284             :     // use them!
     285             : 
     286       76349 :     Value* base() const { return argv_ - 2; }
     287             : 
     288       30300 :     Value* spAfterCall() const {
     289       30300 :         this->setUsedRval();
     290       30300 :         return argv_ - 1;
     291             :     }
     292             : };
     293             : 
     294             : } // namespace detail
     295             : 
     296       15563 : class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
     297             : {
     298             :   private:
     299             :     friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
     300             :     friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing,
     301             :                                    bool ignoresReturnValue);
     302             : 
     303      173333 :     static CallArgs create(unsigned argc, Value* argv, bool constructing,
     304             :                            bool ignoresReturnValue = false) {
     305             :         CallArgs args;
     306      173333 :         args.clearUsedRval();
     307      173333 :         args.argv_ = argv;
     308      173333 :         args.argc_ = argc;
     309      173333 :         args.constructing_ = constructing;
     310      173333 :         args.ignoresReturnValue_ = ignoresReturnValue;
     311             : #ifdef DEBUG
     312      173333 :         MOZ_ASSERT(ValueIsNotGray(args.thisv()));
     313      173333 :         MOZ_ASSERT(ValueIsNotGray(args.calleev()));
     314      418973 :         for (unsigned i = 0; i < argc; ++i)
     315      245640 :             MOZ_ASSERT(ValueIsNotGray(argv[i]));
     316             : #endif
     317      173333 :         return args;
     318             :     }
     319             : 
     320             :   public:
     321             :     /*
     322             :      * Returns true if there are at least |required| arguments passed in. If
     323             :      * false, it reports an error message on the context.
     324             :      */
     325             :     JS_PUBLIC_API(bool) requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const;
     326             : 
     327             : };
     328             : 
     329             : MOZ_ALWAYS_INLINE CallArgs
     330      136911 : CallArgsFromVp(unsigned argc, Value* vp)
     331             : {
     332      136911 :     return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
     333             : }
     334             : 
     335             : // This method is only intended for internal use in SpiderMonkey.  We may
     336             : // eventually move it to an internal header.  Embedders should use
     337             : // JS::CallArgsFromVp!
     338             : MOZ_ALWAYS_INLINE CallArgs
     339       36138 : CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false,
     340             :                bool ignoresReturnValue = false)
     341             : {
     342       36138 :     return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
     343       72276 :                             ignoresReturnValue);
     344             : }
     345             : 
     346             : } // namespace JS
     347             : 
     348             : /*
     349             :  * Macros to hide interpreter stack layout details from a JSNative using its
     350             :  * JS::Value* vp parameter.  DO NOT USE THESE!  Instead use JS::CallArgs and
     351             :  * friends, above.  These macros will be removed when we change JSNative to
     352             :  * take a const JS::CallArgs&.
     353             :  */
     354             : 
     355             : /*
     356             :  * Return |this| if |this| is an object.  Otherwise, return the global object
     357             :  * if |this| is null or undefined, and finally return a boxed version of any
     358             :  * other primitive.
     359             :  *
     360             :  * Note: if this method returns null, an error has occurred and must be
     361             :  * propagated or caught.
     362             :  */
     363             : MOZ_ALWAYS_INLINE JS::Value
     364       11289 : JS_THIS(JSContext* cx, JS::Value* vp)
     365             : {
     366       11289 :     return vp[1].isPrimitive() ? JS::detail::ComputeThis(cx, vp) : vp[1];
     367             : }
     368             : 
     369             : /*
     370             :  * A note on JS_THIS_OBJECT: no equivalent method is part of the CallArgs
     371             :  * interface, and we're unlikely to add one (functions shouldn't be implicitly
     372             :  * exposing the global object to arbitrary callers).  Continue using |vp|
     373             :  * directly for this case, but be aware this API will eventually be replaced
     374             :  * with a function that operates directly upon |args.thisv()|.
     375             :  */
     376             : #define JS_THIS_OBJECT(cx,vp)   (JS_THIS(cx,vp).toObjectOrNull())
     377             : 
     378             : /*
     379             :  * |this| is passed to functions in ES5 without change.  Functions themselves
     380             :  * do any post-processing they desire to box |this|, compute the global object,
     381             :  * &c.  This macro retrieves a function's unboxed |this| value.
     382             :  *
     383             :  * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
     384             :  * or vice versa.  Either use the provided this value with this macro, or
     385             :  * compute the boxed |this| value using those.  JS_THIS_VALUE must not be used
     386             :  * if the function is being called as a constructor.
     387             :  *
     388             :  * But: DO NOT USE THIS!  Instead use JS::CallArgs::thisv(), above.
     389             :  *
     390             :  */
     391             : #define JS_THIS_VALUE(cx,vp)    ((vp)[1])
     392             : 
     393             : #endif /* js_CallArgs_h */

Generated by: LCOV version 1.13