Bug 1856126 [wpt PR 42137] - LoAF: ensure scripts are added after microtask checkpoin...
[gecko.git] / js / public / CallArgs.h
blobe40790fdecca93a1e6f519d32552076e713d3a31
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 /*
8 * [SMDOC] JS::CallArgs API
10 * Helper classes encapsulating access to the callee, |this| value, arguments,
11 * and argument count for a call/construct operation.
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:
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);
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 * }
30 * // Access to the callee must occur before accessing/setting
31 * // the return value.
32 * JSObject& callee = args.callee();
33 * args.rval().setObject(callee);
35 * // callee() and calleev() will now assert.
37 * // It's always fine to access thisv().
38 * HandleValue thisv = args.thisv();
39 * args.rval().set(thisv);
41 * // As the return value was last set to |this|, returns |this|.
42 * return true;
43 * }
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.
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.
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.
63 #ifndef js_CallArgs_h
64 #define js_CallArgs_h
66 #include "mozilla/Assertions.h"
67 #include "mozilla/Attributes.h"
69 #include <type_traits>
71 #include "jstypes.h"
73 #include "js/RootingAPI.h"
74 #include "js/Value.h"
76 /* Typedef for native functions called by the JS VM. */
77 using JSNative = bool (*)(JSContext* cx, unsigned argc, JS::Value* vp);
79 namespace JS {
81 extern JS_PUBLIC_DATA const HandleValue UndefinedHandleValue;
83 namespace detail {
86 * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
87 * replacing with the global object as necessary.
89 extern JS_PUBLIC_API bool ComputeThis(JSContext* cx, JS::Value* vp,
90 MutableHandleObject thisObject);
92 #ifdef JS_DEBUG
93 extern JS_PUBLIC_API void CheckIsValidConstructible(const Value& v);
94 #endif
96 class MOZ_STACK_CLASS IncludeUsedRval {
97 mutable bool usedRval_ = false;
99 public:
100 bool usedRval() const { return usedRval_; }
101 void setUsedRval() const { usedRval_ = true; }
102 void clearUsedRval() const { usedRval_ = false; }
103 void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
106 class MOZ_STACK_CLASS NoUsedRval {
107 public:
108 bool usedRval() const { return false; }
109 void setUsedRval() const {}
110 void clearUsedRval() const {}
111 void assertUnusedRval() const {}
114 template <class WantUsedRval>
115 class MOZ_STACK_CLASS CallArgsBase {
116 static_assert(std::is_same_v<WantUsedRval, IncludeUsedRval> ||
117 std::is_same_v<WantUsedRval, NoUsedRval>,
118 "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
120 protected:
121 Value* argv_ = nullptr;
122 unsigned argc_ = 0;
123 bool constructing_ : 1;
125 // True if the caller does not use the return value.
126 bool ignoresReturnValue_ : 1;
128 #ifdef JS_DEBUG
129 WantUsedRval wantUsedRval_;
130 bool usedRval() const { return wantUsedRval_.usedRval(); }
131 void setUsedRval() const { wantUsedRval_.setUsedRval(); }
132 void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
133 void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
134 #else
135 bool usedRval() const { return false; }
136 void setUsedRval() const {}
137 void clearUsedRval() const {}
138 void assertUnusedRval() const {}
139 #endif
141 CallArgsBase() : constructing_(false), ignoresReturnValue_(false) {}
143 public:
144 // CALLEE ACCESS
147 * Returns the function being called, as a value. Must not be called after
148 * rval() has been used!
150 HandleValue calleev() const {
151 this->assertUnusedRval();
152 return HandleValue::fromMarkedLocation(&argv_[-2]);
156 * Returns the function being called, as an object. Must not be called
157 * after rval() has been used!
159 JSObject& callee() const { return calleev().toObject(); }
161 // CALLING/CONSTRUCTING-DIFFERENTIATIONS
163 bool isConstructing() const {
164 if (!argv_[-1].isMagic()) {
165 return false;
168 #ifdef JS_DEBUG
169 if (!this->usedRval()) {
170 CheckIsValidConstructible(calleev());
172 #endif
174 return true;
177 bool ignoresReturnValue() const { return ignoresReturnValue_; }
179 MutableHandleValue newTarget() const {
180 MOZ_ASSERT(constructing_);
181 return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
185 * Returns the |this| value passed to the function. This method must not
186 * be called when the function is being called as a constructor via |new|.
187 * The value may or may not be an object: it is the individual function's
188 * responsibility to box the value if needed.
190 HandleValue thisv() const {
191 // Some internal code uses thisv() in constructing cases, so don't do
192 // this yet.
193 // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
194 return HandleValue::fromMarkedLocation(&argv_[-1]);
197 bool computeThis(JSContext* cx, MutableHandleObject thisObject) const {
198 if (thisv().isObject()) {
199 thisObject.set(&thisv().toObject());
200 return true;
203 return ComputeThis(cx, base(), thisObject);
206 // ARGUMENTS
208 /* Returns the number of arguments. */
209 unsigned length() const { return argc_; }
211 /* Returns the i-th zero-indexed argument. */
212 MutableHandleValue operator[](unsigned i) const {
213 MOZ_ASSERT(i < argc_);
214 return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
218 * Returns the i-th zero-indexed argument, or |undefined| if there's no
219 * such argument.
221 HandleValue get(unsigned i) const {
222 return i < length() ? HandleValue::fromMarkedLocation(&this->argv_[i])
223 : UndefinedHandleValue;
227 * Returns true if the i-th zero-indexed argument is present and is not
228 * |undefined|.
230 bool hasDefined(unsigned i) const {
231 return i < argc_ && !this->argv_[i].isUndefined();
234 // RETURN VALUE
237 * Returns the currently-set return value. The initial contents of this
238 * value are unspecified. Once this method has been called, callee() and
239 * calleev() can no longer be used. (If you're compiling against a debug
240 * build of SpiderMonkey, these methods will assert to aid debugging.)
242 * If the method you're implementing succeeds by returning true, you *must*
243 * set this. (SpiderMonkey doesn't currently assert this, but it will do
244 * so eventually.) You don't need to use or change this if your method
245 * fails.
247 MutableHandleValue rval() const {
248 this->setUsedRval();
249 return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
253 * Returns true if there are at least |required| arguments passed in. If
254 * false, it reports an error message on the context.
256 JS_PUBLIC_API inline bool requireAtLeast(JSContext* cx, const char* fnname,
257 unsigned required) const;
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!
264 void setCallee(const Value& aCalleev) const {
265 this->clearUsedRval();
266 argv_[-2] = aCalleev;
269 void setThis(const Value& aThisv) const { argv_[-1] = aThisv; }
271 MutableHandleValue mutableThisv() const {
272 return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
275 public:
276 // These methods are publicly exposed, but we're unsure of the interfaces
277 // (because they're hackish and drop assertions). Avoid using these if you
278 // can.
280 Value* array() const { return argv_; }
281 Value* end() const { return argv_ + argc_ + constructing_; }
283 public:
284 // These methods are only intended for internal use. Embedders shouldn't
285 // use them!
287 Value* base() const { return argv_ - 2; }
289 Value* spAfterCall() const {
290 this->setUsedRval();
291 return argv_ - 1;
295 } // namespace detail
297 class MOZ_STACK_CLASS CallArgs
298 : public detail::CallArgsBase<detail::IncludeUsedRval> {
299 private:
300 friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
301 friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
302 bool constructing, bool ignoresReturnValue);
304 static CallArgs create(unsigned argc, Value* argv, bool constructing,
305 bool ignoresReturnValue = false) {
306 CallArgs args;
307 args.clearUsedRval();
308 args.argv_ = argv;
309 args.argc_ = argc;
310 args.constructing_ = constructing;
311 args.ignoresReturnValue_ = ignoresReturnValue;
312 #ifdef DEBUG
313 AssertValueIsNotGray(args.thisv());
314 AssertValueIsNotGray(args.calleev());
315 for (unsigned i = 0; i < argc; ++i) {
316 AssertValueIsNotGray(argv[i]);
318 #endif
319 return args;
322 public:
324 * Helper for requireAtLeast to report the actual exception. Public
325 * so we can call it from CallArgsBase and not need multiple
326 * per-template instantiations of it.
328 static JS_PUBLIC_API void reportMoreArgsNeeded(JSContext* cx,
329 const char* fnname,
330 unsigned required,
331 unsigned actual);
334 namespace detail {
335 template <class WantUsedRval>
336 JS_PUBLIC_API inline bool CallArgsBase<WantUsedRval>::requireAtLeast(
337 JSContext* cx, const char* fnname, unsigned required) const {
338 if (MOZ_LIKELY(required <= length())) {
339 return true;
342 CallArgs::reportMoreArgsNeeded(cx, fnname, required, length());
343 return false;
345 } // namespace detail
347 MOZ_ALWAYS_INLINE CallArgs CallArgsFromVp(unsigned argc, Value* vp) {
348 return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
351 // This method is only intended for internal use in SpiderMonkey. We may
352 // eventually move it to an internal header. Embedders should use
353 // JS::CallArgsFromVp!
354 MOZ_ALWAYS_INLINE CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp,
355 bool constructing = false,
356 bool ignoresReturnValue = false) {
357 return CallArgs::create(stackSlots - constructing, sp - stackSlots,
358 constructing, ignoresReturnValue);
361 } // namespace JS
363 #endif /* js_CallArgs_h */