LCOV - code coverage report
Current view: top level - js/src/vm - ArgumentsObject.h (source / functions) Hit Total Coverage
Test: output.info Lines: 51 97 52.6 %
Date: 2017-07-14 16:53:18 Functions: 17 32 53.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             : #ifndef vm_ArgumentsObject_h
       8             : #define vm_ArgumentsObject_h
       9             : 
      10             : #include "mozilla/MemoryReporting.h"
      11             : 
      12             : #include "gc/Barrier.h"
      13             : #include "vm/NativeObject.h"
      14             : 
      15             : namespace js {
      16             : 
      17             : class AbstractFramePtr;
      18             : class ScriptFrameIter;
      19             : 
      20             : namespace jit {
      21             : class JitFrameLayout;
      22             : } // namespace jit
      23             : 
      24             : // RareArgumentsData stores the deleted-elements bits for an arguments object.
      25             : // Because |delete arguments[i]| is uncommon, we allocate this data the first
      26             : // time an element is deleted.
      27             : class RareArgumentsData
      28             : {
      29             :     // Pointer to an array of bits indicating, for every argument in
      30             :     // [0, initialLength) whether the element has been deleted. See
      31             :     // ArgumentsObject::isElementDeleted comment.
      32             :     size_t deletedBits_[1];
      33             : 
      34             :     RareArgumentsData() = default;
      35             :     RareArgumentsData(const RareArgumentsData&) = delete;
      36             :     void operator=(const RareArgumentsData&) = delete;
      37             : 
      38             :   public:
      39             :     static RareArgumentsData* create(JSContext* cx, ArgumentsObject* obj);
      40             :     static size_t bytesRequired(size_t numActuals);
      41             : 
      42           0 :     bool isAnyElementDeleted(size_t len) const {
      43           0 :         return IsAnyBitArrayElementSet(deletedBits_, len);
      44             :     }
      45           0 :     bool isElementDeleted(size_t len, size_t i) const {
      46           0 :         MOZ_ASSERT(i < len);
      47           0 :         return IsBitArrayElementSet(deletedBits_, len, i);
      48             :     }
      49           0 :     void markElementDeleted(size_t len, size_t i) {
      50           0 :         MOZ_ASSERT(i < len);
      51           0 :         SetBitArrayElement(deletedBits_, len, i);
      52           0 :     }
      53             : };
      54             : 
      55             : // ArgumentsData stores the initial indexed arguments provided to a function
      56             : // call. It is used to store arguments[i] -- up until the corresponding
      57             : // property is modified, when the relevant value is flagged to memorialize the
      58             : // modification.
      59             : struct ArgumentsData
      60             : {
      61             :     /*
      62             :      * numArgs = Max(numFormalArgs, numActualArgs)
      63             :      * The array 'args' has numArgs elements.
      64             :      */
      65             :     uint32_t    numArgs;
      66             : 
      67             :     RareArgumentsData* rareData;
      68             : 
      69             :     /*
      70             :      * This array holds either the current argument value or the magic
      71             :      * forwarding value. The latter means that the function has both a
      72             :      * CallObject and an ArgumentsObject AND the particular formal variable is
      73             :      * aliased by the CallObject. In such cases, the CallObject holds the
      74             :      * canonical value so any element access to the arguments object should load
      75             :      * the value out of the CallObject (which is pointed to by MAYBE_CALL_SLOT).
      76             :      */
      77             :     GCPtrValue args[1];
      78             : 
      79             :     /* For jit use: */
      80           0 :     static ptrdiff_t offsetOfArgs() { return offsetof(ArgumentsData, args); }
      81             : 
      82             :     /* Iterate args. */
      83           3 :     GCPtrValue* begin() { return args; }
      84             :     const GCPtrValue* begin() const { return args; }
      85             :     GCPtrValue* end() { return args + numArgs; }
      86             :     const GCPtrValue* end() const { return args + numArgs; }
      87             : 
      88         130 :     static size_t bytesRequired(size_t numArgs) {
      89         130 :         return offsetof(ArgumentsData, args) + numArgs * sizeof(Value);
      90             :     }
      91             : };
      92             : 
      93             : // Maximum supported value of arguments.length. This bounds the maximum
      94             : // number of arguments that can be supplied to Function.prototype.apply.
      95             : // This value also bounds the number of elements parsed in an array
      96             : // initializer.
      97             : // NB: keep this in sync with the copy in builtin/SelfHostingDefines.h.
      98             : static const unsigned ARGS_LENGTH_MAX = 500 * 1000;
      99             : 
     100             : /*
     101             :  * ArgumentsObject instances represent |arguments| objects created to store
     102             :  * function arguments when a function is called.  It's expensive to create such
     103             :  * objects if they're never used, so they're only created when they are
     104             :  * potentially used.
     105             :  *
     106             :  * Arguments objects are complicated because, for non-strict mode code, they
     107             :  * must alias any named arguments which were provided to the function.  Gnarly
     108             :  * example:
     109             :  *
     110             :  *   function f(a, b, c, d)
     111             :  *   {
     112             :  *     arguments[0] = "seta";
     113             :  *     assertEq(a, "seta");
     114             :  *     b = "setb";
     115             :  *     assertEq(arguments[1], "setb");
     116             :  *     c = "setc";
     117             :  *     assertEq(arguments[2], undefined);
     118             :  *     arguments[3] = "setd";
     119             :  *     assertEq(d, undefined);
     120             :  *   }
     121             :  *   f("arga", "argb");
     122             :  *
     123             :  * ES5's strict mode behaves more sanely, and named arguments don't alias
     124             :  * elements of an arguments object.
     125             :  *
     126             :  * ArgumentsObject instances use the following reserved slots:
     127             :  *
     128             :  *   INITIAL_LENGTH_SLOT
     129             :  *     Stores the initial value of arguments.length, plus a bit indicating
     130             :  *     whether arguments.length and/or arguments[@@iterator] have been
     131             :  *     modified.  Use initialLength(), hasOverriddenLength(), and
     132             :  *     hasOverriddenIterator() to access these values.  If arguments.length has
     133             :  *     been modified, then the current value of arguments.length is stored in
     134             :  *     another slot associated with a new property.
     135             :  *   DATA_SLOT
     136             :  *     Stores an ArgumentsData*, described above.
     137             :  *   MAYBE_CALL_SLOT
     138             :  *     Stores the CallObject, if the callee has aliased bindings. See
     139             :  *     the ArgumentsData::args comment.
     140             :  *   CALLEE_SLOT
     141             :  *     Stores the initial arguments.callee. This value can be overridden on
     142             :  *     mapped arguments objects, see hasOverriddenCallee.
     143             :  */
     144             : class ArgumentsObject : public NativeObject
     145             : {
     146             :   protected:
     147             :     static const uint32_t INITIAL_LENGTH_SLOT = 0;
     148             :     static const uint32_t DATA_SLOT = 1;
     149             :     static const uint32_t MAYBE_CALL_SLOT = 2;
     150             :     static const uint32_t CALLEE_SLOT = 3;
     151             : 
     152             :   public:
     153             :     static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
     154             :     static const uint32_t ITERATOR_OVERRIDDEN_BIT = 0x2;
     155             :     static const uint32_t ELEMENT_OVERRIDDEN_BIT = 0x4;
     156             :     static const uint32_t CALLEE_OVERRIDDEN_BIT = 0x8;
     157             :     static const uint32_t PACKED_BITS_COUNT = 4;
     158             : 
     159             :     static_assert(ARGS_LENGTH_MAX <= (UINT32_MAX >> PACKED_BITS_COUNT),
     160             :                   "Max arguments length must fit in available bits");
     161             : 
     162             :   protected:
     163             :     template <typename CopyArgs>
     164             :     static ArgumentsObject* create(JSContext* cx, HandleFunction callee, unsigned numActuals,
     165             :                                    CopyArgs& copy);
     166             : 
     167        1385 :     ArgumentsData* data() const {
     168        1385 :         return reinterpret_cast<ArgumentsData*>(getFixedSlot(DATA_SLOT).toPrivate());
     169             :     }
     170             : 
     171         555 :     RareArgumentsData* maybeRareData() const {
     172         555 :         return data()->rareData;
     173             :     }
     174             : 
     175             :     MOZ_MUST_USE bool createRareData(JSContext* cx);
     176             : 
     177           0 :     RareArgumentsData* getOrCreateRareData(JSContext* cx) {
     178           0 :         if (!data()->rareData && !createRareData(cx))
     179           0 :             return nullptr;
     180           0 :         return data()->rareData;
     181             :     }
     182             : 
     183             :     static bool obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
     184             :                                 ObjectOpResult& result);
     185             : 
     186             :     static bool obj_mayResolve(const JSAtomState& names, jsid id, JSObject*);
     187             : 
     188             :   public:
     189             :     static const uint32_t RESERVED_SLOTS = 4;
     190             :     static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
     191             : 
     192             :     /* Create an arguments object for a frame that is expecting them. */
     193             :     static ArgumentsObject* createExpected(JSContext* cx, AbstractFramePtr frame);
     194             : 
     195             :     /*
     196             :      * Purposefully disconnect the returned arguments object from the frame
     197             :      * by always creating a new copy that does not alias formal parameters.
     198             :      * This allows function-local analysis to determine that formals are
     199             :      * not aliased and generally simplifies arguments objects.
     200             :      */
     201             :     static ArgumentsObject* createUnexpected(JSContext* cx, ScriptFrameIter& iter);
     202             :     static ArgumentsObject* createUnexpected(JSContext* cx, AbstractFramePtr frame);
     203             :     static ArgumentsObject* createForIon(JSContext* cx, jit::JitFrameLayout* frame,
     204             :                                          HandleObject scopeChain);
     205             : 
     206             :     /*
     207             :      * Allocate ArgumentsData and fill reserved slots after allocating an
     208             :      * ArgumentsObject in Ion code.
     209             :      */
     210             :     static ArgumentsObject* finishForIon(JSContext* cx, jit::JitFrameLayout* frame,
     211             :                                          JSObject* scopeChain, ArgumentsObject* obj);
     212             : 
     213             :     static ArgumentsObject* createTemplateObject(JSContext* cx, bool mapped);
     214             : 
     215             :     /*
     216             :      * Return the initial length of the arguments.  This may differ from the
     217             :      * current value of arguments.length!
     218             :      */
     219        1130 :     uint32_t initialLength() const {
     220        1130 :         uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
     221        1130 :         MOZ_ASSERT(argc <= ARGS_LENGTH_MAX);
     222        1130 :         return argc;
     223             :     }
     224             : 
     225             :     /* True iff arguments.length has been assigned or its attributes changed. */
     226         307 :     bool hasOverriddenLength() const {
     227         307 :         const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT);
     228         307 :         return v.toInt32() & LENGTH_OVERRIDDEN_BIT;
     229             :     }
     230             : 
     231          17 :     void markLengthOverridden() {
     232          17 :         uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT;
     233          17 :         setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
     234          17 :     }
     235             : 
     236             :     /*
     237             :      * Create the default "length" property and set LENGTH_OVERRIDDEN_BIT.
     238             :      */
     239             :     static bool reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj);
     240             : 
     241             :     /* True iff arguments[@@iterator] has been assigned or its attributes
     242             :      * changed. */
     243           0 :     bool hasOverriddenIterator() const {
     244           0 :         const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT);
     245           0 :         return v.toInt32() & ITERATOR_OVERRIDDEN_BIT;
     246             :     }
     247             : 
     248           0 :     void markIteratorOverridden() {
     249           0 :         uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | ITERATOR_OVERRIDDEN_BIT;
     250           0 :         setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
     251           0 :     }
     252             : 
     253             :     /*
     254             :      * Create the default @@iterator property and set ITERATOR_OVERRIDDEN_BIT.
     255             :      */
     256             :     static bool reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj);
     257             : 
     258             :     /* True iff any element has been assigned or its attributes
     259             :      * changed. */
     260           0 :     bool hasOverriddenElement() const {
     261           0 :         const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT);
     262           0 :         return v.toInt32() & ELEMENT_OVERRIDDEN_BIT;
     263             :     }
     264             : 
     265          17 :     void markElementOverridden() {
     266          17 :         uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | ELEMENT_OVERRIDDEN_BIT;
     267          17 :         setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
     268          17 :     }
     269             : 
     270             :     /*
     271             :      * Because the arguments object is a real object, its elements may be
     272             :      * deleted. This is implemented by setting a 'deleted' flag for the arg
     273             :      * which is read by argument object resolve and getter/setter hooks.
     274             :      *
     275             :      * NB: an element, once deleted, stays deleted. Thus:
     276             :      *
     277             :      *   function f(x) { delete arguments[0]; arguments[0] = 42; return x }
     278             :      *   assertEq(f(1), 1);
     279             :      *
     280             :      * This works because, once a property is deleted from an arguments object,
     281             :      * it gets regular properties with regular getters/setters that don't alias
     282             :      * ArgumentsData::slots.
     283             :      */
     284         552 :     bool isElementDeleted(uint32_t i) const {
     285         552 :         MOZ_ASSERT(i < data()->numArgs);
     286         552 :         if (i >= initialLength())
     287           0 :             return false;
     288         552 :         return maybeRareData() && maybeRareData()->isElementDeleted(initialLength(), i);
     289             :     }
     290             : 
     291           0 :     bool isAnyElementDeleted() const {
     292           0 :         return maybeRareData() && maybeRareData()->isAnyElementDeleted(initialLength());
     293             :     }
     294             : 
     295             :     bool markElementDeleted(JSContext* cx, uint32_t i);
     296             : 
     297             :     /*
     298             :      * An ArgumentsObject serves two roles:
     299             :      *  - a real object, accessed through regular object operations, e.g..,
     300             :      *    GetElement corresponding to 'arguments[i]';
     301             :      *  - a VM-internal data structure, storing the value of arguments (formal
     302             :      *    and actual) that are accessed directly by the VM when a reading the
     303             :      *    value of a formal parameter.
     304             :      * There are two ways to access the ArgumentsData::args corresponding to
     305             :      * these two use cases:
     306             :      *  - object access should use elements(i) which will take care of
     307             :      *    forwarding when the value is the magic forwarding value;
     308             :      *  - VM argument access should use arg(i) which will assert that the
     309             :      *    value is not the magic forwarding value (since, if such forwarding was
     310             :      *    needed, the frontend should have emitted JSOP_GETALIASEDVAR).
     311             :      */
     312             :     const Value& element(uint32_t i) const;
     313             : 
     314             :     inline void setElement(JSContext* cx, uint32_t i, const Value& v);
     315             : 
     316           7 :     const Value& arg(unsigned i) const {
     317           7 :         MOZ_ASSERT(i < data()->numArgs);
     318           7 :         const Value& v = data()->args[i];
     319           7 :         MOZ_ASSERT(!v.isMagic());
     320           7 :         return v;
     321             :     }
     322             : 
     323           2 :     void setArg(unsigned i, const Value& v) {
     324           2 :         MOZ_ASSERT(i < data()->numArgs);
     325           2 :         GCPtrValue& lhs = data()->args[i];
     326           2 :         MOZ_ASSERT(!lhs.isMagic());
     327           2 :         lhs = v;
     328           2 :     }
     329             : 
     330             :     /*
     331             :      * Attempt to speedily and efficiently access the i-th element of this
     332             :      * arguments object.  Return true if the element was speedily returned.
     333             :      * Return false if the element must be looked up more slowly using
     334             :      * getProperty or some similar method. The second overload copies the
     335             :      * elements [start, start + count) into the locations starting at 'vp'.
     336             :      *
     337             :      * NB: Returning false does not indicate error!
     338             :      */
     339          75 :     bool maybeGetElement(uint32_t i, MutableHandleValue vp) {
     340          75 :         if (i >= initialLength() || isElementDeleted(i))
     341           0 :             return false;
     342          75 :         vp.set(element(i));
     343          75 :         return true;
     344             :     }
     345             : 
     346             :     inline bool maybeGetElements(uint32_t start, uint32_t count, js::Value* vp);
     347             : 
     348             :     /*
     349             :      * Measures things hanging off this ArgumentsObject that are counted by the
     350             :      * |miscSize| argument in JSObject::sizeOfExcludingThis().
     351             :      */
     352           0 :     size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const {
     353           0 :         if (!data()) // Template arguments objects have no data.
     354           0 :             return 0;
     355           0 :         return mallocSizeOf(data()) + mallocSizeOf(maybeRareData());
     356             :     }
     357           0 :     size_t sizeOfData() const {
     358           0 :         return ArgumentsData::bytesRequired(data()->numArgs) +
     359           0 :                (maybeRareData() ? RareArgumentsData::bytesRequired(initialLength()) : 0);
     360             :     }
     361             : 
     362             :     static void finalize(FreeOp* fop, JSObject* obj);
     363             :     static void trace(JSTracer* trc, JSObject* obj);
     364             :     static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
     365             : 
     366             :     /* For jit use: */
     367           0 :     static size_t getDataSlotOffset() {
     368           0 :         return getFixedSlotOffset(DATA_SLOT);
     369             :     }
     370           1 :     static size_t getInitialLengthSlotOffset() {
     371           1 :         return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
     372             :     }
     373             : 
     374          14 :     static Value MagicEnvSlotValue(uint32_t slot) {
     375             :         // When forwarding slots to a backing CallObject, the slot numbers are
     376             :         // stored as uint32 magic values. This raises an ambiguity if we have
     377             :         // also copied JS_OPTIMIZED_OUT magic from a JIT frame or
     378             :         // JS_UNINITIALIZED_LEXICAL magic on the CallObject. To distinguish
     379             :         // normal magic values (those with a JSWhyMagic) and uint32 magic
     380             :         // values, we add the maximum JSWhyMagic value to the slot
     381             :         // number. This is safe as ARGS_LENGTH_MAX is well below UINT32_MAX.
     382             :         JS_STATIC_ASSERT(UINT32_MAX - JS_WHY_MAGIC_COUNT > ARGS_LENGTH_MAX);
     383          14 :         return JS::MagicValueUint32(slot + JS_WHY_MAGIC_COUNT);
     384             :     }
     385           0 :     static uint32_t SlotFromMagicScopeSlotValue(const Value& v) {
     386             :         JS_STATIC_ASSERT(UINT32_MAX - JS_WHY_MAGIC_COUNT > ARGS_LENGTH_MAX);
     387           0 :         return v.magicUint32() - JS_WHY_MAGIC_COUNT;
     388             :     }
     389         242 :     static bool IsMagicScopeSlotValue(const Value& v) {
     390         242 :         return v.isMagic() && v.magicUint32() > JS_WHY_MAGIC_COUNT;
     391             :     }
     392             : 
     393             :     static void MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObject* obj,
     394             :                                          ArgumentsData* data);
     395             :     static void MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObject callObj,
     396             :                                          ArgumentsObject* obj, ArgumentsData* data);
     397             : };
     398             : 
     399             : class MappedArgumentsObject : public ArgumentsObject
     400             : {
     401             :     static const ClassOps classOps_;
     402             :     static const ObjectOps objectOps_;
     403             : 
     404             :   public:
     405             :     static const Class class_;
     406             : 
     407          66 :     JSFunction& callee() const {
     408          66 :         return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
     409             :     }
     410             : 
     411           0 :     bool hasOverriddenCallee() const {
     412           0 :         const Value& v = getFixedSlot(INITIAL_LENGTH_SLOT);
     413           0 :         return v.toInt32() & CALLEE_OVERRIDDEN_BIT;
     414             :     }
     415             : 
     416           0 :     void markCalleeOverridden() {
     417           0 :         uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | CALLEE_OVERRIDDEN_BIT;
     418           0 :         setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
     419           0 :     }
     420             : 
     421             :   private:
     422             :     static bool obj_enumerate(JSContext* cx, HandleObject obj);
     423             :     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
     424             :     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
     425             :                                    Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result);
     426             : };
     427             : 
     428             : class UnmappedArgumentsObject : public ArgumentsObject
     429             : {
     430             :     static const ClassOps classOps_;
     431             : 
     432             :   public:
     433             :     static const Class class_;
     434             : 
     435             :   private:
     436             :     static bool obj_enumerate(JSContext* cx, HandleObject obj);
     437             :     static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp);
     438             : };
     439             : 
     440             : } // namespace js
     441             : 
     442             : template<>
     443             : inline bool
     444      141565 : JSObject::is<js::ArgumentsObject>() const
     445             : {
     446      141565 :     return is<js::MappedArgumentsObject>() || is<js::UnmappedArgumentsObject>();
     447             : }
     448             : 
     449             : #endif /* vm_ArgumentsObject_h */

Generated by: LCOV version 1.13