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 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef ArgumentsObject_h___
42 : #define ArgumentsObject_h___
43 :
44 : #include "jsfun.h"
45 :
46 : #ifdef JS_POLYIC
47 : class GetPropCompiler;
48 : #endif
49 :
50 : namespace js {
51 :
52 : #ifdef JS_POLYIC
53 : struct VMFrame;
54 : namespace mjit {
55 : namespace ic {
56 : struct PICInfo;
57 : struct GetElementIC;
58 :
59 : /* Aargh, Windows. */
60 : #ifdef GetProp
61 : #undef GetProp
62 : #endif
63 : void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *pic);
64 : }
65 : }
66 : #endif
67 :
68 : struct EmptyShape;
69 :
70 : /*
71 : * ArgumentsData stores the initial indexed arguments provided to the
72 : * corresponding and that function itself. It is used to store arguments[i]
73 : * and arguments.callee -- up until the corresponding property is modified,
74 : * when the relevant value is overwritten with MagicValue(JS_ARGS_HOLE) to
75 : * memorialize the modification.
76 : */
77 : struct ArgumentsData
78 : {
79 : /*
80 : * arguments.callee, or MagicValue(JS_ARGS_HOLE) if arguments.callee has
81 : * been modified.
82 : */
83 : HeapValue callee;
84 :
85 : /*
86 : * Values of the arguments for this object, or MagicValue(JS_ARGS_HOLE) if
87 : * the indexed argument has been modified.
88 : */
89 : HeapValue slots[1];
90 : };
91 :
92 : /*
93 : * ArgumentsObject instances represent |arguments| objects created to store
94 : * function arguments when a function is called. It's expensive to create such
95 : * objects if they're never used, so they're only created lazily. (See
96 : * js::StackFrame::setArgsObj and friends.)
97 : *
98 : * Arguments objects are complicated because, for non-strict mode code, they
99 : * must alias any named arguments which were provided to the function. Gnarly
100 : * example:
101 : *
102 : * function f(a, b, c, d)
103 : * {
104 : * arguments[0] = "seta";
105 : * assertEq(a, "seta");
106 : * b = "setb";
107 : * assertEq(arguments[1], "setb");
108 : * c = "setc";
109 : * assertEq(arguments[2], undefined);
110 : * arguments[3] = "setd";
111 : * assertEq(d, undefined);
112 : * }
113 : * f("arga", "argb");
114 : *
115 : * ES5's strict mode behaves more sanely, and named arguments don't alias
116 : * elements of an arguments object.
117 : *
118 : * ArgumentsObject instances use the following reserved slots:
119 : *
120 : * INITIAL_LENGTH_SLOT
121 : * Stores the initial value of arguments.length, plus a bit indicating
122 : * whether arguments.length has been modified. Use initialLength() and
123 : * hasOverriddenLength() to access these values. If arguments.length has
124 : * been modified, then the current value of arguments.length is stored in
125 : * another slot associated with a new property.
126 : * DATA_SLOT
127 : * Stores an ArgumentsData* storing argument values and the callee, or
128 : * sentinels for any of these if the corresponding property is modified.
129 : * Use callee() to access the callee/sentinel, and use
130 : * element/addressOfElement/setElement to access the values stored in
131 : * the ArgumentsData. If you're simply looking to get arguments[i],
132 : * however, use getElement or getElements to avoid spreading arguments
133 : * object implementation details around too much.
134 : * STACK_FRAME_SLOT
135 : * Stores the function's stack frame for non-strict arguments objects until
136 : * the function returns, when it is replaced with null. When an arguments
137 : * object is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE,
138 : * and when the trace exits its private is replaced with the stack frame or
139 : * null, as appropriate. This slot is used by strict arguments objects as
140 : * well, but the slot is always null. Conceptually it would be better to
141 : * remove this oddity, but preserving it allows us to work with arguments
142 : * objects of either kind more abstractly, so we keep it for now.
143 : */
144 : class ArgumentsObject : public JSObject
145 : {
146 : static const uint32_t INITIAL_LENGTH_SLOT = 0;
147 : static const uint32_t DATA_SLOT = 1;
148 : static const uint32_t STACK_FRAME_SLOT = 2;
149 :
150 : public:
151 : static const uint32_t RESERVED_SLOTS = 3;
152 : static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
153 :
154 : private:
155 : /* Lower-order bit stolen from the length slot. */
156 : static const uint32_t LENGTH_OVERRIDDEN_BIT = 0x1;
157 : static const uint32_t PACKED_BITS_COUNT = 1;
158 :
159 : /*
160 : * Need access to DATA_SLOT, INITIAL_LENGTH_SLOT, LENGTH_OVERRIDDEN_BIT, and
161 : * PACKED_BIT_COUNT.
162 : */
163 : #ifdef JS_POLYIC
164 : friend class ::GetPropCompiler;
165 : friend struct mjit::ic::GetElementIC;
166 : #endif
167 :
168 : void initInitialLength(uint32_t length);
169 :
170 : void initData(ArgumentsData *data);
171 :
172 : public:
173 : /* Create an arguments object for the given callee function and frame. */
174 : static ArgumentsObject *create(JSContext *cx, uint32_t argc, JSObject &callee);
175 :
176 : /*
177 : * Return the initial length of the arguments. This may differ from the
178 : * current value of arguments.length!
179 : */
180 : inline uint32_t initialLength() const;
181 :
182 : /* True iff arguments.length has been assigned or its attributes changed. */
183 : inline bool hasOverriddenLength() const;
184 : inline void markLengthOverridden();
185 :
186 : /*
187 : * Attempt to speedily and efficiently access the i-th element of this
188 : * arguments object. Return true if the element was speedily returned.
189 : * Return false if the element must be looked up more slowly using
190 : * getProperty or some similar method.
191 : *
192 : * NB: Returning false does not indicate error!
193 : */
194 : inline bool getElement(uint32_t i, js::Value *vp);
195 :
196 : /*
197 : * Attempt to speedily and efficiently get elements [start, start + count)
198 : * of this arguments object into the locations starting at |vp|. Return
199 : * true if all elements were copied. Return false if the elements must be
200 : * gotten more slowly, perhaps using a getProperty or some similar method
201 : * in a loop.
202 : *
203 : * NB: Returning false does not indicate error!
204 : */
205 : inline bool getElements(uint32_t start, uint32_t count, js::Value *vp);
206 :
207 : inline js::ArgumentsData *data() const;
208 :
209 : inline const js::Value &element(uint32_t i) const;
210 : inline const js::Value *elements() const;
211 : inline void setElement(uint32_t i, const js::Value &v);
212 :
213 : /* The stack frame for this ArgumentsObject, if the frame is still active. */
214 : inline js::StackFrame *maybeStackFrame() const;
215 : inline void setStackFrame(js::StackFrame *frame);
216 :
217 : /*
218 : * Measures things hanging off this ArgumentsObject that are counted by the
219 : * |miscSize| argument in JSObject::sizeOfExcludingThis().
220 : */
221 : inline size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
222 : };
223 :
224 : class NormalArgumentsObject : public ArgumentsObject
225 : {
226 : friend bool JSObject::isNormalArguments() const;
227 : friend struct EmptyShape; // for EmptyShape::getEmptyArgumentsShape
228 : friend ArgumentsObject *
229 : ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee);
230 :
231 : public:
232 : /*
233 : * Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
234 : * been cleared.
235 : */
236 : inline const js::Value &callee() const;
237 :
238 : /* Clear the location storing arguments.callee's initial value. */
239 : inline void clearCallee();
240 : };
241 :
242 : class StrictArgumentsObject : public ArgumentsObject
243 : {
244 : friend bool JSObject::isStrictArguments() const;
245 : friend ArgumentsObject *
246 : ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee);
247 : };
248 :
249 : } // namespace js
250 :
251 : js::NormalArgumentsObject &
252 12425 : JSObject::asNormalArguments()
253 : {
254 12425 : JS_ASSERT(isNormalArguments());
255 12425 : return *static_cast<js::NormalArgumentsObject *>(this);
256 : }
257 :
258 : js::StrictArgumentsObject &
259 1822 : JSObject::asStrictArguments()
260 : {
261 1822 : JS_ASSERT(isStrictArguments());
262 1822 : return *static_cast<js::StrictArgumentsObject *>(this);
263 : }
264 :
265 : js::ArgumentsObject &
266 2132468 : JSObject::asArguments()
267 : {
268 2132468 : JS_ASSERT(isArguments());
269 2132468 : return *static_cast<js::ArgumentsObject *>(this);
270 : }
271 :
272 : const js::ArgumentsObject &
273 54 : JSObject::asArguments() const
274 : {
275 54 : JS_ASSERT(isArguments());
276 54 : return *static_cast<const js::ArgumentsObject *>(this);
277 : }
278 :
279 : #endif /* ArgumentsObject_h___ */
|