/work/obj-fuzz/dist/include/js/CallArgs.h
Line | Count | Source (jump to first uncovered line) |
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 | | * [SMDOC] JS::CallArgs API |
9 | | * |
10 | | * Helper classes encapsulating access to the callee, |this| value, arguments, |
11 | | * and argument count for a call/construct operation. |
12 | | * |
13 | | * JS::CallArgs encapsulates access to a JSNative's un-abstracted |
14 | | * |unsigned argc, Value* vp| arguments. The principal way to create a |
15 | | * JS::CallArgs is using JS::CallArgsFromVp: |
16 | | * |
17 | | * // If provided no arguments or a non-numeric first argument, return zero. |
18 | | * // Otherwise return |this| exactly as given, without boxing. |
19 | | * static bool |
20 | | * Func(JSContext* cx, unsigned argc, JS::Value* vp) |
21 | | * { |
22 | | * JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
23 | | * |
24 | | * // Guard against no arguments or a non-numeric arg0. |
25 | | * if (args.length() == 0 || !args[0].isNumber()) { |
26 | | * args.rval().setInt32(0); |
27 | | * return true; |
28 | | * } |
29 | | * |
30 | | * // Access to the callee must occur before accessing/setting |
31 | | * // the return value. |
32 | | * JSObject& callee = args.callee(); |
33 | | * args.rval().setObject(callee); |
34 | | * |
35 | | * // callee() and calleev() will now assert. |
36 | | * |
37 | | * // It's always fine to access thisv(). |
38 | | * HandleValue thisv = args.thisv(); |
39 | | * args.rval().set(thisv); |
40 | | * |
41 | | * // As the return value was last set to |this|, returns |this|. |
42 | | * return true; |
43 | | * } |
44 | | * |
45 | | * CallArgs is exposed publicly and used internally. Not all parts of its |
46 | | * public interface are meant to be used by embedders! See inline comments to |
47 | | * for details. |
48 | | * |
49 | | * It's possible (albeit deprecated) to manually index into |vp| to access the |
50 | | * callee, |this|, and arguments of a function, and to set its return value. |
51 | | * This does not have the error-handling or moving-GC correctness of CallArgs. |
52 | | * New code should use CallArgs instead whenever possible. |
53 | | * |
54 | | * The eventual plan is to change JSNative to take |const CallArgs&| directly, |
55 | | * for automatic assertion of correct use and to make calling functions more |
56 | | * efficient. Embedders should start internally switching away from using |
57 | | * |argc| and |vp| directly, except to create a |CallArgs|. Then, when an |
58 | | * eventual release making that change occurs, porting efforts will require |
59 | | * changing methods' signatures but won't require invasive changes to the |
60 | | * methods' implementations, potentially under time pressure. |
61 | | */ |
62 | | |
63 | | #ifndef js_CallArgs_h |
64 | | #define js_CallArgs_h |
65 | | |
66 | | #include "mozilla/Assertions.h" |
67 | | #include "mozilla/Attributes.h" |
68 | | #include "mozilla/TypeTraits.h" |
69 | | |
70 | | #include "jstypes.h" |
71 | | |
72 | | #include "js/RootingAPI.h" |
73 | | #include "js/Value.h" |
74 | | |
75 | | /* Typedef for native functions called by the JS VM. */ |
76 | | typedef bool |
77 | | (* JSNative)(JSContext* cx, unsigned argc, JS::Value* vp); |
78 | | |
79 | | namespace JS { |
80 | | |
81 | | extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue; |
82 | | |
83 | | namespace detail { |
84 | | |
85 | | /* |
86 | | * Compute |this| for the |vp| inside a JSNative, either boxing primitives or |
87 | | * replacing with the global object as necessary. |
88 | | */ |
89 | | extern JS_PUBLIC_API(bool) |
90 | | ComputeThis(JSContext* cx, JS::Value* vp, MutableHandleObject thisObject); |
91 | | |
92 | | #ifdef JS_DEBUG |
93 | | extern JS_PUBLIC_API(void) |
94 | | CheckIsValidConstructible(const Value& v); |
95 | | #endif |
96 | | |
97 | | class MOZ_STACK_CLASS IncludeUsedRval |
98 | | { |
99 | | mutable bool usedRval_; |
100 | | |
101 | | public: |
102 | 0 | bool usedRval() const { return usedRval_; } |
103 | 0 | void setUsedRval() const { usedRval_ = true; } |
104 | 0 | void clearUsedRval() const { usedRval_ = false; } |
105 | 0 | void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); } |
106 | | }; |
107 | | |
108 | | class MOZ_STACK_CLASS NoUsedRval |
109 | | { |
110 | | public: |
111 | 0 | bool usedRval() const { return false; } |
112 | 0 | void setUsedRval() const {} |
113 | 0 | void clearUsedRval() const {} |
114 | 0 | void assertUnusedRval() const {} |
115 | | }; |
116 | | |
117 | | template<class WantUsedRval> |
118 | | class MOZ_STACK_CLASS CallArgsBase |
119 | | { |
120 | | static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value || |
121 | | mozilla::IsSame<WantUsedRval, NoUsedRval>::value, |
122 | | "WantUsedRval can only be IncludeUsedRval or NoUsedRval"); |
123 | | |
124 | | protected: |
125 | | Value* argv_; |
126 | | unsigned argc_; |
127 | | bool constructing_:1; |
128 | | |
129 | | // True if the caller does not use the return value. |
130 | | bool ignoresReturnValue_:1; |
131 | | |
132 | | #ifdef JS_DEBUG |
133 | | WantUsedRval wantUsedRval_; |
134 | | bool usedRval() const { return wantUsedRval_.usedRval(); } |
135 | | void setUsedRval() const { wantUsedRval_.setUsedRval(); } |
136 | | void clearUsedRval() const { wantUsedRval_.clearUsedRval(); } |
137 | | void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); } |
138 | | #else |
139 | | bool usedRval() const { return false; } |
140 | 12.9M | void setUsedRval() const {} JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::setUsedRval() const Line | Count | Source | 140 | 12.9M | void setUsedRval() const {} |
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::setUsedRval() const |
141 | 19.4M | void clearUsedRval() const {} |
142 | 40.5M | void assertUnusedRval() const {} |
143 | | #endif |
144 | | |
145 | | public: |
146 | | // CALLEE ACCESS |
147 | | |
148 | | /* |
149 | | * Returns the function being called, as a value. Must not be called after |
150 | | * rval() has been used! |
151 | | */ |
152 | 40.5M | HandleValue calleev() const { |
153 | 40.5M | this->assertUnusedRval(); |
154 | 40.5M | return HandleValue::fromMarkedLocation(&argv_[-2]); |
155 | 40.5M | } |
156 | | |
157 | | /* |
158 | | * Returns the function being called, as an object. Must not be called |
159 | | * after rval() has been used! |
160 | | */ |
161 | 27.5M | JSObject& callee() const { |
162 | 27.5M | return calleev().toObject(); |
163 | 27.5M | } |
164 | | |
165 | | // CALLING/CONSTRUCTING-DIFFERENTIATIONS |
166 | | |
167 | 0 | bool isConstructing() const { |
168 | 0 | if (!argv_[-1].isMagic()) { |
169 | 0 | return false; |
170 | 0 | } |
171 | 0 | |
172 | | #ifdef JS_DEBUG |
173 | | if (!this->usedRval()) { |
174 | | CheckIsValidConstructible(calleev()); |
175 | | } |
176 | | #endif |
177 | | |
178 | 0 | return true; |
179 | 0 | } |
180 | | |
181 | 4.86M | bool ignoresReturnValue() const { |
182 | 4.86M | return ignoresReturnValue_; |
183 | 4.86M | } |
184 | | |
185 | 10 | MutableHandleValue newTarget() const { |
186 | 10 | MOZ_ASSERT(constructing_); |
187 | 10 | return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]); |
188 | 10 | } |
189 | | |
190 | | /* |
191 | | * Returns the |this| value passed to the function. This method must not |
192 | | * be called when the function is being called as a constructor via |new|. |
193 | | * The value may or may not be an object: it is the individual function's |
194 | | * responsibility to box the value if needed. |
195 | | */ |
196 | 25.9M | HandleValue thisv() const { |
197 | 25.9M | // Some internal code uses thisv() in constructing cases, so don't do |
198 | 25.9M | // this yet. |
199 | 25.9M | // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING)); |
200 | 25.9M | return HandleValue::fromMarkedLocation(&argv_[-1]); |
201 | 25.9M | } |
202 | | |
203 | 6.49M | bool computeThis(JSContext* cx, MutableHandleObject thisObject) const { |
204 | 6.49M | if (thisv().isObject()) { |
205 | 6.49M | thisObject.set(&thisv().toObject()); |
206 | 6.49M | return true; |
207 | 6.49M | } |
208 | 0 | |
209 | 0 | return ComputeThis(cx, base(), thisObject); |
210 | 0 | } |
211 | | |
212 | | // ARGUMENTS |
213 | | |
214 | | /* Returns the number of arguments. */ |
215 | 21.0M | unsigned length() const { return argc_; } JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::length() const Line | Count | Source | 215 | 21.0M | unsigned length() const { return argc_; } |
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::length() const |
216 | | |
217 | | /* Returns the i-th zero-indexed argument. */ |
218 | 8.09M | MutableHandleValue operator[](unsigned i) const { |
219 | 8.09M | MOZ_ASSERT(i < argc_); |
220 | 8.09M | return MutableHandleValue::fromMarkedLocation(&this->argv_[i]); |
221 | 8.09M | } JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::operator[](unsigned int) const Line | Count | Source | 218 | 8.09M | MutableHandleValue operator[](unsigned i) const { | 219 | 8.09M | MOZ_ASSERT(i < argc_); | 220 | 8.09M | return MutableHandleValue::fromMarkedLocation(&this->argv_[i]); | 221 | 8.09M | } |
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::operator[](unsigned int) const |
222 | | |
223 | | /* |
224 | | * Returns the i-th zero-indexed argument, or |undefined| if there's no |
225 | | * such argument. |
226 | | */ |
227 | 6 | HandleValue get(unsigned i) const { |
228 | 6 | return i < length() |
229 | 6 | ? HandleValue::fromMarkedLocation(&this->argv_[i]) |
230 | 6 | : UndefinedHandleValue; |
231 | 6 | } Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::get(unsigned int) const JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::get(unsigned int) const Line | Count | Source | 227 | 6 | HandleValue get(unsigned i) const { | 228 | 6 | return i < length() | 229 | 6 | ? HandleValue::fromMarkedLocation(&this->argv_[i]) | 230 | 6 | : UndefinedHandleValue; | 231 | 6 | } |
|
232 | | |
233 | | /* |
234 | | * Returns true if the i-th zero-indexed argument is present and is not |
235 | | * |undefined|. |
236 | | */ |
237 | 17 | bool hasDefined(unsigned i) const { |
238 | 17 | return i < argc_ && !this->argv_[i].isUndefined(); |
239 | 17 | } Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::hasDefined(unsigned int) const JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::hasDefined(unsigned int) const Line | Count | Source | 237 | 17 | bool hasDefined(unsigned i) const { | 238 | 17 | return i < argc_ && !this->argv_[i].isUndefined(); | 239 | 17 | } |
|
240 | | |
241 | | // RETURN VALUE |
242 | | |
243 | | /* |
244 | | * Returns the currently-set return value. The initial contents of this |
245 | | * value are unspecified. Once this method has been called, callee() and |
246 | | * calleev() can no longer be used. (If you're compiling against a debug |
247 | | * build of SpiderMonkey, these methods will assert to aid debugging.) |
248 | | * |
249 | | * If the method you're implementing succeeds by returning true, you *must* |
250 | | * set this. (SpiderMonkey doesn't currently assert this, but it will do |
251 | | * so eventually.) You don't need to use or change this if your method |
252 | | * fails. |
253 | | */ |
254 | 12.9M | MutableHandleValue rval() const { |
255 | 12.9M | this->setUsedRval(); |
256 | 12.9M | return MutableHandleValue::fromMarkedLocation(&argv_[-2]); |
257 | 12.9M | } JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::rval() const Line | Count | Source | 254 | 12.9M | MutableHandleValue rval() const { | 255 | 12.9M | this->setUsedRval(); | 256 | 12.9M | return MutableHandleValue::fromMarkedLocation(&argv_[-2]); | 257 | 12.9M | } |
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::rval() const |
258 | | |
259 | | public: |
260 | | // These methods are publicly exposed, but they are *not* to be used when |
261 | | // implementing a JSNative method and encapsulating access to |vp| within |
262 | | // it. You probably don't want to use these! |
263 | | |
264 | 6.45M | void setCallee(const Value& aCalleev) const { |
265 | 6.45M | this->clearUsedRval(); |
266 | 6.45M | argv_[-2] = aCalleev; |
267 | 6.45M | } |
268 | | |
269 | 6.45M | void setThis(const Value& aThisv) const { |
270 | 6.45M | argv_[-1] = aThisv; |
271 | 6.45M | } |
272 | | |
273 | 6.48M | MutableHandleValue mutableThisv() const { |
274 | 6.48M | return MutableHandleValue::fromMarkedLocation(&argv_[-1]); |
275 | 6.48M | } |
276 | | |
277 | | public: |
278 | | // These methods are publicly exposed, but we're unsure of the interfaces |
279 | | // (because they're hackish and drop assertions). Avoid using these if you |
280 | | // can. |
281 | | |
282 | 8.11M | Value* array() const { return argv_; } |
283 | 17.8M | Value* end() const { return argv_ + argc_ + constructing_; } |
284 | | |
285 | | public: |
286 | | // These methods are only intended for internal use. Embedders shouldn't |
287 | | // use them! |
288 | | |
289 | 9.72M | Value* base() const { return argv_ - 2; } |
290 | | |
291 | 511 | Value* spAfterCall() const { |
292 | 511 | this->setUsedRval(); |
293 | 511 | return argv_ - 1; |
294 | 511 | } |
295 | | }; |
296 | | |
297 | | } // namespace detail |
298 | | |
299 | | class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval> |
300 | | { |
301 | | private: |
302 | | friend CallArgs CallArgsFromVp(unsigned argc, Value* vp); |
303 | | friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing, |
304 | | bool ignoresReturnValue); |
305 | | |
306 | | static CallArgs create(unsigned argc, Value* argv, bool constructing, |
307 | 12.9M | bool ignoresReturnValue = false) { |
308 | 12.9M | CallArgs args; |
309 | 12.9M | args.clearUsedRval(); |
310 | 12.9M | args.argv_ = argv; |
311 | 12.9M | args.argc_ = argc; |
312 | 12.9M | args.constructing_ = constructing; |
313 | 12.9M | args.ignoresReturnValue_ = ignoresReturnValue; |
314 | | #ifdef DEBUG |
315 | | MOZ_ASSERT(ValueIsNotGray(args.thisv())); |
316 | | MOZ_ASSERT(ValueIsNotGray(args.calleev())); |
317 | | for (unsigned i = 0; i < argc; ++i) { |
318 | | MOZ_ASSERT(ValueIsNotGray(argv[i])); |
319 | | } |
320 | | #endif |
321 | | return args; |
322 | 12.9M | } |
323 | | |
324 | | public: |
325 | | /* |
326 | | * Returns true if there are at least |required| arguments passed in. If |
327 | | * false, it reports an error message on the context. |
328 | | */ |
329 | | JS_PUBLIC_API(bool) requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const; |
330 | | |
331 | | }; |
332 | | |
333 | | MOZ_ALWAYS_INLINE CallArgs |
334 | | CallArgsFromVp(unsigned argc, Value* vp) |
335 | 12.9M | { |
336 | 12.9M | return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING)); |
337 | 12.9M | } |
338 | | |
339 | | // This method is only intended for internal use in SpiderMonkey. We may |
340 | | // eventually move it to an internal header. Embedders should use |
341 | | // JS::CallArgsFromVp! |
342 | | MOZ_ALWAYS_INLINE CallArgs |
343 | | CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false, |
344 | | bool ignoresReturnValue = false) |
345 | 31.6k | { |
346 | 31.6k | return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing, |
347 | 31.6k | ignoresReturnValue); |
348 | 31.6k | } |
349 | | |
350 | | } // namespace JS |
351 | | |
352 | | #endif /* js_CallArgs_h */ |