1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey arguments object code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Jeff Walden <jwalden+code@mit.edu> (original author)
26 : * Luke Wagner <luke@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #ifndef ArgumentsObject_h___
43 : #define ArgumentsObject_h___
44 :
45 : #include "jsfun.h"
46 :
47 : namespace js {
48 :
49 : /*
50 : * ArgumentsData stores the initial indexed arguments provided to the
51 : * corresponding and that function itself. It is used to store arguments[i]
52 : * and arguments.callee -- up until the corresponding property is modified,
53 : * when the relevant value is overwritten with MagicValue(JS_ARGS_HOLE) to
54 : * memorialize the modification.
55 : */
56 : struct ArgumentsData
57 : {
58 : /*
59 : * arguments.callee, or MagicValue(JS_ARGS_HOLE) if arguments.callee has
60 : * been modified.
61 : */
62 : HeapValue callee;
63 :
64 : /*
65 : * Pointer to an array of bits indicating, for every argument in 'slots',
66 : * whether the element has been deleted. See isElementDeleted comment.
67 : */
68 : size_t *deletedBits;
69 :
70 : /*
71 : * Values of the arguments for this object, or MagicValue(JS_ARGS_HOLE) if
72 : * the indexed argument has been modified.
73 : */
74 : HeapValue slots[1];
75 : };
76 :
77 : /*
78 : * ArgumentsObject instances represent |arguments| objects created to store
79 : * function arguments when a function is called. It's expensive to create such
80 : * objects if they're never used, so they're only created lazily. (See
81 : * js::StackFrame::setArgsObj and friends.)
82 : *
83 : * Arguments objects are complicated because, for non-strict mode code, they
84 : * must alias any named arguments which were provided to the function. Gnarly
85 : * example:
86 : *
87 : * function f(a, b, c, d)
88 : * {
89 : * arguments[0] = "seta";
90 : * assertEq(a, "seta");
91 : * b = "setb";
92 : * assertEq(arguments[1], "setb");
93 : * c = "setc";
94 : * assertEq(arguments[2], undefined);
95 : * arguments[3] = "setd";
96 : * assertEq(d, undefined);
97 : * }
98 : * f("arga", "argb");
99 : *
100 : * ES5's strict mode behaves more sanely, and named arguments don't alias
101 : * elements of an arguments object.
102 : *
103 : * ArgumentsObject instances use the following reserved slots:
104 : *
105 : * INITIAL_LENGTH_SLOT
106 : * Stores the initial value of arguments.length, plus a bit indicating
107 : * whether arguments.length has been modified. Use initialLength() and
108 : * hasOverriddenLength() to access these values. If arguments.length has
109 : * been modified, then the current value of arguments.length is stored in
110 : * another slot associated with a new property.
111 : * DATA_SLOT
112 : * Stores an ArgumentsData* storing argument values and the callee, or
113 : * sentinels for any of these if the corresponding property is modified.
114 : * Use callee() to access the callee/sentinel, and use
115 : * element/addressOfElement/setElement to access the values stored in
116 : * the ArgumentsData. If you're simply looking to get arguments[i],
117 : * however, use getElement or getElements to avoid spreading arguments
118 : * object implementation details around too much.
119 : * STACK_FRAME_SLOT
120 : * Stores the function's stack frame for non-strict arguments objects until
121 : * the function returns, when it is replaced with null. When an arguments
122 : * object is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE,
123 : * and when the trace exits its private is replaced with the stack frame or
124 : * null, as appropriate. This slot is used by strict arguments objects as
125 : * well, but the slot is always null. Conceptually it would be better to
126 : * remove this oddity, but preserving it allows us to work with arguments
127 : * objects of either kind more abstractly, so we keep it for now.
128 : */
129 : class ArgumentsObject : public JSObject
130 : {
131 : static const uint32_t INITIAL_LENGTH_SLOT = 0;
132 : static const uint32_t DATA_SLOT = 1;
133 : static const uint32_t STACK_FRAME_SLOT = 2;
134 :
135 : /* Lower-order bit stolen from the length slot. */
136 : static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
137 : static const uint32_t PACKED_BITS_COUNT = 1;
138 :
139 : void initInitialLength(uint32_t length);
140 : void initData(ArgumentsData *data);
141 : static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
142 :
143 : public:
144 : static const uint32_t RESERVED_SLOTS = 3;
145 : static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
146 :
147 : /* Create an arguments object for a frame that is expecting them. */
148 : static bool create(JSContext *cx, StackFrame *fp);
149 :
150 : /*
151 : * Purposefully disconnect the returned arguments object from the frame
152 : * by always creating a new copy that does not alias formal parameters.
153 : * This allows function-local analysis to determine that formals are
154 : * not aliased and generally simplifies arguments objects.
155 : */
156 : static ArgumentsObject *createUnexpected(JSContext *cx, StackFrame *fp);
157 :
158 : /*
159 : * Return the initial length of the arguments. This may differ from the
160 : * current value of arguments.length!
161 : */
162 : inline uint32_t initialLength() const;
163 :
164 : /* True iff arguments.length has been assigned or its attributes changed. */
165 : inline bool hasOverriddenLength() const;
166 : inline void markLengthOverridden();
167 :
168 : /*
169 : * Attempt to speedily and efficiently access the i-th element of this
170 : * arguments object. Return true if the element was speedily returned.
171 : * Return false if the element must be looked up more slowly using
172 : * getProperty or some similar method.
173 : *
174 : * NB: Returning false does not indicate error!
175 : */
176 : inline bool getElement(uint32_t i, js::Value *vp);
177 :
178 : /*
179 : * Attempt to speedily and efficiently get elements [start, start + count)
180 : * of this arguments object into the locations starting at |vp|. Return
181 : * true if all elements were copied. Return false if the elements must be
182 : * gotten more slowly, perhaps using a getProperty or some similar method
183 : * in a loop.
184 : *
185 : * NB: Returning false does not indicate error!
186 : */
187 : inline bool getElements(uint32_t start, uint32_t count, js::Value *vp);
188 :
189 : inline js::ArgumentsData *data() const;
190 :
191 : /*
192 : * Because the arguments object is a real object, its elements may be
193 : * deleted. This is implemented by setting a 'deleted' flag for the arg
194 : * which is read by argument object resolve and getter/setter hooks.
195 : *
196 : * NB: an element, once deleted, stays deleted. Thus:
197 : *
198 : * function f(x) { delete arguments[0]; arguments[0] = 42; return x }
199 : * assertEq(f(1), 1);
200 : *
201 : * This works because, once a property is deleted from an arguments object,
202 : * it gets regular properties with regular getters/setters that don't alias
203 : * ArgumentsData::slots.
204 : */
205 : inline bool isElementDeleted(uint32_t i) const;
206 : inline bool isAnyElementDeleted() const;
207 : inline void markElementDeleted(uint32_t i);
208 :
209 : inline const js::Value &element(uint32_t i) const;
210 : inline void setElement(uint32_t i, const js::Value &v);
211 :
212 : /* The stack frame for this ArgumentsObject, if the frame is still active. */
213 : inline js::StackFrame *maybeStackFrame() const;
214 : inline void setStackFrame(js::StackFrame *frame);
215 :
216 : /*
217 : * Measures things hanging off this ArgumentsObject that are counted by the
218 : * |miscSize| argument in JSObject::sizeOfExcludingThis().
219 : */
220 : inline size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
221 : };
222 :
223 : class NormalArgumentsObject : public ArgumentsObject
224 : {
225 : public:
226 : /*
227 : * Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
228 : * been cleared.
229 : */
230 : inline const js::Value &callee() const;
231 :
232 : /* Clear the location storing arguments.callee's initial value. */
233 : inline void clearCallee();
234 :
235 : /*
236 : * Return 'arguments[index]' for some unmodified NormalArgumentsObject of
237 : * 'fp' (the actual instance of 'arguments' doesn't matter so it does not
238 : * have to be passed or even created).
239 : */
240 : static bool optimizedGetElem(JSContext *cx, StackFrame *fp, const Value &elem, Value *vp);
241 : };
242 :
243 : class StrictArgumentsObject : public ArgumentsObject
244 : {};
245 :
246 : } // namespace js
247 :
248 : js::NormalArgumentsObject &
249 9034 : JSObject::asNormalArguments()
250 : {
251 9034 : JS_ASSERT(isNormalArguments());
252 9034 : return *static_cast<js::NormalArgumentsObject *>(this);
253 : }
254 :
255 : js::StrictArgumentsObject &
256 1628 : JSObject::asStrictArguments()
257 : {
258 1628 : JS_ASSERT(isStrictArguments());
259 1628 : return *static_cast<js::StrictArgumentsObject *>(this);
260 : }
261 :
262 : js::ArgumentsObject &
263 1286964 : JSObject::asArguments()
264 : {
265 1286964 : JS_ASSERT(isArguments());
266 1286964 : return *static_cast<js::ArgumentsObject *>(this);
267 : }
268 :
269 : const js::ArgumentsObject &
270 : JSObject::asArguments() const
271 : {
272 : JS_ASSERT(isArguments());
273 : return *static_cast<const js::ArgumentsObject *>(this);
274 : }
275 :
276 : #endif /* ArgumentsObject_h___ */
|