tor-browser

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

ArgumentsObject.h (21084B)


      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 #ifndef vm_ArgumentsObject_h
      8 #define vm_ArgumentsObject_h
      9 
     10 #include "mozilla/MemoryReporting.h"
     11 
     12 #include "gc/Barrier.h"
     13 #include "gc/GCArray.h"
     14 #include "util/BitArray.h"
     15 #include "vm/NativeObject.h"
     16 
     17 namespace js {
     18 
     19 class AbstractFramePtr;
     20 class ArgumentsObject;
     21 class ScriptFrameIter;
     22 
     23 namespace jit {
     24 class JitFrameLayout;
     25 }  // namespace jit
     26 
     27 // RareArgumentsData stores the deleted-elements bits for an arguments object.
     28 // Because |delete arguments[i]| is uncommon, we allocate this data the first
     29 // time an element is deleted.
     30 class RareArgumentsData {
     31  // Pointer to an array of bits indicating, for every argument in
     32  // [0, initialLength) whether the element has been deleted. See
     33  // ArgumentsObject::isElementDeleted comment.
     34  size_t deletedBits_[1];
     35 
     36  RareArgumentsData() = default;
     37  RareArgumentsData(const RareArgumentsData&) = delete;
     38  void operator=(const RareArgumentsData&) = delete;
     39 
     40 public:
     41  static RareArgumentsData* create(JSContext* cx, ArgumentsObject* obj);
     42  static size_t bytesRequired(size_t numActuals);
     43 
     44  bool isElementDeleted(size_t len, size_t i) const {
     45    MOZ_ASSERT(i < len);
     46    return IsBitArrayElementSet(deletedBits_, len, i);
     47  }
     48  void markElementDeleted(size_t len, size_t i) {
     49    MOZ_ASSERT(i < len);
     50    SetBitArrayElement(deletedBits_, len, i);
     51  }
     52 };
     53 
     54 // ArgumentsData stores the initial indexed arguments provided to a function
     55 // call. It is used to store arguments[i] -- up until the corresponding
     56 // property is modified, when the relevant value is flagged to memorialize the
     57 // modification.
     58 struct ArgumentsData {
     59  RareArgumentsData* rareData = nullptr;
     60 
     61  /*
     62   * This array holds either the current argument value or the magic
     63   * forwarding value. The latter means that the function has both a
     64   * CallObject and an ArgumentsObject AND the particular formal variable is
     65   * aliased by the CallObject. In such cases, the CallObject holds the
     66   * canonical value so any element access to the arguments object should load
     67   * the value out of the CallObject (which is pointed to by MAYBE_CALL_SLOT).
     68   */
     69  GCOwnedArray<Value> args;
     70 
     71  /*
     72   * numArgs = std::max(numFormalArgs, numActualArgs)
     73   * The array 'args' has numArgs elements.
     74   */
     75  explicit ArgumentsData(uint32_t numArgs);
     76 
     77  uint32_t numArgs() const { return args.size(); }
     78 
     79  /* For jit use: */
     80  static constexpr ptrdiff_t offsetOfArgs() {
     81    return offsetof(ArgumentsData, args) +
     82           GCOwnedArray<Value>::offsetOfElements();
     83  }
     84 
     85  static size_t bytesRequired(size_t numArgs) {
     86    return offsetof(ArgumentsData, args) +
     87           GCOwnedArray<Value>::bytesRequired(numArgs);
     88  }
     89 };
     90 
     91 // Maximum supported value of arguments.length. This bounds the
     92 // maximum number of arguments that can be supplied to a spread call
     93 // or Function.prototype.apply.  This value also bounds the number of
     94 // elements parsed in an array initializer.  NB: keep this in sync
     95 // with the copy in builtin/SelfHostingDefines.h.
     96 static const unsigned ARGS_LENGTH_MAX = 500 * 1000;
     97 
     98 // Maximum number of arguments supported in jitcode. This bounds the
     99 // maximum number of arguments that can be supplied to a spread call
    100 // or Function.prototype.apply without entering the VM. We limit the
    101 // number of parameters we can handle to a number that does not risk
    102 // us allocating too much stack, notably on Windows where there is a
    103 // 4K guard page that has to be touched to extend the stack. The value
    104 // "3000" is the size of the guard page minus an arbitrary, but large,
    105 // safety margin. See bug 1351278.
    106 static const uint32_t JIT_ARGS_LENGTH_MAX = 3000 / sizeof(JS::Value);
    107 
    108 static_assert(JIT_ARGS_LENGTH_MAX <= ARGS_LENGTH_MAX,
    109              "maximum jit arguments should be <= maximum arguments");
    110 
    111 /*
    112 * [SMDOC] ArgumentsObject
    113 *
    114 * ArgumentsObject instances represent |arguments| objects created to store
    115 * function arguments when a function is called.  It's expensive to create such
    116 * objects if they're never used, so they're only created when they are
    117 * potentially used.
    118 *
    119 * Arguments objects are complicated because, for non-strict mode code, they
    120 * must alias any named arguments which were provided to the function.  Gnarly
    121 * example:
    122 *
    123 *   function f(a, b, c, d)
    124 *   {
    125 *     arguments[0] = "seta";
    126 *     assertEq(a, "seta");
    127 *     b = "setb";
    128 *     assertEq(arguments[1], "setb");
    129 *     c = "setc";
    130 *     assertEq(arguments[2], undefined);
    131 *     arguments[3] = "setd";
    132 *     assertEq(d, undefined);
    133 *   }
    134 *   f("arga", "argb");
    135 *
    136 * ES5's strict mode behaves more sanely, and named arguments don't alias
    137 * elements of an arguments object.
    138 *
    139 * ArgumentsObject instances use the following reserved slots:
    140 *
    141 *   INITIAL_LENGTH_SLOT
    142 *     Stores the initial value of arguments.length, plus a bit indicating
    143 *     whether arguments.length and/or arguments[@@iterator] have been
    144 *     modified.  Use initialLength(), hasOverriddenLength(), and
    145 *     hasOverriddenIterator() to access these values.  If arguments.length has
    146 *     been modified, then the current value of arguments.length is stored in
    147 *     another slot associated with a new property.
    148 *   DATA_SLOT
    149 *     Stores an ArgumentsData*, described above.
    150 *   MAYBE_CALL_SLOT
    151 *     Stores the CallObject, if the callee has aliased bindings. See
    152 *     the ArgumentsData::args comment.
    153 *   CALLEE_SLOT
    154 *     Stores the initial arguments.callee. This value can be overridden on
    155 *     mapped arguments objects, see hasOverriddenCallee.
    156 */
    157 class ArgumentsObject : public NativeObject {
    158 public:
    159  static const uint32_t INITIAL_LENGTH_SLOT = 0;
    160  static const uint32_t DATA_SLOT = 1;
    161  static const uint32_t MAYBE_CALL_SLOT = 2;
    162  static const uint32_t CALLEE_SLOT = 3;
    163 
    164  static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
    165  static const uint32_t ITERATOR_OVERRIDDEN_BIT = 0x2;
    166  static const uint32_t ELEMENT_OVERRIDDEN_BIT = 0x4;
    167  static const uint32_t CALLEE_OVERRIDDEN_BIT = 0x8;
    168  static const uint32_t FORWARDED_ARGUMENTS_BIT = 0x10;
    169  static const uint32_t PACKED_BITS_COUNT = 5;
    170  static const uint32_t PACKED_BITS_MASK = (1 << PACKED_BITS_COUNT) - 1;
    171 
    172  static_assert(ARGS_LENGTH_MAX <= (UINT32_MAX >> PACKED_BITS_COUNT),
    173                "Max arguments length must fit in available bits");
    174 
    175 // Our ability to inline functions that use |arguments| is limited by
    176 // the number of registers available to represent Value operands to
    177 // CreateInlinedArgumentsObject.
    178 #if defined(JS_CODEGEN_X86)
    179  static const uint32_t MaxInlinedArgs = 1;
    180 #else
    181  static const uint32_t MaxInlinedArgs = 3;
    182 #endif
    183 
    184 protected:
    185  template <typename CopyArgs>
    186  static ArgumentsObject* create(JSContext* cx, HandleFunction callee,
    187                                 unsigned numActuals, CopyArgs& copy);
    188 
    189  ArgumentsData* data() const {
    190    return reinterpret_cast<ArgumentsData*>(
    191        getFixedSlot(DATA_SLOT).toPrivate());
    192  }
    193 
    194  RareArgumentsData* maybeRareData() const { return data()->rareData; }
    195 
    196  [[nodiscard]] bool createRareData(JSContext* cx);
    197 
    198  RareArgumentsData* getOrCreateRareData(JSContext* cx) {
    199    if (!data()->rareData && !createRareData(cx)) {
    200      return nullptr;
    201    }
    202    return data()->rareData;
    203  }
    204 
    205  static bool obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
    206                              ObjectOpResult& result);
    207 
    208  static bool obj_mayResolve(const JSAtomState& names, jsid id, JSObject*);
    209 
    210 public:
    211  static const uint32_t RESERVED_SLOTS = 4;
    212  static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
    213 
    214  /* Create an arguments object for a frame that is expecting them. */
    215  static ArgumentsObject* createExpected(JSContext* cx, AbstractFramePtr frame);
    216 
    217  /*
    218   * Purposefully disconnect the returned arguments object from the frame
    219   * by always creating a new copy that does not alias formal parameters.
    220   * This allows function-local analysis to determine that formals are
    221   * not aliased and generally simplifies arguments objects.
    222   */
    223  static ArgumentsObject* createUnexpected(JSContext* cx,
    224                                           ScriptFrameIter& iter);
    225  static ArgumentsObject* createUnexpected(JSContext* cx,
    226                                           AbstractFramePtr frame);
    227 
    228  static ArgumentsObject* createForIon(JSContext* cx,
    229                                       jit::JitFrameLayout* frame,
    230                                       HandleObject scopeChain);
    231  static ArgumentsObject* createForInlinedIon(JSContext* cx, Value* args,
    232                                              HandleFunction callee,
    233                                              HandleObject scopeChain,
    234                                              uint32_t numActuals);
    235  static ArgumentsObject* createFromValueArray(JSContext* cx,
    236                                               HandleValueArray argsArray,
    237                                               HandleFunction callee,
    238                                               HandleObject scopeChain,
    239                                               uint32_t numActuals);
    240 
    241 private:
    242  template <typename CopyArgs>
    243  static ArgumentsObject* finishPure(JSContext* cx, ArgumentsObject* obj,
    244                                     JSFunction* callee, JSObject* callObj,
    245                                     unsigned numActuals, CopyArgs& copy);
    246 
    247 public:
    248  /*
    249   * Allocate ArgumentsData and fill reserved slots after allocating an
    250   * ArgumentsObject in Ion code.
    251   */
    252  static ArgumentsObject* finishForIonPure(JSContext* cx,
    253                                           jit::JitFrameLayout* frame,
    254                                           JSObject* scopeChain,
    255                                           ArgumentsObject* obj);
    256 
    257  /*
    258   * Allocate ArgumentsData for inlined arguments and fill reserved slots after
    259   * allocating an ArgumentsObject in Ion code.
    260   */
    261  static ArgumentsObject* finishInlineForIonPure(
    262      JSContext* cx, JSObject* rawCallObj, JSFunction* rawCallee, Value* args,
    263      uint32_t numActuals, ArgumentsObject* obj);
    264 
    265  static ArgumentsObject* createTemplateObject(JSContext* cx, bool mapped);
    266 
    267  /*
    268   * Return the initial length of the arguments.  This may differ from the
    269   * current value of arguments.length!
    270   */
    271  uint32_t initialLength() const {
    272    uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >>
    273                    PACKED_BITS_COUNT;
    274    MOZ_ASSERT(argc <= ARGS_LENGTH_MAX);
    275    return argc;
    276  }
    277 
    278  bool hasFlags(uint32_t flags) const {
    279    const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT);
    280    return v.toInt32() & flags;
    281  }
    282 
    283  // True iff arguments.length has been assigned or deleted.
    284  bool hasOverriddenLength() const { return hasFlags(LENGTH_OVERRIDDEN_BIT); }
    285 
    286  void markLengthOverridden() {
    287    uint32_t v =
    288        getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT;
    289    setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
    290  }
    291 
    292  // Create the default "length" property and set LENGTH_OVERRIDDEN_BIT.
    293  static bool reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj);
    294 
    295  // True iff arguments[@@iterator] has been assigned or deleted.
    296  bool hasOverriddenIterator() const {
    297    return hasFlags(ITERATOR_OVERRIDDEN_BIT);
    298  }
    299 
    300  void markIteratorOverridden() {
    301    uint32_t v =
    302        getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | ITERATOR_OVERRIDDEN_BIT;
    303    setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
    304  }
    305 
    306  // Create the default @@iterator property and set ITERATOR_OVERRIDDEN_BIT.
    307  static bool reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj);
    308 
    309  /*
    310   * Return the arguments iterator function.
    311   */
    312  static bool getArgumentsIterator(JSContext* cx, MutableHandleValue val);
    313 
    314  // True iff any element has been assigned or deleted.
    315  bool hasOverriddenElement() const { return hasFlags(ELEMENT_OVERRIDDEN_BIT); }
    316 
    317  void markElementOverridden() {
    318    uint32_t v =
    319        getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | ELEMENT_OVERRIDDEN_BIT;
    320    setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
    321  }
    322 
    323 private:
    324  /*
    325   * Because the arguments object is a real object, its elements may be
    326   * deleted. This is implemented by setting a 'deleted' flag for the arg
    327   * which is read by argument object resolve and getter/setter hooks.
    328   *
    329   * NB: an element, once deleted, stays deleted. Thus:
    330   *
    331   *   function f(x) { delete arguments[0]; arguments[0] = 42; return x }
    332   *   assertEq(f(1), 1);
    333   *
    334   * This works because, once a property is deleted from an arguments object,
    335   * it gets regular properties with regular getters/setters that don't alias
    336   * ArgumentsData::args.
    337   */
    338  bool isElementDeleted(uint32_t i) const {
    339    MOZ_ASSERT(i < data()->numArgs());
    340    if (i >= initialLength()) {
    341      return false;
    342    }
    343    bool result = maybeRareData() &&
    344                  maybeRareData()->isElementDeleted(initialLength(), i);
    345    MOZ_ASSERT_IF(result, hasOverriddenElement());
    346    return result;
    347  }
    348 
    349 protected:
    350  bool markElementDeleted(JSContext* cx, uint32_t i);
    351 
    352 public:
    353  /*
    354   * Return true iff the index is a valid element index for this arguments
    355   * object.
    356   *
    357   * Returning true here doesn't imply that the element value can be read
    358   * through |ArgumentsObject::element()|. For example unmapped arguments
    359   * objects can have an element index property redefined without having marked
    360   * the element as deleted. Instead use |maybeGetElement()| or manually check
    361   * for |hasOverriddenElement()|.
    362   */
    363  bool isElement(uint32_t i) const {
    364    return i < initialLength() && !isElementDeleted(i);
    365  }
    366 
    367  /*
    368   * An ArgumentsObject serves two roles:
    369   *  - a real object, accessed through regular object operations, e.g..,
    370   *    GetElement corresponding to 'arguments[i]';
    371   *  - a VM-internal data structure, storing the value of arguments (formal
    372   *    and actual) that are accessed directly by the VM when a reading the
    373   *    value of a formal parameter.
    374   * There are two ways to access the ArgumentsData::args corresponding to
    375   * these two use cases:
    376   *  - object access should use elements(i) which will take care of
    377   *    forwarding when the value is the magic forwarding value;
    378   *  - VM argument access should use arg(i) which will assert that the
    379   *    value is not the magic forwarding value (since, if such forwarding was
    380   *    needed, the frontend should have emitted JSOp::GetAliasedVar).
    381   */
    382  const Value& element(uint32_t i) const;
    383 
    384  inline void setElement(uint32_t i, const Value& v);
    385 
    386  const Value& arg(unsigned i) const {
    387    MOZ_ASSERT(i < data()->numArgs());
    388    const Value& v = data()->args[i];
    389    MOZ_ASSERT(!v.isMagic());
    390    return v;
    391  }
    392 
    393  void setArg(unsigned i, const Value& v) {
    394    MOZ_ASSERT(i < data()->numArgs());
    395    MOZ_ASSERT(!data()->args[i].isMagic());
    396    data()->args.setElement(this, i, v);
    397  }
    398 
    399  /*
    400   * Test if an argument is forwarded, i.e. its actual value is stored in the
    401   * CallObject and can't be directly read from |ArgumentsData::args|.
    402   */
    403  bool argIsForwarded(unsigned i) const {
    404    MOZ_ASSERT(i < data()->numArgs());
    405    const Value& v = data()->args[i];
    406    MOZ_ASSERT_IF(IsMagicScopeSlotValue(v), anyArgIsForwarded());
    407    return IsMagicScopeSlotValue(v);
    408  }
    409 
    410  bool anyArgIsForwarded() const { return hasFlags(FORWARDED_ARGUMENTS_BIT); }
    411 
    412  void markArgumentForwarded() {
    413    uint32_t v =
    414        getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | FORWARDED_ARGUMENTS_BIT;
    415    setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
    416  }
    417 
    418  /*
    419   * Attempt to speedily and efficiently access the i-th element of this
    420   * arguments object.  Return true if the element was speedily returned.
    421   * Return false if the element must be looked up more slowly using
    422   * getProperty or some similar method. The second overload copies the
    423   * elements [start, start + count) into the locations starting at 'vp'.
    424   *
    425   * NB: Returning false does not indicate error!
    426   */
    427  bool maybeGetElement(uint32_t i, MutableHandleValue vp) {
    428    if (i >= initialLength() || hasOverriddenElement()) {
    429      return false;
    430    }
    431    vp.set(element(i));
    432    return true;
    433  }
    434 
    435  inline bool maybeGetElements(uint32_t start, uint32_t count, js::Value* vp);
    436 
    437  /*
    438   * Measures things hanging off this ArgumentsObject that are counted by the
    439   * |miscSize| argument in JSObject::sizeOfExcludingThis().
    440   */
    441  size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const {
    442    if (!data()) {  // Template arguments objects have no data.
    443      return 0;
    444    }
    445    return mallocSizeOf(data()) + mallocSizeOf(maybeRareData());
    446  }
    447  size_t sizeOfData() const {
    448    return ArgumentsData::bytesRequired(data()->numArgs()) +
    449           (maybeRareData() ? RareArgumentsData::bytesRequired(initialLength())
    450                            : 0);
    451  }
    452 
    453  static void finalize(JS::GCContext* gcx, JSObject* obj);
    454  static void trace(JSTracer* trc, JSObject* obj);
    455  static size_t objectMoved(JSObject* dst, JSObject* src);
    456 
    457  /* For jit use: */
    458  static size_t getDataSlotOffset() { return getFixedSlotOffset(DATA_SLOT); }
    459  static size_t getInitialLengthSlotOffset() {
    460    return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
    461  }
    462 
    463  static Value MagicEnvSlotValue(uint32_t slot) {
    464    // When forwarding slots to a backing CallObject, the slot numbers are
    465    // stored as uint32 magic values. This raises an ambiguity if we have
    466    // also copied JS_OPTIMIZED_OUT magic from a JIT frame or
    467    // JS_UNINITIALIZED_LEXICAL magic on the CallObject. To distinguish
    468    // normal magic values (those with a JSWhyMagic) and uint32 magic
    469    // values, we add the maximum JSWhyMagic value to the slot
    470    // number. This is safe as ARGS_LENGTH_MAX is well below UINT32_MAX.
    471    static_assert(UINT32_MAX - JS_WHY_MAGIC_COUNT > ARGS_LENGTH_MAX);
    472    return JS::MagicValueUint32(slot + JS_WHY_MAGIC_COUNT);
    473  }
    474  static uint32_t SlotFromMagicScopeSlotValue(const Value& v) {
    475    static_assert(UINT32_MAX - JS_WHY_MAGIC_COUNT > ARGS_LENGTH_MAX);
    476    return v.magicUint32() - JS_WHY_MAGIC_COUNT;
    477  }
    478  static bool IsMagicScopeSlotValue(const Value& v) {
    479    return v.isMagic() && v.magicUint32() > JS_WHY_MAGIC_COUNT;
    480  }
    481 
    482  static void MaybeForwardToCallObject(AbstractFramePtr frame,
    483                                       ArgumentsObject* obj,
    484                                       ArgumentsData* data);
    485  static void MaybeForwardToCallObject(JSFunction* callee, JSObject* callObj,
    486                                       ArgumentsObject* obj,
    487                                       ArgumentsData* data);
    488 };
    489 
    490 class MappedArgumentsObject : public ArgumentsObject {
    491  static const JSClassOps classOps_;
    492  static const ClassExtension classExt_;
    493  static const ObjectOps objectOps_;
    494 
    495 public:
    496  static const JSClass class_;
    497 
    498  JSFunction& callee() const {
    499    return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
    500  }
    501 
    502  bool hasOverriddenCallee() const { return hasFlags(CALLEE_OVERRIDDEN_BIT); }
    503 
    504  void markCalleeOverridden() {
    505    uint32_t v =
    506        getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | CALLEE_OVERRIDDEN_BIT;
    507    setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
    508  }
    509 
    510  static size_t getCalleeSlotOffset() {
    511    return getFixedSlotOffset(CALLEE_SLOT);
    512  }
    513 
    514  // Create the default "callee" property and set CALLEE_OVERRIDDEN_BIT.
    515  static bool reifyCallee(JSContext* cx, Handle<MappedArgumentsObject*> obj);
    516 
    517 private:
    518  static bool obj_enumerate(JSContext* cx, HandleObject obj);
    519  static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id,
    520                          bool* resolvedp);
    521  static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
    522                                 Handle<JS::PropertyDescriptor> desc,
    523                                 ObjectOpResult& result);
    524 };
    525 
    526 class UnmappedArgumentsObject : public ArgumentsObject {
    527  static const JSClassOps classOps_;
    528  static const ClassExtension classExt_;
    529 
    530 public:
    531  static const JSClass class_;
    532 
    533 private:
    534  static bool obj_enumerate(JSContext* cx, HandleObject obj);
    535  static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id,
    536                          bool* resolvedp);
    537 };
    538 
    539 extern bool MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id,
    540                            MutableHandleValue vp);
    541 
    542 extern bool MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id,
    543                            HandleValue v, ObjectOpResult& result);
    544 
    545 extern bool UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id,
    546                              MutableHandleValue vp);
    547 
    548 extern bool UnmappedArgSetter(JSContext* cx, HandleObject obj, HandleId id,
    549                              HandleValue v, ObjectOpResult& result);
    550 
    551 }  // namespace js
    552 
    553 template <>
    554 inline bool JSObject::is<js::ArgumentsObject>() const {
    555  return is<js::MappedArgumentsObject>() || is<js::UnmappedArgumentsObject>();
    556 }
    557 
    558 #endif /* vm_ArgumentsObject_h */