js/public/CallArgs.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * Helper classes encapsulating access to the callee, |this| value, arguments,
michael@0 9 * and argument count for a function call.
michael@0 10 *
michael@0 11 * The intent of JS::CallArgs and JS::CallReceiver is that they be used to
michael@0 12 * encapsulate access to the un-abstracted |unsigned argc, Value *vp| arguments
michael@0 13 * to a function. It's possible (albeit deprecated) to manually index into
michael@0 14 * |vp| to access the callee, |this|, and arguments of a function, and to set
michael@0 15 * its return value. It's also possible to use the supported API of JS_CALLEE,
michael@0 16 * JS_THIS, JS_ARGV, JS_RVAL and JS_SET_RVAL to the same ends. But neither API
michael@0 17 * has the error-handling or moving-GC correctness of CallArgs or CallReceiver.
michael@0 18 * New code should use CallArgs and CallReceiver instead whenever possible.
michael@0 19 *
michael@0 20 * The eventual plan is to change JSNative to take |const CallArgs&| directly,
michael@0 21 * for automatic assertion of correct use and to make calling functions more
michael@0 22 * efficient. Embedders should start internally switching away from using
michael@0 23 * |argc| and |vp| directly, except to create a |CallArgs|. Then, when an
michael@0 24 * eventual release making that change occurs, porting efforts will require
michael@0 25 * changing methods' signatures but won't require invasive changes to the
michael@0 26 * methods' implementations, potentially under time pressure.
michael@0 27 */
michael@0 28
michael@0 29 #ifndef js_CallArgs_h
michael@0 30 #define js_CallArgs_h
michael@0 31
michael@0 32 #include "mozilla/Assertions.h"
michael@0 33 #include "mozilla/Attributes.h"
michael@0 34 #include "mozilla/TypeTraits.h"
michael@0 35
michael@0 36 #include "jstypes.h"
michael@0 37
michael@0 38 #include "js/RootingAPI.h"
michael@0 39 #include "js/Value.h"
michael@0 40
michael@0 41 /* Typedef for native functions called by the JS VM. */
michael@0 42 typedef bool
michael@0 43 (* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
michael@0 44
michael@0 45 /* Typedef for native functions that may be called in parallel. */
michael@0 46 typedef bool
michael@0 47 (* JSParallelNative)(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
michael@0 48
michael@0 49 /*
michael@0 50 * Typedef for native functions that may be called either in parallel or
michael@0 51 * sequential execution.
michael@0 52 */
michael@0 53 typedef bool
michael@0 54 (* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp);
michael@0 55
michael@0 56 /*
michael@0 57 * Convenience wrappers for passing in ThreadSafeNative to places that expect
michael@0 58 * a JSNative or a JSParallelNative.
michael@0 59 */
michael@0 60 template <JSThreadSafeNative threadSafeNative>
michael@0 61 inline bool
michael@0 62 JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp);
michael@0 63
michael@0 64 template <JSThreadSafeNative threadSafeNative>
michael@0 65 inline bool
michael@0 66 JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
michael@0 67
michael@0 68 /*
michael@0 69 * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
michael@0 70 * replacing with the global object as necessary.
michael@0 71 *
michael@0 72 * This method will go away at some point: instead use |args.thisv()|. If the
michael@0 73 * value is an object, no further work is required. If that value is |null| or
michael@0 74 * |undefined|, use |JS_GetGlobalForObject| to compute the global object. If
michael@0 75 * the value is some other primitive, use |JS_ValueToObject| to box it.
michael@0 76 */
michael@0 77 extern JS_PUBLIC_API(JS::Value)
michael@0 78 JS_ComputeThis(JSContext *cx, JS::Value *vp);
michael@0 79
michael@0 80 namespace JS {
michael@0 81
michael@0 82 extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
michael@0 83
michael@0 84 /*
michael@0 85 * JS::CallReceiver encapsulates access to the callee, |this|, and eventual
michael@0 86 * return value for a function call. The principal way to create a
michael@0 87 * CallReceiver is using JS::CallReceiverFromVp:
michael@0 88 *
michael@0 89 * static bool
michael@0 90 * FunctionReturningThis(JSContext *cx, unsigned argc, JS::Value *vp)
michael@0 91 * {
michael@0 92 * JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
michael@0 93 *
michael@0 94 * // Access to the callee must occur before accessing/setting
michael@0 95 * // the return value.
michael@0 96 * JSObject &callee = rec.callee();
michael@0 97 * rec.rval().set(JS::ObjectValue(callee));
michael@0 98 *
michael@0 99 * // callee() and calleev() will now assert.
michael@0 100 *
michael@0 101 * // It's always fine to access thisv().
michael@0 102 * HandleValue thisv = rec.thisv();
michael@0 103 * rec.rval().set(thisv);
michael@0 104 *
michael@0 105 * // As the return value was last set to |this|, returns |this|.
michael@0 106 * return true;
michael@0 107 * }
michael@0 108 *
michael@0 109 * A note on JS_ComputeThis and JS_THIS_OBJECT: these methods currently aren't
michael@0 110 * part of the CallReceiver interface. We will likely add them at some point.
michael@0 111 * Until then, you should probably continue using |vp| directly for these two
michael@0 112 * cases.
michael@0 113 *
michael@0 114 * CallReceiver is exposed publicly and used internally. Not all parts of its
michael@0 115 * public interface are meant to be used by embedders! See inline comments to
michael@0 116 * for details.
michael@0 117 */
michael@0 118
michael@0 119 namespace detail {
michael@0 120
michael@0 121 #ifdef JS_DEBUG
michael@0 122 extern JS_PUBLIC_API(void)
michael@0 123 CheckIsValidConstructible(Value v);
michael@0 124 #endif
michael@0 125
michael@0 126 enum UsedRval { IncludeUsedRval, NoUsedRval };
michael@0 127
michael@0 128 template<UsedRval WantUsedRval>
michael@0 129 class MOZ_STACK_CLASS UsedRvalBase;
michael@0 130
michael@0 131 template<>
michael@0 132 class MOZ_STACK_CLASS UsedRvalBase<IncludeUsedRval>
michael@0 133 {
michael@0 134 protected:
michael@0 135 mutable bool usedRval_;
michael@0 136 void setUsedRval() const { usedRval_ = true; }
michael@0 137 void clearUsedRval() const { usedRval_ = false; }
michael@0 138 };
michael@0 139
michael@0 140 template<>
michael@0 141 class MOZ_STACK_CLASS UsedRvalBase<NoUsedRval>
michael@0 142 {
michael@0 143 protected:
michael@0 144 void setUsedRval() const {}
michael@0 145 void clearUsedRval() const {}
michael@0 146 };
michael@0 147
michael@0 148 template<UsedRval WantUsedRval>
michael@0 149 class MOZ_STACK_CLASS CallReceiverBase : public UsedRvalBase<
michael@0 150 #ifdef JS_DEBUG
michael@0 151 WantUsedRval
michael@0 152 #else
michael@0 153 NoUsedRval
michael@0 154 #endif
michael@0 155 >
michael@0 156 {
michael@0 157 protected:
michael@0 158 Value *argv_;
michael@0 159
michael@0 160 public:
michael@0 161 /*
michael@0 162 * Returns the function being called, as an object. Must not be called
michael@0 163 * after rval() has been used!
michael@0 164 */
michael@0 165 JSObject &callee() const {
michael@0 166 MOZ_ASSERT(!this->usedRval_);
michael@0 167 return argv_[-2].toObject();
michael@0 168 }
michael@0 169
michael@0 170 /*
michael@0 171 * Returns the function being called, as a value. Must not be called after
michael@0 172 * rval() has been used!
michael@0 173 */
michael@0 174 HandleValue calleev() const {
michael@0 175 MOZ_ASSERT(!this->usedRval_);
michael@0 176 return HandleValue::fromMarkedLocation(&argv_[-2]);
michael@0 177 }
michael@0 178
michael@0 179 /*
michael@0 180 * Returns the |this| value passed to the function. This method must not
michael@0 181 * be called when the function is being called as a constructor via |new|.
michael@0 182 * The value may or may not be an object: it is the individual function's
michael@0 183 * responsibility to box the value if needed.
michael@0 184 */
michael@0 185 HandleValue thisv() const {
michael@0 186 // Some internal code uses thisv() in constructing cases, so don't do
michael@0 187 // this yet.
michael@0 188 // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
michael@0 189 return HandleValue::fromMarkedLocation(&argv_[-1]);
michael@0 190 }
michael@0 191
michael@0 192 Value computeThis(JSContext *cx) const {
michael@0 193 if (thisv().isObject())
michael@0 194 return thisv();
michael@0 195
michael@0 196 return JS_ComputeThis(cx, base());
michael@0 197 }
michael@0 198
michael@0 199 bool isConstructing() const {
michael@0 200 #ifdef JS_DEBUG
michael@0 201 if (this->usedRval_)
michael@0 202 CheckIsValidConstructible(calleev());
michael@0 203 #endif
michael@0 204 return argv_[-1].isMagic();
michael@0 205 }
michael@0 206
michael@0 207 /*
michael@0 208 * Returns the currently-set return value. The initial contents of this
michael@0 209 * value are unspecified. Once this method has been called, callee() and
michael@0 210 * calleev() can no longer be used. (If you're compiling against a debug
michael@0 211 * build of SpiderMonkey, these methods will assert to aid debugging.)
michael@0 212 *
michael@0 213 * If the method you're implementing succeeds by returning true, you *must*
michael@0 214 * set this. (SpiderMonkey doesn't currently assert this, but it will do
michael@0 215 * so eventually.) You don't need to use or change this if your method
michael@0 216 * fails.
michael@0 217 */
michael@0 218 MutableHandleValue rval() const {
michael@0 219 this->setUsedRval();
michael@0 220 return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
michael@0 221 }
michael@0 222
michael@0 223 public:
michael@0 224 // These methods are only intended for internal use. Embedders shouldn't
michael@0 225 // use them!
michael@0 226
michael@0 227 Value *base() const { return argv_ - 2; }
michael@0 228
michael@0 229 Value *spAfterCall() const {
michael@0 230 this->setUsedRval();
michael@0 231 return argv_ - 1;
michael@0 232 }
michael@0 233
michael@0 234 public:
michael@0 235 // These methods are publicly exposed, but they are *not* to be used when
michael@0 236 // implementing a JSNative method and encapsulating access to |vp| within
michael@0 237 // it. You probably don't want to use these!
michael@0 238
michael@0 239 void setCallee(Value aCalleev) const {
michael@0 240 this->clearUsedRval();
michael@0 241 argv_[-2] = aCalleev;
michael@0 242 }
michael@0 243
michael@0 244 void setThis(Value aThisv) const {
michael@0 245 argv_[-1] = aThisv;
michael@0 246 }
michael@0 247
michael@0 248 MutableHandleValue mutableThisv() const {
michael@0 249 return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
michael@0 250 }
michael@0 251 };
michael@0 252
michael@0 253 } // namespace detail
michael@0 254
michael@0 255 class MOZ_STACK_CLASS CallReceiver : public detail::CallReceiverBase<detail::IncludeUsedRval>
michael@0 256 {
michael@0 257 private:
michael@0 258 friend CallReceiver CallReceiverFromVp(Value *vp);
michael@0 259 friend CallReceiver CallReceiverFromArgv(Value *argv);
michael@0 260 };
michael@0 261
michael@0 262 MOZ_ALWAYS_INLINE CallReceiver
michael@0 263 CallReceiverFromArgv(Value *argv)
michael@0 264 {
michael@0 265 CallReceiver receiver;
michael@0 266 receiver.clearUsedRval();
michael@0 267 receiver.argv_ = argv;
michael@0 268 return receiver;
michael@0 269 }
michael@0 270
michael@0 271 MOZ_ALWAYS_INLINE CallReceiver
michael@0 272 CallReceiverFromVp(Value *vp)
michael@0 273 {
michael@0 274 return CallReceiverFromArgv(vp + 2);
michael@0 275 }
michael@0 276
michael@0 277 /*
michael@0 278 * JS::CallArgs encapsulates everything JS::CallReceiver does, plus access to
michael@0 279 * the function call's arguments. The principal way to create a CallArgs is
michael@0 280 * like so, using JS::CallArgsFromVp:
michael@0 281 *
michael@0 282 * static bool
michael@0 283 * FunctionReturningArgcTimesArg0(JSContext *cx, unsigned argc, JS::Value *vp)
michael@0 284 * {
michael@0 285 * JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 286 *
michael@0 287 * // Guard against no arguments or a non-numeric arg0.
michael@0 288 * if (args.length() == 0 || !args[0].isNumber()) {
michael@0 289 * args.rval().setInt32(0);
michael@0 290 * return true;
michael@0 291 * }
michael@0 292 *
michael@0 293 * args.rval().set(JS::NumberValue(args.length() * args[0].toNumber()));
michael@0 294 * return true;
michael@0 295 * }
michael@0 296 *
michael@0 297 * CallArgs is exposed publicly and used internally. Not all parts of its
michael@0 298 * public interface are meant to be used by embedders! See inline comments to
michael@0 299 * for details.
michael@0 300 */
michael@0 301 namespace detail {
michael@0 302
michael@0 303 template<UsedRval WantUsedRval>
michael@0 304 class MOZ_STACK_CLASS CallArgsBase :
michael@0 305 public mozilla::Conditional<WantUsedRval == detail::IncludeUsedRval,
michael@0 306 CallReceiver,
michael@0 307 CallReceiverBase<NoUsedRval> >::Type
michael@0 308 {
michael@0 309 protected:
michael@0 310 unsigned argc_;
michael@0 311
michael@0 312 public:
michael@0 313 /* Returns the number of arguments. */
michael@0 314 unsigned length() const { return argc_; }
michael@0 315
michael@0 316 /* Returns the i-th zero-indexed argument. */
michael@0 317 MutableHandleValue operator[](unsigned i) const {
michael@0 318 MOZ_ASSERT(i < argc_);
michael@0 319 return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
michael@0 320 }
michael@0 321
michael@0 322 /*
michael@0 323 * Returns the i-th zero-indexed argument, or |undefined| if there's no
michael@0 324 * such argument.
michael@0 325 */
michael@0 326 HandleValue get(unsigned i) const {
michael@0 327 return i < length()
michael@0 328 ? HandleValue::fromMarkedLocation(&this->argv_[i])
michael@0 329 : UndefinedHandleValue;
michael@0 330 }
michael@0 331
michael@0 332 /*
michael@0 333 * Returns true if the i-th zero-indexed argument is present and is not
michael@0 334 * |undefined|.
michael@0 335 */
michael@0 336 bool hasDefined(unsigned i) const {
michael@0 337 return i < argc_ && !this->argv_[i].isUndefined();
michael@0 338 }
michael@0 339
michael@0 340 public:
michael@0 341 // These methods are publicly exposed, but we're less sure of the interface
michael@0 342 // here than we'd like (because they're hackish and drop assertions). Try
michael@0 343 // to avoid using these if you can.
michael@0 344
michael@0 345 Value *array() const { return this->argv_; }
michael@0 346 Value *end() const { return this->argv_ + argc_; }
michael@0 347 };
michael@0 348
michael@0 349 } // namespace detail
michael@0 350
michael@0 351 class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
michael@0 352 {
michael@0 353 private:
michael@0 354 friend CallArgs CallArgsFromVp(unsigned argc, Value *vp);
michael@0 355 friend CallArgs CallArgsFromSp(unsigned argc, Value *sp);
michael@0 356
michael@0 357 static CallArgs create(unsigned argc, Value *argv) {
michael@0 358 CallArgs args;
michael@0 359 args.clearUsedRval();
michael@0 360 args.argv_ = argv;
michael@0 361 args.argc_ = argc;
michael@0 362 return args;
michael@0 363 }
michael@0 364
michael@0 365 };
michael@0 366
michael@0 367 MOZ_ALWAYS_INLINE CallArgs
michael@0 368 CallArgsFromVp(unsigned argc, Value *vp)
michael@0 369 {
michael@0 370 return CallArgs::create(argc, vp + 2);
michael@0 371 }
michael@0 372
michael@0 373 // This method is only intended for internal use in SpiderMonkey. We may
michael@0 374 // eventually move it to an internal header. Embedders should use
michael@0 375 // JS::CallArgsFromVp!
michael@0 376 MOZ_ALWAYS_INLINE CallArgs
michael@0 377 CallArgsFromSp(unsigned argc, Value *sp)
michael@0 378 {
michael@0 379 return CallArgs::create(argc, sp - argc);
michael@0 380 }
michael@0 381
michael@0 382 } // namespace JS
michael@0 383
michael@0 384 /*
michael@0 385 * Macros to hide interpreter stack layout details from a JSNative using its
michael@0 386 * JS::Value *vp parameter. DO NOT USE THESE! Instead use JS::CallArgs and
michael@0 387 * friends, above. These macros will be removed when we change JSNative to
michael@0 388 * take a const JS::CallArgs&.
michael@0 389 */
michael@0 390
michael@0 391 #define JS_THIS_OBJECT(cx,vp) (JSVAL_TO_OBJECT(JS_THIS(cx,vp)))
michael@0 392
michael@0 393 /*
michael@0 394 * Note: if this method returns null, an error has occurred and must be
michael@0 395 * propagated or caught.
michael@0 396 */
michael@0 397 MOZ_ALWAYS_INLINE JS::Value
michael@0 398 JS_THIS(JSContext *cx, JS::Value *vp)
michael@0 399 {
michael@0 400 return JSVAL_IS_PRIMITIVE(vp[1]) ? JS_ComputeThis(cx, vp) : vp[1];
michael@0 401 }
michael@0 402
michael@0 403 /*
michael@0 404 * |this| is passed to functions in ES5 without change. Functions themselves
michael@0 405 * do any post-processing they desire to box |this|, compute the global object,
michael@0 406 * &c. This macro retrieves a function's unboxed |this| value.
michael@0 407 *
michael@0 408 * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
michael@0 409 * or vice versa. Either use the provided this value with this macro, or
michael@0 410 * compute the boxed |this| value using those. JS_THIS_VALUE must not be used
michael@0 411 * if the function is being called as a constructor.
michael@0 412 *
michael@0 413 * But: DO NOT USE THIS! Instead use JS::CallArgs::thisv(), above.
michael@0 414 *
michael@0 415 */
michael@0 416 #define JS_THIS_VALUE(cx,vp) ((vp)[1])
michael@0 417
michael@0 418 #endif /* js_CallArgs_h */

mercurial