blob: 4d353d2e77ec5a65827821fb4617564e68a87e8a [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_ArgumentsObject_h
#define vm_ArgumentsObject_h
#include "jsfun.h"
namespace js {
class AbstractFramePtr;
namespace jit {
class IonJSFrameLayout;
}
/*
* ArgumentsData stores the initial indexed arguments provided to the
* corresponding and that function itself. It is used to store arguments[i]
* and arguments.callee -- up until the corresponding property is modified,
* when the relevant value is flagged to memorialize the modification.
*/
struct ArgumentsData
{
/*
* numArgs = Max(numFormalArgs, numActualArgs)
* The array 'args' has numArgs elements.
*/
unsigned numArgs;
/*
* arguments.callee, or MagicValue(JS_OVERWRITTEN_CALLEE) if
* arguments.callee has been modified.
*/
HeapValue callee;
/* The script for the function containing this arguments object. */
JSScript *script;
/*
* Pointer to an array of bits indicating, for every argument in 'slots',
* whether the element has been deleted. See isElementDeleted comment.
*/
size_t *deletedBits;
/*
* This array holds either the current argument value or the magic value
* JS_FORWARD_TO_CALL_OBJECT. The latter means that the function has both a
* CallObject and an ArgumentsObject AND the particular formal variable is
* aliased by the CallObject. In such cases, the CallObject holds the
* canonical value so any element access to the arguments object should
* load the value out of the CallObject (which is pointed to by
* MAYBE_CALL_SLOT).
*/
HeapValue args[1];
/* For jit use: */
static ptrdiff_t offsetOfArgs() { return offsetof(ArgumentsData, args); }
};
/*
* ArgumentsObject instances represent |arguments| objects created to store
* function arguments when a function is called. It's expensive to create such
* objects if they're never used, so they're only created when they are
* potentially used.
*
* Arguments objects are complicated because, for non-strict mode code, they
* must alias any named arguments which were provided to the function. Gnarly
* example:
*
* function f(a, b, c, d)
* {
* arguments[0] = "seta";
* assertEq(a, "seta");
* b = "setb";
* assertEq(arguments[1], "setb");
* c = "setc";
* assertEq(arguments[2], undefined);
* arguments[3] = "setd";
* assertEq(d, undefined);
* }
* f("arga", "argb");
*
* ES5's strict mode behaves more sanely, and named arguments don't alias
* elements of an arguments object.
*
* ArgumentsObject instances use the following reserved slots:
*
* INITIAL_LENGTH_SLOT
* Stores the initial value of arguments.length, plus a bit indicating
* whether arguments.length has been modified. Use initialLength() and
* hasOverriddenLength() to access these values. If arguments.length has
* been modified, then the current value of arguments.length is stored in
* another slot associated with a new property.
* DATA_SLOT
* Stores an ArgumentsData*, described above.
*/
class ArgumentsObject : public JSObject
{
protected:
static const uint32_t INITIAL_LENGTH_SLOT = 0;
static const uint32_t DATA_SLOT = 1;
static const uint32_t MAYBE_CALL_SLOT = 2;
public:
static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
static const uint32_t PACKED_BITS_COUNT = 1;
protected:
template <typename CopyArgs>
static ArgumentsObject *create(JSContext *cx, HandleScript script, HandleFunction callee,
unsigned numActuals, CopyArgs &copy);
inline ArgumentsData *data() const;
public:
static const uint32_t RESERVED_SLOTS = 3;
static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
/* Create an arguments object for a frame that is expecting them. */
static ArgumentsObject *createExpected(JSContext *cx, AbstractFramePtr frame);
/*
* Purposefully disconnect the returned arguments object from the frame
* by always creating a new copy that does not alias formal parameters.
* This allows function-local analysis to determine that formals are
* not aliased and generally simplifies arguments objects.
*/
static ArgumentsObject *createUnexpected(JSContext *cx, ScriptFrameIter &iter);
static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame);
#if defined(JS_ION)
static ArgumentsObject *createForIon(JSContext *cx, jit::IonJSFrameLayout *frame,
HandleObject scopeChain);
#endif
/*
* Return the initial length of the arguments. This may differ from the
* current value of arguments.length!
*/
inline uint32_t initialLength() const;
/* The script for the function containing this arguments object. */
JSScript *containingScript() const;
/* True iff arguments.length has been assigned or its attributes changed. */
inline bool hasOverriddenLength() const;
inline void markLengthOverridden();
/*
* Because the arguments object is a real object, its elements may be
* deleted. This is implemented by setting a 'deleted' flag for the arg
* which is read by argument object resolve and getter/setter hooks.
*
* NB: an element, once deleted, stays deleted. Thus:
*
* function f(x) { delete arguments[0]; arguments[0] = 42; return x }
* assertEq(f(1), 1);
*
* This works because, once a property is deleted from an arguments object,
* it gets regular properties with regular getters/setters that don't alias
* ArgumentsData::slots.
*/
inline bool isElementDeleted(uint32_t i) const;
inline bool isAnyElementDeleted() const;
inline void markElementDeleted(uint32_t i);
/*
* An ArgumentsObject serves two roles:
* - a real object, accessed through regular object operations, e.g..,
* JSObject::getElement corresponding to 'arguments[i]';
* - a VM-internal data structure, storing the value of arguments (formal
* and actual) that are accessed directly by the VM when a reading the
* value of a formal parameter.
* There are two ways to access the ArgumentsData::args corresponding to
* these two use cases:
* - object access should use elements(i) which will take care of
* forwarding when the value is JS_FORWARD_TO_CALL_OBJECT;
* - VM argument access should use arg(i) which will assert that the
* value is not JS_FORWARD_TO_CALL_OBJECT (since, if such forwarding was
* needed, the frontend should have emitted JSOP_GETALIASEDVAR.
*/
inline const Value &element(uint32_t i) const;
inline void setElement(JSContext *cx, uint32_t i, const Value &v);
inline const Value &arg(unsigned i) const;
inline void setArg(unsigned i, const Value &v);
/*
* Attempt to speedily and efficiently access the i-th element of this
* arguments object. Return true if the element was speedily returned.
* Return false if the element must be looked up more slowly using
* getProperty or some similar method. The second overload copies the
* elements [start, start + count) into the locations starting at 'vp'.
*
* NB: Returning false does not indicate error!
*/
inline bool maybeGetElement(uint32_t i, MutableHandleValue vp);
inline bool maybeGetElements(uint32_t start, uint32_t count, js::Value *vp);
/*
* Measures things hanging off this ArgumentsObject that are counted by the
* |miscSize| argument in JSObject::sizeOfExcludingThis().
*/
inline size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
static void finalize(FreeOp *fop, JSObject *obj);
static void trace(JSTracer *trc, JSObject *obj);
/* For jit use: */
static size_t getDataSlotOffset() {
return getFixedSlotOffset(DATA_SLOT);
}
static size_t getInitialLengthSlotOffset() {
return getFixedSlotOffset(INITIAL_LENGTH_SLOT);
}
static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data);
#if defined(JS_ION)
static void MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj,
JSObject *obj, ArgumentsData *data);
#endif
};
class NormalArgumentsObject : public ArgumentsObject
{
public:
static Class class_;
/*
* Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
* been cleared.
*/
inline const js::Value &callee() const;
/* Clear the location storing arguments.callee's initial value. */
inline void clearCallee();
};
class StrictArgumentsObject : public ArgumentsObject
{
public:
static Class class_;
};
} // namespace js
template<>
inline bool
JSObject::is<js::ArgumentsObject>() const
{
return is<js::NormalArgumentsObject>() || is<js::StrictArgumentsObject>();
}
#endif /* vm_ArgumentsObject_h */