| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* |
| * Helper classes encapsulating access to the callee, |this| value, arguments, |
| * and argument count for a function call. |
| * |
| * The intent of JS::CallArgs and JS::CallReceiver is that they be used to |
| * encapsulate access to the un-abstracted |unsigned argc, Value *vp| arguments |
| * to a function. It's possible (albeit deprecated) to manually index into |
| * |vp| to access the callee, |this|, and arguments of a function, and to set |
| * its return value. It's also possible to use the supported API of JS_CALLEE, |
| * JS_THIS, JS_ARGV, JS_RVAL and JS_SET_RVAL to the same ends. But neither API |
| * has the error-handling or moving-GC correctness of CallArgs or CallReceiver. |
| * New code should use CallArgs and CallReceiver instead whenever possible. |
| * |
| * The eventual plan is to change JSNative to take |const CallArgs&| directly, |
| * for automatic assertion of correct use and to make calling functions more |
| * efficient. Embedders should start internally switching away from using |
| * |argc| and |vp| directly, except to create a |CallArgs|. Then, when an |
| * eventual release making that change occurs, porting efforts will require |
| * changing methods' signatures but won't require invasive changes to the |
| * methods' implementations, potentially under time pressure. |
| */ |
| |
| #ifndef js_CallArgs_h |
| #define js_CallArgs_h |
| |
| #include "mozilla/Assertions.h" |
| #include "mozilla/Attributes.h" |
| #include "mozilla/TypeTraits.h" |
| |
| #include "jstypes.h" |
| |
| #include "js/RootingAPI.h" |
| #include "js/Value.h" |
| |
| struct JSContext; |
| class JSObject; |
| |
| /* Typedef for native functions called by the JS VM. */ |
| typedef JSBool |
| (* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp); |
| |
| /* |
| * Compute |this| for the |vp| inside a JSNative, either boxing primitives or |
| * replacing with the global object as necessary. |
| * |
| * This method will go away at some point: instead use |args.thisv()|. If the |
| * value is an object, no further work is required. If that value is |null| or |
| * |undefined|, use |JS_GetGlobalForObject| to compute the global object. If |
| * the value is some other primitive, use |JS_ValueToObject| to box it. |
| */ |
| extern JS_PUBLIC_API(JS::Value) |
| JS_ComputeThis(JSContext *cx, JS::Value *vp); |
| |
| namespace JS { |
| |
| extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue; |
| |
| /* |
| * JS::CallReceiver encapsulates access to the callee, |this|, and eventual |
| * return value for a function call. The principal way to create a |
| * CallReceiver is using JS::CallReceiverFromVp: |
| * |
| * static JSBool |
| * FunctionReturningThis(JSContext *cx, unsigned argc, JS::Value *vp) |
| * { |
| * JS::CallReceiver rec = JS::CallReceiverFromVp(vp); |
| * |
| * // Access to the callee must occur before accessing/setting |
| * // the return value. |
| * JSObject &callee = rec.callee(); |
| * rec.rval().set(JS::ObjectValue(callee)); |
| * |
| * // callee() and calleev() will now assert. |
| * |
| * // It's always fine to access thisv(). |
| * HandleValue thisv = rec.thisv(); |
| * rec.rval().set(thisv); |
| * |
| * // As the return value was last set to |this|, returns |this|. |
| * return true; |
| * } |
| * |
| * A note on JS_ComputeThis and JS_THIS_OBJECT: these methods currently aren't |
| * part of the CallReceiver interface. We will likely add them at some point. |
| * Until then, you should probably continue using |vp| directly for these two |
| * cases. |
| * |
| * CallReceiver is exposed publicly and used internally. Not all parts of its |
| * public interface are meant to be used by embedders! See inline comments to |
| * for details. |
| */ |
| |
| namespace detail { |
| |
| enum UsedRval { IncludeUsedRval, NoUsedRval }; |
| |
| template<UsedRval WantUsedRval> |
| class MOZ_STACK_CLASS UsedRvalBase; |
| |
| template<> |
| class MOZ_STACK_CLASS UsedRvalBase<IncludeUsedRval> |
| { |
| protected: |
| mutable bool usedRval_; |
| void setUsedRval() const { usedRval_ = true; } |
| void clearUsedRval() const { usedRval_ = false; } |
| }; |
| |
| template<> |
| class MOZ_STACK_CLASS UsedRvalBase<NoUsedRval> |
| { |
| protected: |
| void setUsedRval() const {} |
| void clearUsedRval() const {} |
| }; |
| |
| template<UsedRval WantUsedRval> |
| class MOZ_STACK_CLASS CallReceiverBase : public UsedRvalBase< |
| #ifdef JS_DEBUG |
| WantUsedRval |
| #else |
| NoUsedRval |
| #endif |
| > |
| { |
| protected: |
| Value *argv_; |
| |
| public: |
| /* |
| * Returns the function being called, as an object. Must not be called |
| * after rval() has been used! |
| */ |
| JSObject &callee() const { |
| MOZ_ASSERT(!this->usedRval_); |
| return argv_[-2].toObject(); |
| } |
| |
| /* |
| * Returns the function being called, as a value. Must not be called after |
| * rval() has been used! |
| */ |
| HandleValue calleev() const { |
| MOZ_ASSERT(!this->usedRval_); |
| return HandleValue::fromMarkedLocation(&argv_[-2]); |
| } |
| |
| /* |
| * Returns the |this| value passed to the function. This method must not |
| * be called when the function is being called as a constructor via |new|. |
| * The value may or may not be an object: it is the individual function's |
| * responsibility to box the value if needed. |
| */ |
| HandleValue thisv() const { |
| // Some internal code uses thisv() in constructing cases, so don't do |
| // this yet. |
| // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING)); |
| return HandleValue::fromMarkedLocation(&argv_[-1]); |
| } |
| |
| Value computeThis(JSContext *cx) const { |
| if (thisv().isObject()) |
| return thisv(); |
| |
| return JS_ComputeThis(cx, base()); |
| } |
| |
| /* |
| * Returns the currently-set return value. The initial contents of this |
| * value are unspecified. Once this method has been called, callee() and |
| * calleev() can no longer be used. (If you're compiling against a debug |
| * build of SpiderMonkey, these methods will assert to aid debugging.) |
| * |
| * If the method you're implementing succeeds by returning true, you *must* |
| * set this. (SpiderMonkey doesn't currently assert this, but it will do |
| * so eventually.) You don't need to use or change this if your method |
| * fails. |
| */ |
| MutableHandleValue rval() const { |
| this->setUsedRval(); |
| return MutableHandleValue::fromMarkedLocation(&argv_[-2]); |
| } |
| |
| public: |
| // These methods are only intended for internal use. Embedders shouldn't |
| // use them! |
| |
| Value *base() const { return argv_ - 2; } |
| |
| Value *spAfterCall() const { |
| this->setUsedRval(); |
| return argv_ - 1; |
| } |
| |
| public: |
| // These methods are publicly exposed, but they are *not* to be used when |
| // implementing a JSNative method and encapsulating access to |vp| within |
| // it. You probably don't want to use these! |
| |
| void setCallee(Value aCalleev) const { |
| this->clearUsedRval(); |
| argv_[-2] = aCalleev; |
| } |
| |
| void setThis(Value aThisv) const { |
| argv_[-1] = aThisv; |
| } |
| |
| MutableHandleValue mutableThisv() const { |
| return MutableHandleValue::fromMarkedLocation(&argv_[-1]); |
| } |
| }; |
| |
| } // namespace detail |
| |
| class MOZ_STACK_CLASS CallReceiver : public detail::CallReceiverBase<detail::IncludeUsedRval> |
| { |
| private: |
| friend CallReceiver CallReceiverFromVp(Value *vp); |
| friend CallReceiver CallReceiverFromArgv(Value *argv); |
| }; |
| |
| MOZ_ALWAYS_INLINE CallReceiver |
| CallReceiverFromArgv(Value *argv) |
| { |
| CallReceiver receiver; |
| receiver.clearUsedRval(); |
| receiver.argv_ = argv; |
| return receiver; |
| } |
| |
| MOZ_ALWAYS_INLINE CallReceiver |
| CallReceiverFromVp(Value *vp) |
| { |
| return CallReceiverFromArgv(vp + 2); |
| } |
| |
| /* |
| * JS::CallArgs encapsulates everything JS::CallReceiver does, plus access to |
| * the function call's arguments. The principal way to create a CallArgs is |
| * like so, using JS::CallArgsFromVp: |
| * |
| * static JSBool |
| * FunctionReturningArgcTimesArg0(JSContext *cx, unsigned argc, JS::Value *vp) |
| * { |
| * JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
| * |
| * // Guard against no arguments or a non-numeric arg0. |
| * if (args.length() == 0 || !args[0].isNumber()) { |
| * args.rval().setInt32(0); |
| * return true; |
| * } |
| * |
| * args.rval().set(JS::NumberValue(args.length() * args[0].toNumber())); |
| * return true; |
| * } |
| * |
| * CallArgs is exposed publicly and used internally. Not all parts of its |
| * public interface are meant to be used by embedders! See inline comments to |
| * for details. |
| */ |
| namespace detail { |
| |
| template<UsedRval WantUsedRval> |
| class MOZ_STACK_CLASS CallArgsBase : |
| public mozilla::Conditional<WantUsedRval == detail::IncludeUsedRval, |
| CallReceiver, |
| CallReceiverBase<NoUsedRval> >::Type |
| { |
| protected: |
| unsigned argc_; |
| |
| public: |
| /* Returns the number of arguments. */ |
| unsigned length() const { return argc_; } |
| |
| /* Returns the i-th zero-indexed argument. */ |
| Value &operator[](unsigned i) const { |
| MOZ_ASSERT(i < argc_); |
| return this->argv_[i]; |
| } |
| |
| /* Returns a mutable handle for the i-th zero-indexed argument. */ |
| MutableHandleValue handleAt(unsigned i) const { |
| MOZ_ASSERT(i < argc_); |
| return MutableHandleValue::fromMarkedLocation(&this->argv_[i]); |
| } |
| |
| /* |
| * Returns the i-th zero-indexed argument, or |undefined| if there's no |
| * such argument. |
| */ |
| Value get(unsigned i) const { |
| return i < length() ? this->argv_[i] : UndefinedValue(); |
| } |
| |
| /* |
| * Returns the i-th zero-indexed argument as a handle, or |undefined| if |
| * there's no such argument. |
| */ |
| HandleValue handleOrUndefinedAt(unsigned i) const { |
| return i < length() |
| ? HandleValue::fromMarkedLocation(&this->argv_[i]) |
| : UndefinedHandleValue; |
| } |
| |
| /* |
| * Returns true if the i-th zero-indexed argument is present and is not |
| * |undefined|. |
| */ |
| bool hasDefined(unsigned i) const { |
| return i < argc_ && !this->argv_[i].isUndefined(); |
| } |
| |
| public: |
| // These methods are publicly exposed, but we're less sure of the interface |
| // here than we'd like (because they're hackish and drop assertions). Try |
| // to avoid using these if you can. |
| |
| Value *array() const { return this->argv_; } |
| Value *end() const { return this->argv_ + argc_; } |
| }; |
| |
| } // namespace detail |
| |
| class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval> |
| { |
| private: |
| friend CallArgs CallArgsFromVp(unsigned argc, Value *vp); |
| friend CallArgs CallArgsFromSp(unsigned argc, Value *sp); |
| |
| static CallArgs create(unsigned argc, Value *argv) { |
| CallArgs args; |
| args.clearUsedRval(); |
| args.argv_ = argv; |
| args.argc_ = argc; |
| return args; |
| } |
| |
| }; |
| |
| MOZ_ALWAYS_INLINE CallArgs |
| CallArgsFromVp(unsigned argc, Value *vp) |
| { |
| return CallArgs::create(argc, vp + 2); |
| } |
| |
| // This method is only intended for internal use in SpiderMonkey. We may |
| // eventually move it to an internal header. Embedders should use |
| // JS::CallArgsFromVp! |
| MOZ_ALWAYS_INLINE CallArgs |
| CallArgsFromSp(unsigned argc, Value *sp) |
| { |
| return CallArgs::create(argc, sp - argc); |
| } |
| |
| } // namespace JS |
| |
| /* |
| * Macros to hide interpreter stack layout details from a JSNative using its |
| * JS::Value *vp parameter. DO NOT USE THESE! Instead use JS::CallArgs and |
| * friends, above. These macros will be removed when we change JSNative to |
| * take a const JS::CallArgs&. |
| */ |
| |
| #define JS_CALLEE(cx,vp) ((vp)[0]) |
| #define JS_THIS_OBJECT(cx,vp) (JSVAL_TO_OBJECT(JS_THIS(cx,vp))) |
| #define JS_ARGV(cx,vp) ((vp) + 2) |
| #define JS_RVAL(cx,vp) (*(vp)) |
| #define JS_SET_RVAL(cx,vp,v) (*(vp) = (v)) |
| |
| /* |
| * Note: if this method returns null, an error has occurred and must be |
| * propagated or caught. |
| */ |
| MOZ_ALWAYS_INLINE JS::Value |
| JS_THIS(JSContext *cx, JS::Value *vp) |
| { |
| return JSVAL_IS_PRIMITIVE(vp[1]) ? JS_ComputeThis(cx, vp) : vp[1]; |
| } |
| |
| /* |
| * |this| is passed to functions in ES5 without change. Functions themselves |
| * do any post-processing they desire to box |this|, compute the global object, |
| * &c. This macro retrieves a function's unboxed |this| value. |
| * |
| * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT, |
| * or vice versa. Either use the provided this value with this macro, or |
| * compute the boxed |this| value using those. JS_THIS_VALUE must not be used |
| * if the function is being called as a constructor. |
| * |
| * But: DO NOT USE THIS! Instead use JS::CallArgs::thisv(), above. |
| * |
| */ |
| #define JS_THIS_VALUE(cx,vp) ((vp)[1]) |
| |
| #endif /* js_CallArgs_h */ |