js/src/vm/ArgumentsObject.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 #ifndef vm_ArgumentsObject_h
     8 #define vm_ArgumentsObject_h
    10 #include "mozilla/MemoryReporting.h"
    12 #include "jsobj.h"
    14 #include "gc/Barrier.h"
    16 namespace js {
    18 class AbstractFramePtr;
    19 class ScriptFrameIter;
    21 namespace jit {
    22 class IonJSFrameLayout;
    23 }
    25 /*
    26  * ArgumentsData stores the initial indexed arguments provided to the
    27  * corresponding and that function itself.  It is used to store arguments[i]
    28  * and arguments.callee -- up until the corresponding property is modified,
    29  * when the relevant value is flagged to memorialize the modification.
    30  */
    31 struct ArgumentsData
    32 {
    33     /*
    34      * numArgs = Max(numFormalArgs, numActualArgs)
    35      * The array 'args' has numArgs elements.
    36      */
    37     unsigned    numArgs;
    39     /*
    40      * arguments.callee, or MagicValue(JS_OVERWRITTEN_CALLEE) if
    41      * arguments.callee has been modified.
    42      */
    43     HeapValue   callee;
    45     /* The script for the function containing this arguments object. */
    46     JSScript    *script;
    48     /*
    49      * Pointer to an array of bits indicating, for every argument in 'slots',
    50      * whether the element has been deleted. See isElementDeleted comment.
    51      */
    52     size_t      *deletedBits;
    54     /*
    55      * This array holds either the current argument value or the magic
    56      * forwarding value. The latter means that the function has both a
    57      * CallObject and an ArgumentsObject AND the particular formal variable is
    58      * aliased by the CallObject. In such cases, the CallObject holds the
    59      * canonical value so any element access to the arguments object should load
    60      * the value out of the CallObject (which is pointed to by MAYBE_CALL_SLOT).
    61      */
    62     HeapValue   args[1];
    64     /* For jit use: */
    65     static ptrdiff_t offsetOfArgs() { return offsetof(ArgumentsData, args); }
    66 };
    68 // Maximum supported value of arguments.length. This bounds the maximum
    69 // number of arguments that can be supplied to Function.prototype.apply.
    70 // This value also bounds the number of elements parsed in an array
    71 // initialiser.
    72 static const unsigned ARGS_LENGTH_MAX = 500 * 1000;
    74 /*
    75  * ArgumentsObject instances represent |arguments| objects created to store
    76  * function arguments when a function is called.  It's expensive to create such
    77  * objects if they're never used, so they're only created when they are
    78  * potentially used.
    79  *
    80  * Arguments objects are complicated because, for non-strict mode code, they
    81  * must alias any named arguments which were provided to the function.  Gnarly
    82  * example:
    83  *
    84  *   function f(a, b, c, d)
    85  *   {
    86  *     arguments[0] = "seta";
    87  *     assertEq(a, "seta");
    88  *     b = "setb";
    89  *     assertEq(arguments[1], "setb");
    90  *     c = "setc";
    91  *     assertEq(arguments[2], undefined);
    92  *     arguments[3] = "setd";
    93  *     assertEq(d, undefined);
    94  *   }
    95  *   f("arga", "argb");
    96  *
    97  * ES5's strict mode behaves more sanely, and named arguments don't alias
    98  * elements of an arguments object.
    99  *
   100  * ArgumentsObject instances use the following reserved slots:
   101  *
   102  *   INITIAL_LENGTH_SLOT
   103  *     Stores the initial value of arguments.length, plus a bit indicating
   104  *     whether arguments.length has been modified.  Use initialLength() and
   105  *     hasOverriddenLength() to access these values.  If arguments.length has
   106  *     been modified, then the current value of arguments.length is stored in
   107  *     another slot associated with a new property.
   108  *   DATA_SLOT
   109  *     Stores an ArgumentsData*, described above.
   110  */
   111 class ArgumentsObject : public JSObject
   112 {
   113   protected:
   114     static const uint32_t INITIAL_LENGTH_SLOT = 0;
   115     static const uint32_t DATA_SLOT = 1;
   116     static const uint32_t MAYBE_CALL_SLOT = 2;
   118   public:
   119     static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
   120     static const uint32_t PACKED_BITS_COUNT = 1;
   122   protected:
   123     template <typename CopyArgs>
   124     static ArgumentsObject *create(JSContext *cx, HandleScript script, HandleFunction callee,
   125                                    unsigned numActuals, CopyArgs &copy);
   127     ArgumentsData *data() const {
   128         return reinterpret_cast<ArgumentsData *>(getFixedSlot(DATA_SLOT).toPrivate());
   129     }
   131   public:
   132     static const uint32_t RESERVED_SLOTS = 3;
   133     static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
   135     /* Create an arguments object for a frame that is expecting them. */
   136     static ArgumentsObject *createExpected(JSContext *cx, AbstractFramePtr frame);
   138     /*
   139      * Purposefully disconnect the returned arguments object from the frame
   140      * by always creating a new copy that does not alias formal parameters.
   141      * This allows function-local analysis to determine that formals are
   142      * not aliased and generally simplifies arguments objects.
   143      */
   144     static ArgumentsObject *createUnexpected(JSContext *cx, ScriptFrameIter &iter);
   145     static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame);
   146 #if defined(JS_ION)
   147     static ArgumentsObject *createForIon(JSContext *cx, jit::IonJSFrameLayout *frame,
   148                                          HandleObject scopeChain);
   149 #endif
   151     /*
   152      * Return the initial length of the arguments.  This may differ from the
   153      * current value of arguments.length!
   154      */
   155     uint32_t initialLength() const {
   156         uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
   157         JS_ASSERT(argc <= ARGS_LENGTH_MAX);
   158         return argc;
   159     }
   161     /* The script for the function containing this arguments object. */
   162     JSScript *containingScript() const {
   163         return data()->script;
   164     }
   166     /* True iff arguments.length has been assigned or its attributes changed. */
   167     bool hasOverriddenLength() const {
   168         const Value &v = getFixedSlot(INITIAL_LENGTH_SLOT);
   169         return v.toInt32() & LENGTH_OVERRIDDEN_BIT;
   170     }
   172     void markLengthOverridden() {
   173         uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT;
   174         setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v));
   175     }
   177     /*
   178      * Because the arguments object is a real object, its elements may be
   179      * deleted. This is implemented by setting a 'deleted' flag for the arg
   180      * which is read by argument object resolve and getter/setter hooks.
   181      *
   182      * NB: an element, once deleted, stays deleted. Thus:
   183      *
   184      *   function f(x) { delete arguments[0]; arguments[0] = 42; return x }
   185      *   assertEq(f(1), 1);
   186      *
   187      * This works because, once a property is deleted from an arguments object,
   188      * it gets regular properties with regular getters/setters that don't alias
   189      * ArgumentsData::slots.
   190      */
   191     bool isElementDeleted(uint32_t i) const {
   192         JS_ASSERT(i < data()->numArgs);
   193         if (i >= initialLength())
   194             return false;
   195         return IsBitArrayElementSet(data()->deletedBits, initialLength(), i);
   196     }
   198     bool isAnyElementDeleted() const {
   199         return IsAnyBitArrayElementSet(data()->deletedBits, initialLength());
   200     }
   202     void markElementDeleted(uint32_t i) {
   203         SetBitArrayElement(data()->deletedBits, initialLength(), i);
   204     }
   206     /*
   207      * An ArgumentsObject serves two roles:
   208      *  - a real object, accessed through regular object operations, e.g..,
   209      *    JSObject::getElement corresponding to 'arguments[i]';
   210      *  - a VM-internal data structure, storing the value of arguments (formal
   211      *    and actual) that are accessed directly by the VM when a reading the
   212      *    value of a formal parameter.
   213      * There are two ways to access the ArgumentsData::args corresponding to
   214      * these two use cases:
   215      *  - object access should use elements(i) which will take care of
   216      *    forwarding when the value is the magic forwarding value;
   217      *  - VM argument access should use arg(i) which will assert that the
   218      *    value is not the magic forwarding value (since, if such forwarding was
   219      *    needed, the frontend should have emitted JSOP_GETALIASEDVAR).
   220      */
   221     const Value &element(uint32_t i) const;
   223     inline void setElement(JSContext *cx, uint32_t i, const Value &v);
   225     const Value &arg(unsigned i) const {
   226         JS_ASSERT(i < data()->numArgs);
   227         const Value &v = data()->args[i];
   228         JS_ASSERT(!v.isMagic());
   229         return v;
   230     }
   232     void setArg(unsigned i, const Value &v) {
   233         JS_ASSERT(i < data()->numArgs);
   234         HeapValue &lhs = data()->args[i];
   235         JS_ASSERT(!lhs.isMagic());
   236         lhs = v;
   237     }
   239     /*
   240      * Attempt to speedily and efficiently access the i-th element of this
   241      * arguments object.  Return true if the element was speedily returned.
   242      * Return false if the element must be looked up more slowly using
   243      * getProperty or some similar method. The second overload copies the
   244      * elements [start, start + count) into the locations starting at 'vp'.
   245      *
   246      * NB: Returning false does not indicate error!
   247      */
   248     bool maybeGetElement(uint32_t i, MutableHandleValue vp) {
   249         if (i >= initialLength() || isElementDeleted(i))
   250             return false;
   251         vp.set(element(i));
   252         return true;
   253     }
   255     inline bool maybeGetElements(uint32_t start, uint32_t count, js::Value *vp);
   257     /*
   258      * Measures things hanging off this ArgumentsObject that are counted by the
   259      * |miscSize| argument in JSObject::sizeOfExcludingThis().
   260      */
   261     size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const {
   262         return mallocSizeOf(data());
   263     }
   265     static void finalize(FreeOp *fop, JSObject *obj);
   266     static void trace(JSTracer *trc, JSObject *obj);
   268     /* For jit use: */
   269     static size_t getDataSlotOffset() {
   270         return getFixedSlotOffset(DATA_SLOT);
   271     }
   272     static size_t getInitialLengthSlotOffset() {
   273         return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
   274     }
   276     static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data);
   277 #if defined(JS_ION)
   278     static void MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj,
   279                                          JSObject *obj, ArgumentsData *data);
   280 #endif
   281 };
   283 class NormalArgumentsObject : public ArgumentsObject
   284 {
   285   public:
   286     static const Class class_;
   288     /*
   289      * Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
   290      * been cleared.
   291      */
   292     const js::Value &callee() const {
   293         return data()->callee;
   294     }
   296     /* Clear the location storing arguments.callee's initial value. */
   297     void clearCallee() {
   298         data()->callee.set(zone(), MagicValue(JS_OVERWRITTEN_CALLEE));
   299     }
   300 };
   302 class StrictArgumentsObject : public ArgumentsObject
   303 {
   304   public:
   305     static const Class class_;
   306 };
   308 } // namespace js
   310 template<>
   311 inline bool
   312 JSObject::is<js::ArgumentsObject>() const
   313 {
   314     return is<js::NormalArgumentsObject>() || is<js::StrictArgumentsObject>();
   315 }
   317 #endif /* vm_ArgumentsObject_h */

mercurial