tor-browser

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

CallArgs.h (11873B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * [SMDOC] JS::CallArgs API
      9 *
     10 * Helper classes encapsulating access to the callee, |this| value, arguments,
     11 * and argument count for a call/construct operation.
     12 *
     13 * JS::CallArgs encapsulates access to a JSNative's un-abstracted
     14 * |unsigned argc, Value* vp| arguments.  The principal way to create a
     15 * JS::CallArgs is using JS::CallArgsFromVp:
     16 *
     17 *   // If provided no arguments or a non-numeric first argument, return zero.
     18 *   // Otherwise return |this| exactly as given, without boxing.
     19 *   static bool
     20 *   Func(JSContext* cx, unsigned argc, JS::Value* vp)
     21 *   {
     22 *       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     23 *
     24 *       // Guard against no arguments or a non-numeric arg0.
     25 *       if (args.length() == 0 || !args[0].isNumber()) {
     26 *           args.rval().setInt32(0);
     27 *           return true;
     28 *       }
     29 *
     30 *       // Access to the callee must occur before accessing/setting
     31 *       // the return value.
     32 *       JSObject& callee = args.callee();
     33 *       args.rval().setObject(callee);
     34 *
     35 *       // callee() and calleev() will now assert.
     36 *
     37 *       // It's always fine to access thisv().
     38 *       HandleValue thisv = args.thisv();
     39 *       args.rval().set(thisv);
     40 *
     41 *       // As the return value was last set to |this|, returns |this|.
     42 *       return true;
     43 *   }
     44 *
     45 * CallArgs is exposed publicly and used internally.  Not all parts of its
     46 * public interface are meant to be used by embedders!  See inline comments to
     47 * for details.
     48 *
     49 * It's possible (albeit deprecated) to manually index into |vp| to access the
     50 * callee, |this|, and arguments of a function, and to set its return value.
     51 * This does not have the error-handling or moving-GC correctness of CallArgs.
     52 * New code should use CallArgs instead whenever possible.
     53 *
     54 * The eventual plan is to change JSNative to take |const CallArgs&| directly,
     55 * for automatic assertion of correct use and to make calling functions more
     56 * efficient.  Embedders should start internally switching away from using
     57 * |argc| and |vp| directly, except to create a |CallArgs|.  Then, when an
     58 * eventual release making that change occurs, porting efforts will require
     59 * changing methods' signatures but won't require invasive changes to the
     60 * methods' implementations, potentially under time pressure.
     61 */
     62 
     63 #ifndef js_CallArgs_h
     64 #define js_CallArgs_h
     65 
     66 #include "mozilla/Assertions.h"
     67 #include "mozilla/Attributes.h"
     68 
     69 #include <type_traits>
     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 using JSNative = bool (*)(JSContext* cx, unsigned argc, JS::Value* vp);
     78 
     79 namespace JS {
     80 
     81 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
     82 
     83 namespace detail {
     84 
     85 /*
     86 * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
     87 * replacing with the global object as necessary.
     88 */
     89 extern JS_PUBLIC_API bool ComputeThis(JSContext* cx, JS::Value* vp,
     90                                      MutableHandleObject thisObject);
     91 
     92 #ifdef JS_DEBUG
     93 extern JS_PUBLIC_API void CheckIsValidConstructible(const Value& v);
     94 #endif
     95 
     96 class MOZ_STACK_CLASS IncludeUsedRval {
     97  mutable bool usedRval_ = false;
     98 
     99 public:
    100  bool usedRval() const { return usedRval_; }
    101  void setUsedRval() const { usedRval_ = true; }
    102  void clearUsedRval() const { usedRval_ = false; }
    103  void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
    104 };
    105 
    106 class MOZ_STACK_CLASS NoUsedRval {
    107 public:
    108  bool usedRval() const { return false; }
    109  void setUsedRval() const {}
    110  void clearUsedRval() const {}
    111  void assertUnusedRval() const {}
    112 };
    113 
    114 template <class WantUsedRval>
    115 class MOZ_STACK_CLASS MOZ_NON_PARAM CallArgsBase {
    116  static_assert(std::is_same_v<WantUsedRval, IncludeUsedRval> ||
    117                    std::is_same_v<WantUsedRval, NoUsedRval>,
    118                "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
    119 
    120 protected:
    121  Value* argv_ = nullptr;
    122  unsigned argc_ = 0;
    123  bool constructing_ : 1;
    124 
    125  // True if the caller does not use the return value.
    126  bool ignoresReturnValue_ : 1;
    127 
    128 #ifdef JS_DEBUG
    129  WantUsedRval wantUsedRval_;
    130  bool usedRval() const { return wantUsedRval_.usedRval(); }
    131  void setUsedRval() const { wantUsedRval_.setUsedRval(); }
    132  void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
    133  void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
    134 #else
    135  bool usedRval() const { return false; }
    136  void setUsedRval() const {}
    137  void clearUsedRval() const {}
    138  void assertUnusedRval() const {}
    139 #endif
    140 
    141  CallArgsBase() : constructing_(false), ignoresReturnValue_(false) {}
    142 
    143 public:
    144  // CALLEE ACCESS
    145 
    146  /*
    147   * Returns the function being called, as a value.  Must not be called after
    148   * rval() has been used!
    149   */
    150  HandleValue calleev() const {
    151    this->assertUnusedRval();
    152    return HandleValue::fromMarkedLocation(&argv_[-2]);
    153  }
    154 
    155  /*
    156   * Returns the function being called, as an object.  Must not be called
    157   * after rval() has been used!
    158   */
    159  JSObject& callee() const { return calleev().toObject(); }
    160 
    161  // CALLING/CONSTRUCTING-DIFFERENTIATIONS
    162 
    163  bool isConstructing() const {
    164    if (!argv_[-1].isMagic()) {
    165      return false;
    166    }
    167 
    168 #ifdef JS_DEBUG
    169    if (!this->usedRval()) {
    170      CheckIsValidConstructible(calleev());
    171    }
    172 #endif
    173 
    174    return true;
    175  }
    176 
    177  bool ignoresReturnValue() const { return ignoresReturnValue_; }
    178 
    179  MutableHandleValue newTarget() const {
    180    MOZ_ASSERT(constructing_);
    181    return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
    182  }
    183 
    184  /*
    185   * Returns the |this| value passed to the function.  This method must not
    186   * be called when the function is being called as a constructor via |new|.
    187   * The value may or may not be an object: it is the individual function's
    188   * responsibility to box the value if needed.
    189   */
    190  HandleValue thisv() const {
    191    // Some internal code uses thisv() in constructing cases, so don't do
    192    // this yet.
    193    // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
    194    return HandleValue::fromMarkedLocation(&argv_[-1]);
    195  }
    196 
    197  bool computeThis(JSContext* cx, MutableHandleObject thisObject) const {
    198    if (thisv().isObject()) {
    199      thisObject.set(&thisv().toObject());
    200      return true;
    201    }
    202 
    203    return ComputeThis(cx, base(), thisObject);
    204  }
    205 
    206  // ARGUMENTS
    207 
    208  /* Returns the number of arguments. */
    209  unsigned length() const { return argc_; }
    210 
    211  /* Returns the i-th zero-indexed argument. */
    212  MutableHandleValue operator[](unsigned i) const {
    213    MOZ_RELEASE_ASSERT(i < argc_);
    214    return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
    215  }
    216 
    217  /*
    218   * Returns the i-th zero-indexed argument, or |undefined| if there's no
    219   * such argument.
    220   */
    221  HandleValue get(unsigned i) const {
    222    return i < length() ? HandleValue::fromMarkedLocation(&this->argv_[i])
    223                        : UndefinedHandleValue;
    224  }
    225 
    226  /*
    227   * Returns true if the i-th zero-indexed argument is present and is not
    228   * |undefined|.
    229   */
    230  bool hasDefined(unsigned i) const {
    231    return i < argc_ && !this->argv_[i].isUndefined();
    232  }
    233 
    234  // RETURN VALUE
    235 
    236  /*
    237   * Returns the currently-set return value.  The initial contents of this
    238   * value are unspecified.  Once this method has been called, callee() and
    239   * calleev() can no longer be used.  (If you're compiling against a debug
    240   * build of SpiderMonkey, these methods will assert to aid debugging.)
    241   *
    242   * If the method you're implementing succeeds by returning true, you *must*
    243   * set this.  (SpiderMonkey doesn't currently assert this, but it will do
    244   * so eventually.)  You don't need to use or change this if your method
    245   * fails.
    246   */
    247  MutableHandleValue rval() const {
    248    this->setUsedRval();
    249    return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
    250  }
    251 
    252  /*
    253   * Returns true if there are at least |required| arguments passed in. If
    254   * false, it reports an error message on the context.
    255   */
    256  JS_PUBLIC_API inline bool requireAtLeast(JSContext* cx, const char* fnname,
    257                                           unsigned required) const;
    258 
    259 public:
    260  // These methods are publicly exposed, but they are *not* to be used when
    261  // implementing a JSNative method and encapsulating access to |vp| within
    262  // it.  You probably don't want to use these!
    263 
    264  void setCallee(const Value& aCalleev) const {
    265    this->clearUsedRval();
    266    argv_[-2] = aCalleev;
    267  }
    268 
    269  void setThis(const Value& aThisv) const { argv_[-1] = aThisv; }
    270 
    271  MutableHandleValue mutableThisv() const {
    272    return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
    273  }
    274 
    275 public:
    276  // These methods are publicly exposed, but we're unsure of the interfaces
    277  // (because they're hackish and drop assertions).  Avoid using these if you
    278  // can.
    279 
    280  Value* array() const { return argv_; }
    281  Value* end() const { return argv_ + argc_ + constructing_; }
    282 
    283 public:
    284  // These methods are only intended for internal use.  Embedders shouldn't
    285  // use them!
    286 
    287  Value* base() const { return argv_ - 2; }
    288 
    289  Value* spAfterCall() const {
    290    this->setUsedRval();
    291    return argv_ - 1;
    292  }
    293 };
    294 
    295 }  // namespace detail
    296 
    297 class MOZ_STACK_CLASS MOZ_NON_PARAM CallArgs
    298    : public detail::CallArgsBase<detail::IncludeUsedRval> {
    299 private:
    300  friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
    301  friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
    302                                 bool constructing, bool ignoresReturnValue);
    303 
    304  static CallArgs create(unsigned argc, Value* argv, bool constructing,
    305                         bool ignoresReturnValue = false) {
    306    CallArgs args;
    307    args.clearUsedRval();
    308    args.argv_ = argv;
    309    args.argc_ = argc;
    310    args.constructing_ = constructing;
    311    args.ignoresReturnValue_ = ignoresReturnValue;
    312 #ifdef DEBUG
    313    AssertValueIsNotGray(args.thisv());
    314    AssertValueIsNotGray(args.calleev());
    315    for (unsigned i = 0; i < argc; ++i) {
    316      AssertValueIsNotGray(argv[i]);
    317    }
    318    if (constructing) {
    319      AssertValueIsNotGray(args.newTarget());
    320    }
    321 #endif
    322    return args;
    323  }
    324 
    325 public:
    326  /*
    327   * Helper for requireAtLeast to report the actual exception.  Public
    328   * so we can call it from CallArgsBase and not need multiple
    329   * per-template instantiations of it.
    330   */
    331  static JS_PUBLIC_API void reportMoreArgsNeeded(JSContext* cx,
    332                                                 const char* fnname,
    333                                                 unsigned required,
    334                                                 unsigned actual);
    335 };
    336 
    337 namespace detail {
    338 template <class WantUsedRval>
    339 JS_PUBLIC_API inline bool CallArgsBase<WantUsedRval>::requireAtLeast(
    340    JSContext* cx, const char* fnname, unsigned required) const {
    341  if (MOZ_LIKELY(required <= length())) {
    342    return true;
    343  }
    344 
    345  CallArgs::reportMoreArgsNeeded(cx, fnname, required, length());
    346  return false;
    347 }
    348 }  // namespace detail
    349 
    350 MOZ_ALWAYS_INLINE CallArgs CallArgsFromVp(unsigned argc, Value* vp) {
    351  return CallArgs::create(argc, vp + 2,
    352                          vp[1].isMagicNoReleaseCheck(JS_IS_CONSTRUCTING));
    353 }
    354 
    355 // This method is only intended for internal use in SpiderMonkey.  We may
    356 // eventually move it to an internal header.  Embedders should use
    357 // JS::CallArgsFromVp!
    358 MOZ_ALWAYS_INLINE CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
    359                                          bool constructing = false,
    360                                          bool ignoresReturnValue = false) {
    361  return CallArgs::create(stackSlots - constructing, sp - stackSlots,
    362                          constructing, ignoresReturnValue);
    363 }
    364 
    365 }  // namespace JS
    366 
    367 #endif /* js_CallArgs_h */