1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/ArgumentsObject.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,317 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef vm_ArgumentsObject_h 1.11 +#define vm_ArgumentsObject_h 1.12 + 1.13 +#include "mozilla/MemoryReporting.h" 1.14 + 1.15 +#include "jsobj.h" 1.16 + 1.17 +#include "gc/Barrier.h" 1.18 + 1.19 +namespace js { 1.20 + 1.21 +class AbstractFramePtr; 1.22 +class ScriptFrameIter; 1.23 + 1.24 +namespace jit { 1.25 +class IonJSFrameLayout; 1.26 +} 1.27 + 1.28 +/* 1.29 + * ArgumentsData stores the initial indexed arguments provided to the 1.30 + * corresponding and that function itself. It is used to store arguments[i] 1.31 + * and arguments.callee -- up until the corresponding property is modified, 1.32 + * when the relevant value is flagged to memorialize the modification. 1.33 + */ 1.34 +struct ArgumentsData 1.35 +{ 1.36 + /* 1.37 + * numArgs = Max(numFormalArgs, numActualArgs) 1.38 + * The array 'args' has numArgs elements. 1.39 + */ 1.40 + unsigned numArgs; 1.41 + 1.42 + /* 1.43 + * arguments.callee, or MagicValue(JS_OVERWRITTEN_CALLEE) if 1.44 + * arguments.callee has been modified. 1.45 + */ 1.46 + HeapValue callee; 1.47 + 1.48 + /* The script for the function containing this arguments object. */ 1.49 + JSScript *script; 1.50 + 1.51 + /* 1.52 + * Pointer to an array of bits indicating, for every argument in 'slots', 1.53 + * whether the element has been deleted. See isElementDeleted comment. 1.54 + */ 1.55 + size_t *deletedBits; 1.56 + 1.57 + /* 1.58 + * This array holds either the current argument value or the magic 1.59 + * forwarding value. The latter means that the function has both a 1.60 + * CallObject and an ArgumentsObject AND the particular formal variable is 1.61 + * aliased by the CallObject. In such cases, the CallObject holds the 1.62 + * canonical value so any element access to the arguments object should load 1.63 + * the value out of the CallObject (which is pointed to by MAYBE_CALL_SLOT). 1.64 + */ 1.65 + HeapValue args[1]; 1.66 + 1.67 + /* For jit use: */ 1.68 + static ptrdiff_t offsetOfArgs() { return offsetof(ArgumentsData, args); } 1.69 +}; 1.70 + 1.71 +// Maximum supported value of arguments.length. This bounds the maximum 1.72 +// number of arguments that can be supplied to Function.prototype.apply. 1.73 +// This value also bounds the number of elements parsed in an array 1.74 +// initialiser. 1.75 +static const unsigned ARGS_LENGTH_MAX = 500 * 1000; 1.76 + 1.77 +/* 1.78 + * ArgumentsObject instances represent |arguments| objects created to store 1.79 + * function arguments when a function is called. It's expensive to create such 1.80 + * objects if they're never used, so they're only created when they are 1.81 + * potentially used. 1.82 + * 1.83 + * Arguments objects are complicated because, for non-strict mode code, they 1.84 + * must alias any named arguments which were provided to the function. Gnarly 1.85 + * example: 1.86 + * 1.87 + * function f(a, b, c, d) 1.88 + * { 1.89 + * arguments[0] = "seta"; 1.90 + * assertEq(a, "seta"); 1.91 + * b = "setb"; 1.92 + * assertEq(arguments[1], "setb"); 1.93 + * c = "setc"; 1.94 + * assertEq(arguments[2], undefined); 1.95 + * arguments[3] = "setd"; 1.96 + * assertEq(d, undefined); 1.97 + * } 1.98 + * f("arga", "argb"); 1.99 + * 1.100 + * ES5's strict mode behaves more sanely, and named arguments don't alias 1.101 + * elements of an arguments object. 1.102 + * 1.103 + * ArgumentsObject instances use the following reserved slots: 1.104 + * 1.105 + * INITIAL_LENGTH_SLOT 1.106 + * Stores the initial value of arguments.length, plus a bit indicating 1.107 + * whether arguments.length has been modified. Use initialLength() and 1.108 + * hasOverriddenLength() to access these values. If arguments.length has 1.109 + * been modified, then the current value of arguments.length is stored in 1.110 + * another slot associated with a new property. 1.111 + * DATA_SLOT 1.112 + * Stores an ArgumentsData*, described above. 1.113 + */ 1.114 +class ArgumentsObject : public JSObject 1.115 +{ 1.116 + protected: 1.117 + static const uint32_t INITIAL_LENGTH_SLOT = 0; 1.118 + static const uint32_t DATA_SLOT = 1; 1.119 + static const uint32_t MAYBE_CALL_SLOT = 2; 1.120 + 1.121 + public: 1.122 + static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1; 1.123 + static const uint32_t PACKED_BITS_COUNT = 1; 1.124 + 1.125 + protected: 1.126 + template <typename CopyArgs> 1.127 + static ArgumentsObject *create(JSContext *cx, HandleScript script, HandleFunction callee, 1.128 + unsigned numActuals, CopyArgs ©); 1.129 + 1.130 + ArgumentsData *data() const { 1.131 + return reinterpret_cast<ArgumentsData *>(getFixedSlot(DATA_SLOT).toPrivate()); 1.132 + } 1.133 + 1.134 + public: 1.135 + static const uint32_t RESERVED_SLOTS = 3; 1.136 + static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND; 1.137 + 1.138 + /* Create an arguments object for a frame that is expecting them. */ 1.139 + static ArgumentsObject *createExpected(JSContext *cx, AbstractFramePtr frame); 1.140 + 1.141 + /* 1.142 + * Purposefully disconnect the returned arguments object from the frame 1.143 + * by always creating a new copy that does not alias formal parameters. 1.144 + * This allows function-local analysis to determine that formals are 1.145 + * not aliased and generally simplifies arguments objects. 1.146 + */ 1.147 + static ArgumentsObject *createUnexpected(JSContext *cx, ScriptFrameIter &iter); 1.148 + static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame); 1.149 +#if defined(JS_ION) 1.150 + static ArgumentsObject *createForIon(JSContext *cx, jit::IonJSFrameLayout *frame, 1.151 + HandleObject scopeChain); 1.152 +#endif 1.153 + 1.154 + /* 1.155 + * Return the initial length of the arguments. This may differ from the 1.156 + * current value of arguments.length! 1.157 + */ 1.158 + uint32_t initialLength() const { 1.159 + uint32_t argc = uint32_t(getFixedSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT; 1.160 + JS_ASSERT(argc <= ARGS_LENGTH_MAX); 1.161 + return argc; 1.162 + } 1.163 + 1.164 + /* The script for the function containing this arguments object. */ 1.165 + JSScript *containingScript() const { 1.166 + return data()->script; 1.167 + } 1.168 + 1.169 + /* True iff arguments.length has been assigned or its attributes changed. */ 1.170 + bool hasOverriddenLength() const { 1.171 + const Value &v = getFixedSlot(INITIAL_LENGTH_SLOT); 1.172 + return v.toInt32() & LENGTH_OVERRIDDEN_BIT; 1.173 + } 1.174 + 1.175 + void markLengthOverridden() { 1.176 + uint32_t v = getFixedSlot(INITIAL_LENGTH_SLOT).toInt32() | LENGTH_OVERRIDDEN_BIT; 1.177 + setFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(v)); 1.178 + } 1.179 + 1.180 + /* 1.181 + * Because the arguments object is a real object, its elements may be 1.182 + * deleted. This is implemented by setting a 'deleted' flag for the arg 1.183 + * which is read by argument object resolve and getter/setter hooks. 1.184 + * 1.185 + * NB: an element, once deleted, stays deleted. Thus: 1.186 + * 1.187 + * function f(x) { delete arguments[0]; arguments[0] = 42; return x } 1.188 + * assertEq(f(1), 1); 1.189 + * 1.190 + * This works because, once a property is deleted from an arguments object, 1.191 + * it gets regular properties with regular getters/setters that don't alias 1.192 + * ArgumentsData::slots. 1.193 + */ 1.194 + bool isElementDeleted(uint32_t i) const { 1.195 + JS_ASSERT(i < data()->numArgs); 1.196 + if (i >= initialLength()) 1.197 + return false; 1.198 + return IsBitArrayElementSet(data()->deletedBits, initialLength(), i); 1.199 + } 1.200 + 1.201 + bool isAnyElementDeleted() const { 1.202 + return IsAnyBitArrayElementSet(data()->deletedBits, initialLength()); 1.203 + } 1.204 + 1.205 + void markElementDeleted(uint32_t i) { 1.206 + SetBitArrayElement(data()->deletedBits, initialLength(), i); 1.207 + } 1.208 + 1.209 + /* 1.210 + * An ArgumentsObject serves two roles: 1.211 + * - a real object, accessed through regular object operations, e.g.., 1.212 + * JSObject::getElement corresponding to 'arguments[i]'; 1.213 + * - a VM-internal data structure, storing the value of arguments (formal 1.214 + * and actual) that are accessed directly by the VM when a reading the 1.215 + * value of a formal parameter. 1.216 + * There are two ways to access the ArgumentsData::args corresponding to 1.217 + * these two use cases: 1.218 + * - object access should use elements(i) which will take care of 1.219 + * forwarding when the value is the magic forwarding value; 1.220 + * - VM argument access should use arg(i) which will assert that the 1.221 + * value is not the magic forwarding value (since, if such forwarding was 1.222 + * needed, the frontend should have emitted JSOP_GETALIASEDVAR). 1.223 + */ 1.224 + const Value &element(uint32_t i) const; 1.225 + 1.226 + inline void setElement(JSContext *cx, uint32_t i, const Value &v); 1.227 + 1.228 + const Value &arg(unsigned i) const { 1.229 + JS_ASSERT(i < data()->numArgs); 1.230 + const Value &v = data()->args[i]; 1.231 + JS_ASSERT(!v.isMagic()); 1.232 + return v; 1.233 + } 1.234 + 1.235 + void setArg(unsigned i, const Value &v) { 1.236 + JS_ASSERT(i < data()->numArgs); 1.237 + HeapValue &lhs = data()->args[i]; 1.238 + JS_ASSERT(!lhs.isMagic()); 1.239 + lhs = v; 1.240 + } 1.241 + 1.242 + /* 1.243 + * Attempt to speedily and efficiently access the i-th element of this 1.244 + * arguments object. Return true if the element was speedily returned. 1.245 + * Return false if the element must be looked up more slowly using 1.246 + * getProperty or some similar method. The second overload copies the 1.247 + * elements [start, start + count) into the locations starting at 'vp'. 1.248 + * 1.249 + * NB: Returning false does not indicate error! 1.250 + */ 1.251 + bool maybeGetElement(uint32_t i, MutableHandleValue vp) { 1.252 + if (i >= initialLength() || isElementDeleted(i)) 1.253 + return false; 1.254 + vp.set(element(i)); 1.255 + return true; 1.256 + } 1.257 + 1.258 + inline bool maybeGetElements(uint32_t start, uint32_t count, js::Value *vp); 1.259 + 1.260 + /* 1.261 + * Measures things hanging off this ArgumentsObject that are counted by the 1.262 + * |miscSize| argument in JSObject::sizeOfExcludingThis(). 1.263 + */ 1.264 + size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const { 1.265 + return mallocSizeOf(data()); 1.266 + } 1.267 + 1.268 + static void finalize(FreeOp *fop, JSObject *obj); 1.269 + static void trace(JSTracer *trc, JSObject *obj); 1.270 + 1.271 + /* For jit use: */ 1.272 + static size_t getDataSlotOffset() { 1.273 + return getFixedSlotOffset(DATA_SLOT); 1.274 + } 1.275 + static size_t getInitialLengthSlotOffset() { 1.276 + return getFixedSlotOffset(INITIAL_LENGTH_SLOT); 1.277 + } 1.278 + 1.279 + static void MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, ArgumentsData *data); 1.280 +#if defined(JS_ION) 1.281 + static void MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj, 1.282 + JSObject *obj, ArgumentsData *data); 1.283 +#endif 1.284 +}; 1.285 + 1.286 +class NormalArgumentsObject : public ArgumentsObject 1.287 +{ 1.288 + public: 1.289 + static const Class class_; 1.290 + 1.291 + /* 1.292 + * Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has 1.293 + * been cleared. 1.294 + */ 1.295 + const js::Value &callee() const { 1.296 + return data()->callee; 1.297 + } 1.298 + 1.299 + /* Clear the location storing arguments.callee's initial value. */ 1.300 + void clearCallee() { 1.301 + data()->callee.set(zone(), MagicValue(JS_OVERWRITTEN_CALLEE)); 1.302 + } 1.303 +}; 1.304 + 1.305 +class StrictArgumentsObject : public ArgumentsObject 1.306 +{ 1.307 + public: 1.308 + static const Class class_; 1.309 +}; 1.310 + 1.311 +} // namespace js 1.312 + 1.313 +template<> 1.314 +inline bool 1.315 +JSObject::is<js::ArgumentsObject>() const 1.316 +{ 1.317 + return is<js::NormalArgumentsObject>() || is<js::StrictArgumentsObject>(); 1.318 +} 1.319 + 1.320 +#endif /* vm_ArgumentsObject_h */