Line data Source code
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/. */
6 :
7 : /*
8 : * Helper classes encapsulating access to the callee, |this| value, arguments,
9 : * and argument count for a call/construct operation.
10 : *
11 : * JS::CallArgs encapsulates access to a JSNative's un-abstracted
12 : * |unsigned argc, Value* vp| arguments. The principal way to create a
13 : * JS::CallArgs is using JS::CallArgsFromVp:
14 : *
15 : * // If provided no arguments or a non-numeric first argument, return zero.
16 : * // Otherwise return |this| exactly as given, without boxing.
17 : * static bool
18 : * Func(JSContext* cx, unsigned argc, JS::Value* vp)
19 : * {
20 : * JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
21 : *
22 : * // Guard against no arguments or a non-numeric arg0.
23 : * if (args.length() == 0 || !args[0].isNumber()) {
24 : * args.rval().setInt32(0);
25 : * return true;
26 : * }
27 : *
28 : * // Access to the callee must occur before accessing/setting
29 : * // the return value.
30 : * JSObject& callee = args.callee();
31 : * args.rval().setObject(callee);
32 : *
33 : * // callee() and calleev() will now assert.
34 : *
35 : * // It's always fine to access thisv().
36 : * HandleValue thisv = args.thisv();
37 : * args.rval().set(thisv);
38 : *
39 : * // As the return value was last set to |this|, returns |this|.
40 : * return true;
41 : * }
42 : *
43 : * CallArgs is exposed publicly and used internally. Not all parts of its
44 : * public interface are meant to be used by embedders! See inline comments to
45 : * for details.
46 : *
47 : * It's possible (albeit deprecated) to manually index into |vp| to access the
48 : * callee, |this|, and arguments of a function, and to set its return value.
49 : * It's also possible to use the supported API of JS_CALLEE, JS_THIS, JS_ARGV,
50 : * JS_RVAL, and JS_SET_RVAL to the same ends.
51 : *
52 : * But neither API has the error-handling or moving-GC correctness of CallArgs.
53 : * New code should use CallArgs instead whenever possible.
54 : *
55 : * The eventual plan is to change JSNative to take |const CallArgs&| directly,
56 : * for automatic assertion of correct use and to make calling functions more
57 : * efficient. Embedders should start internally switching away from using
58 : * |argc| and |vp| directly, except to create a |CallArgs|. Then, when an
59 : * eventual release making that change occurs, porting efforts will require
60 : * changing methods' signatures but won't require invasive changes to the
61 : * methods' implementations, potentially under time pressure.
62 : */
63 :
64 : #ifndef js_CallArgs_h
65 : #define js_CallArgs_h
66 :
67 : #include "mozilla/Assertions.h"
68 : #include "mozilla/Attributes.h"
69 : #include "mozilla/TypeTraits.h"
70 :
71 : #include "jstypes.h"
72 :
73 : #include "js/RootingAPI.h"
74 : #include "js/Value.h"
75 :
76 : /* Typedef for native functions called by the JS VM. */
77 : typedef bool
78 : (* JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);
79 :
80 : namespace JS {
81 :
82 : extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
83 :
84 : namespace detail {
85 :
86 : /*
87 : * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
88 : * replacing with the global object as necessary.
89 : */
90 : extern JS_PUBLIC_API(Value)
91 : ComputeThis(JSContext* cx, JS::Value* vp);
92 :
93 : #ifdef JS_DEBUG
94 : extern JS_PUBLIC_API(void)
95 : CheckIsValidConstructible(const Value& v);
96 : #endif
97 :
98 : class MOZ_STACK_CLASS IncludeUsedRval
99 : {
100 : mutable bool usedRval_;
101 :
102 : public:
103 1364 : bool usedRval() const { return usedRval_; }
104 218112 : void setUsedRval() const { usedRval_ = true; }
105 193932 : void clearUsedRval() const { usedRval_ = false; }
106 515266 : void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
107 : };
108 :
109 : class MOZ_STACK_CLASS NoUsedRval
110 : {
111 : public:
112 : bool usedRval() const { return false; }
113 1480 : void setUsedRval() const {}
114 : void clearUsedRval() const {}
115 : void assertUnusedRval() const {}
116 : };
117 :
118 : template<class WantUsedRval>
119 17006 : class MOZ_STACK_CLASS CallArgsBase
120 : {
121 : static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value ||
122 : mozilla::IsSame<WantUsedRval, NoUsedRval>::value,
123 : "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
124 :
125 : protected:
126 : Value* argv_;
127 : unsigned argc_;
128 : bool constructing_:1;
129 :
130 : // True if the caller does not use the return value.
131 : bool ignoresReturnValue_:1;
132 :
133 : #ifdef JS_DEBUG
134 : WantUsedRval wantUsedRval_;
135 1364 : bool usedRval() const { return wantUsedRval_.usedRval(); }
136 219592 : void setUsedRval() const { wantUsedRval_.setUsedRval(); }
137 193932 : void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
138 515266 : void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
139 : #else
140 : bool usedRval() const { return false; }
141 : void setUsedRval() const {}
142 : void clearUsedRval() const {}
143 : void assertUnusedRval() const {}
144 : #endif
145 :
146 : public:
147 : // CALLEE ACCESS
148 :
149 : /*
150 : * Returns the function being called, as a value. Must not be called after
151 : * rval() has been used!
152 : */
153 515266 : HandleValue calleev() const {
154 515266 : this->assertUnusedRval();
155 515267 : return HandleValue::fromMarkedLocation(&argv_[-2]);
156 : }
157 :
158 : /*
159 : * Returns the function being called, as an object. Must not be called
160 : * after rval() has been used!
161 : */
162 212523 : JSObject& callee() const {
163 212523 : return calleev().toObject();
164 : }
165 :
166 : // CALLING/CONSTRUCTING-DIFFERENTIATIONS
167 :
168 1684 : bool isConstructing() const {
169 1684 : if (!argv_[-1].isMagic())
170 320 : return false;
171 :
172 : #ifdef JS_DEBUG
173 1364 : if (!this->usedRval())
174 1364 : CheckIsValidConstructible(calleev());
175 : #endif
176 :
177 1364 : return true;
178 : }
179 :
180 26683 : bool ignoresReturnValue() const {
181 26683 : return ignoresReturnValue_;
182 : }
183 :
184 3575 : MutableHandleValue newTarget() const {
185 3575 : MOZ_ASSERT(constructing_);
186 3575 : return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
187 : }
188 :
189 : /*
190 : * Returns the |this| value passed to the function. This method must not
191 : * be called when the function is being called as a constructor via |new|.
192 : * The value may or may not be an object: it is the individual function's
193 : * responsibility to box the value if needed.
194 : */
195 316634 : HandleValue thisv() const {
196 : // Some internal code uses thisv() in constructing cases, so don't do
197 : // this yet.
198 : // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
199 316634 : return HandleValue::fromMarkedLocation(&argv_[-1]);
200 : }
201 :
202 0 : Value computeThis(JSContext* cx) const {
203 0 : if (thisv().isObject())
204 0 : return thisv();
205 :
206 0 : return ComputeThis(cx, base());
207 : }
208 :
209 : // ARGUMENTS
210 :
211 : /* Returns the number of arguments. */
212 419762 : unsigned length() const { return argc_; }
213 :
214 : /* Returns the i-th zero-indexed argument. */
215 249903 : MutableHandleValue operator[](unsigned i) const {
216 249903 : MOZ_ASSERT(i < argc_);
217 249903 : return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
218 : }
219 :
220 : /*
221 : * Returns the i-th zero-indexed argument, or |undefined| if there's no
222 : * such argument.
223 : */
224 11479 : HandleValue get(unsigned i) const {
225 11479 : return i < length()
226 10967 : ? HandleValue::fromMarkedLocation(&this->argv_[i])
227 22446 : : UndefinedHandleValue;
228 : }
229 :
230 : /*
231 : * Returns true if the i-th zero-indexed argument is present and is not
232 : * |undefined|.
233 : */
234 1851 : bool hasDefined(unsigned i) const {
235 1851 : return i < argc_ && !this->argv_[i].isUndefined();
236 : }
237 :
238 : // RETURN VALUE
239 :
240 : /*
241 : * Returns the currently-set return value. The initial contents of this
242 : * value are unspecified. Once this method has been called, callee() and
243 : * calleev() can no longer be used. (If you're compiling against a debug
244 : * build of SpiderMonkey, these methods will assert to aid debugging.)
245 : *
246 : * If the method you're implementing succeeds by returning true, you *must*
247 : * set this. (SpiderMonkey doesn't currently assert this, but it will do
248 : * so eventually.) You don't need to use or change this if your method
249 : * fails.
250 : */
251 189017 : MutableHandleValue rval() const {
252 189017 : this->setUsedRval();
253 189017 : return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
254 : }
255 :
256 : public:
257 : // These methods are publicly exposed, but they are *not* to be used when
258 : // implementing a JSNative method and encapsulating access to |vp| within
259 : // it. You probably don't want to use these!
260 :
261 20603 : void setCallee(const Value& aCalleev) const {
262 20603 : this->clearUsedRval();
263 20603 : argv_[-2] = aCalleev;
264 20603 : }
265 :
266 16089 : void setThis(const Value& aThisv) const {
267 16089 : argv_[-1] = aThisv;
268 16089 : }
269 :
270 33714 : MutableHandleValue mutableThisv() const {
271 33714 : return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
272 : }
273 :
274 : public:
275 : // These methods are publicly exposed, but we're unsure of the interfaces
276 : // (because they're hackish and drop assertions). Avoid using these if you
277 : // can.
278 :
279 75624 : Value* array() const { return argv_; }
280 190784 : Value* end() const { return argv_ + argc_ + constructing_; }
281 :
282 : public:
283 : // These methods are only intended for internal use. Embedders shouldn't
284 : // use them!
285 :
286 76349 : Value* base() const { return argv_ - 2; }
287 :
288 30300 : Value* spAfterCall() const {
289 30300 : this->setUsedRval();
290 30300 : return argv_ - 1;
291 : }
292 : };
293 :
294 : } // namespace detail
295 :
296 15563 : class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
297 : {
298 : private:
299 : friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
300 : friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing,
301 : bool ignoresReturnValue);
302 :
303 173333 : static CallArgs create(unsigned argc, Value* argv, bool constructing,
304 : bool ignoresReturnValue = false) {
305 : CallArgs args;
306 173333 : args.clearUsedRval();
307 173333 : args.argv_ = argv;
308 173333 : args.argc_ = argc;
309 173333 : args.constructing_ = constructing;
310 173333 : args.ignoresReturnValue_ = ignoresReturnValue;
311 : #ifdef DEBUG
312 173333 : MOZ_ASSERT(ValueIsNotGray(args.thisv()));
313 173333 : MOZ_ASSERT(ValueIsNotGray(args.calleev()));
314 418973 : for (unsigned i = 0; i < argc; ++i)
315 245640 : MOZ_ASSERT(ValueIsNotGray(argv[i]));
316 : #endif
317 173333 : return args;
318 : }
319 :
320 : public:
321 : /*
322 : * Returns true if there are at least |required| arguments passed in. If
323 : * false, it reports an error message on the context.
324 : */
325 : JS_PUBLIC_API(bool) requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const;
326 :
327 : };
328 :
329 : MOZ_ALWAYS_INLINE CallArgs
330 136911 : CallArgsFromVp(unsigned argc, Value* vp)
331 : {
332 136911 : return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
333 : }
334 :
335 : // This method is only intended for internal use in SpiderMonkey. We may
336 : // eventually move it to an internal header. Embedders should use
337 : // JS::CallArgsFromVp!
338 : MOZ_ALWAYS_INLINE CallArgs
339 36138 : CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false,
340 : bool ignoresReturnValue = false)
341 : {
342 36138 : return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
343 72276 : ignoresReturnValue);
344 : }
345 :
346 : } // namespace JS
347 :
348 : /*
349 : * Macros to hide interpreter stack layout details from a JSNative using its
350 : * JS::Value* vp parameter. DO NOT USE THESE! Instead use JS::CallArgs and
351 : * friends, above. These macros will be removed when we change JSNative to
352 : * take a const JS::CallArgs&.
353 : */
354 :
355 : /*
356 : * Return |this| if |this| is an object. Otherwise, return the global object
357 : * if |this| is null or undefined, and finally return a boxed version of any
358 : * other primitive.
359 : *
360 : * Note: if this method returns null, an error has occurred and must be
361 : * propagated or caught.
362 : */
363 : MOZ_ALWAYS_INLINE JS::Value
364 11289 : JS_THIS(JSContext* cx, JS::Value* vp)
365 : {
366 11289 : return vp[1].isPrimitive() ? JS::detail::ComputeThis(cx, vp) : vp[1];
367 : }
368 :
369 : /*
370 : * A note on JS_THIS_OBJECT: no equivalent method is part of the CallArgs
371 : * interface, and we're unlikely to add one (functions shouldn't be implicitly
372 : * exposing the global object to arbitrary callers). Continue using |vp|
373 : * directly for this case, but be aware this API will eventually be replaced
374 : * with a function that operates directly upon |args.thisv()|.
375 : */
376 : #define JS_THIS_OBJECT(cx,vp) (JS_THIS(cx,vp).toObjectOrNull())
377 :
378 : /*
379 : * |this| is passed to functions in ES5 without change. Functions themselves
380 : * do any post-processing they desire to box |this|, compute the global object,
381 : * &c. This macro retrieves a function's unboxed |this| value.
382 : *
383 : * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
384 : * or vice versa. Either use the provided this value with this macro, or
385 : * compute the boxed |this| value using those. JS_THIS_VALUE must not be used
386 : * if the function is being called as a constructor.
387 : *
388 : * But: DO NOT USE THIS! Instead use JS::CallArgs::thisv(), above.
389 : *
390 : */
391 : #define JS_THIS_VALUE(cx,vp) ((vp)[1])
392 :
393 : #endif /* js_CallArgs_h */
|