js/public/CallArgs.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/public/CallArgs.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,418 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * Helper classes encapsulating access to the callee, |this| value, arguments,
    1.12 + * and argument count for a function call.
    1.13 + *
    1.14 + * The intent of JS::CallArgs and JS::CallReceiver is that they be used to
    1.15 + * encapsulate access to the un-abstracted |unsigned argc, Value *vp| arguments
    1.16 + * to a function.  It's possible (albeit deprecated) to manually index into
    1.17 + * |vp| to access the callee, |this|, and arguments of a function, and to set
    1.18 + * its return value.  It's also possible to use the supported API of JS_CALLEE,
    1.19 + * JS_THIS, JS_ARGV, JS_RVAL and JS_SET_RVAL to the same ends.  But neither API
    1.20 + * has the error-handling or moving-GC correctness of CallArgs or CallReceiver.
    1.21 + * New code should use CallArgs and CallReceiver instead whenever possible.
    1.22 + *
    1.23 + * The eventual plan is to change JSNative to take |const CallArgs&| directly,
    1.24 + * for automatic assertion of correct use and to make calling functions more
    1.25 + * efficient.  Embedders should start internally switching away from using
    1.26 + * |argc| and |vp| directly, except to create a |CallArgs|.  Then, when an
    1.27 + * eventual release making that change occurs, porting efforts will require
    1.28 + * changing methods' signatures but won't require invasive changes to the
    1.29 + * methods' implementations, potentially under time pressure.
    1.30 + */
    1.31 +
    1.32 +#ifndef js_CallArgs_h
    1.33 +#define js_CallArgs_h
    1.34 +
    1.35 +#include "mozilla/Assertions.h"
    1.36 +#include "mozilla/Attributes.h"
    1.37 +#include "mozilla/TypeTraits.h"
    1.38 +
    1.39 +#include "jstypes.h"
    1.40 +
    1.41 +#include "js/RootingAPI.h"
    1.42 +#include "js/Value.h"
    1.43 +
    1.44 +/* Typedef for native functions called by the JS VM. */
    1.45 +typedef bool
    1.46 +(* JSNative)(JSContext *cx, unsigned argc, JS::Value *vp);
    1.47 +
    1.48 +/* Typedef for native functions that may be called in parallel. */
    1.49 +typedef bool
    1.50 +(* JSParallelNative)(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
    1.51 +
    1.52 +/*
    1.53 + * Typedef for native functions that may be called either in parallel or
    1.54 + * sequential execution.
    1.55 + */
    1.56 +typedef bool
    1.57 +(* JSThreadSafeNative)(js::ThreadSafeContext *cx, unsigned argc, JS::Value *vp);
    1.58 +
    1.59 +/*
    1.60 + * Convenience wrappers for passing in ThreadSafeNative to places that expect
    1.61 + * a JSNative or a JSParallelNative.
    1.62 + */
    1.63 +template <JSThreadSafeNative threadSafeNative>
    1.64 +inline bool
    1.65 +JSNativeThreadSafeWrapper(JSContext *cx, unsigned argc, JS::Value *vp);
    1.66 +
    1.67 +template <JSThreadSafeNative threadSafeNative>
    1.68 +inline bool
    1.69 +JSParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, JS::Value *vp);
    1.70 +
    1.71 +/*
    1.72 + * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
    1.73 + * replacing with the global object as necessary.
    1.74 + *
    1.75 + * This method will go away at some point: instead use |args.thisv()|.  If the
    1.76 + * value is an object, no further work is required.  If that value is |null| or
    1.77 + * |undefined|, use |JS_GetGlobalForObject| to compute the global object.  If
    1.78 + * the value is some other primitive, use |JS_ValueToObject| to box it.
    1.79 + */
    1.80 +extern JS_PUBLIC_API(JS::Value)
    1.81 +JS_ComputeThis(JSContext *cx, JS::Value *vp);
    1.82 +
    1.83 +namespace JS {
    1.84 +
    1.85 +extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
    1.86 +
    1.87 +/*
    1.88 + * JS::CallReceiver encapsulates access to the callee, |this|, and eventual
    1.89 + * return value for a function call.  The principal way to create a
    1.90 + * CallReceiver is using JS::CallReceiverFromVp:
    1.91 + *
    1.92 + *   static bool
    1.93 + *   FunctionReturningThis(JSContext *cx, unsigned argc, JS::Value *vp)
    1.94 + *   {
    1.95 + *       JS::CallReceiver rec = JS::CallReceiverFromVp(vp);
    1.96 + *
    1.97 + *       // Access to the callee must occur before accessing/setting
    1.98 + *       // the return value.
    1.99 + *       JSObject &callee = rec.callee();
   1.100 + *       rec.rval().set(JS::ObjectValue(callee));
   1.101 + *
   1.102 + *       // callee() and calleev() will now assert.
   1.103 + *
   1.104 + *       // It's always fine to access thisv().
   1.105 + *       HandleValue thisv = rec.thisv();
   1.106 + *       rec.rval().set(thisv);
   1.107 + *
   1.108 + *       // As the return value was last set to |this|, returns |this|.
   1.109 + *       return true;
   1.110 + *   }
   1.111 + *
   1.112 + * A note on JS_ComputeThis and JS_THIS_OBJECT: these methods currently aren't
   1.113 + * part of the CallReceiver interface.  We will likely add them at some point.
   1.114 + * Until then, you should probably continue using |vp| directly for these two
   1.115 + * cases.
   1.116 + *
   1.117 + * CallReceiver is exposed publicly and used internally.  Not all parts of its
   1.118 + * public interface are meant to be used by embedders!  See inline comments to
   1.119 + * for details.
   1.120 + */
   1.121 +
   1.122 +namespace detail {
   1.123 +
   1.124 +#ifdef JS_DEBUG
   1.125 +extern JS_PUBLIC_API(void)
   1.126 +CheckIsValidConstructible(Value v);
   1.127 +#endif
   1.128 +
   1.129 +enum UsedRval { IncludeUsedRval, NoUsedRval };
   1.130 +
   1.131 +template<UsedRval WantUsedRval>
   1.132 +class MOZ_STACK_CLASS UsedRvalBase;
   1.133 +
   1.134 +template<>
   1.135 +class MOZ_STACK_CLASS UsedRvalBase<IncludeUsedRval>
   1.136 +{
   1.137 +  protected:
   1.138 +    mutable bool usedRval_;
   1.139 +    void setUsedRval() const { usedRval_ = true; }
   1.140 +    void clearUsedRval() const { usedRval_ = false; }
   1.141 +};
   1.142 +
   1.143 +template<>
   1.144 +class MOZ_STACK_CLASS UsedRvalBase<NoUsedRval>
   1.145 +{
   1.146 +  protected:
   1.147 +    void setUsedRval() const {}
   1.148 +    void clearUsedRval() const {}
   1.149 +};
   1.150 +
   1.151 +template<UsedRval WantUsedRval>
   1.152 +class MOZ_STACK_CLASS CallReceiverBase : public UsedRvalBase<
   1.153 +#ifdef JS_DEBUG
   1.154 +        WantUsedRval
   1.155 +#else
   1.156 +        NoUsedRval
   1.157 +#endif
   1.158 +    >
   1.159 +{
   1.160 +  protected:
   1.161 +    Value *argv_;
   1.162 +
   1.163 +  public:
   1.164 +    /*
   1.165 +     * Returns the function being called, as an object.  Must not be called
   1.166 +     * after rval() has been used!
   1.167 +     */
   1.168 +    JSObject &callee() const {
   1.169 +        MOZ_ASSERT(!this->usedRval_);
   1.170 +        return argv_[-2].toObject();
   1.171 +    }
   1.172 +
   1.173 +    /*
   1.174 +     * Returns the function being called, as a value.  Must not be called after
   1.175 +     * rval() has been used!
   1.176 +     */
   1.177 +    HandleValue calleev() const {
   1.178 +        MOZ_ASSERT(!this->usedRval_);
   1.179 +        return HandleValue::fromMarkedLocation(&argv_[-2]);
   1.180 +    }
   1.181 +
   1.182 +    /*
   1.183 +     * Returns the |this| value passed to the function.  This method must not
   1.184 +     * be called when the function is being called as a constructor via |new|.
   1.185 +     * The value may or may not be an object: it is the individual function's
   1.186 +     * responsibility to box the value if needed.
   1.187 +     */
   1.188 +    HandleValue thisv() const {
   1.189 +        // Some internal code uses thisv() in constructing cases, so don't do
   1.190 +        // this yet.
   1.191 +        // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
   1.192 +        return HandleValue::fromMarkedLocation(&argv_[-1]);
   1.193 +    }
   1.194 +
   1.195 +    Value computeThis(JSContext *cx) const {
   1.196 +        if (thisv().isObject())
   1.197 +            return thisv();
   1.198 +
   1.199 +        return JS_ComputeThis(cx, base());
   1.200 +    }
   1.201 +
   1.202 +    bool isConstructing() const {
   1.203 +#ifdef JS_DEBUG
   1.204 +        if (this->usedRval_)
   1.205 +            CheckIsValidConstructible(calleev());
   1.206 +#endif
   1.207 +        return argv_[-1].isMagic();
   1.208 +    }
   1.209 +
   1.210 +    /*
   1.211 +     * Returns the currently-set return value.  The initial contents of this
   1.212 +     * value are unspecified.  Once this method has been called, callee() and
   1.213 +     * calleev() can no longer be used.  (If you're compiling against a debug
   1.214 +     * build of SpiderMonkey, these methods will assert to aid debugging.)
   1.215 +     *
   1.216 +     * If the method you're implementing succeeds by returning true, you *must*
   1.217 +     * set this.  (SpiderMonkey doesn't currently assert this, but it will do
   1.218 +     * so eventually.)  You don't need to use or change this if your method
   1.219 +     * fails.
   1.220 +     */
   1.221 +    MutableHandleValue rval() const {
   1.222 +        this->setUsedRval();
   1.223 +        return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
   1.224 +    }
   1.225 +
   1.226 +  public:
   1.227 +    // These methods are only intended for internal use.  Embedders shouldn't
   1.228 +    // use them!
   1.229 +
   1.230 +    Value *base() const { return argv_ - 2; }
   1.231 +
   1.232 +    Value *spAfterCall() const {
   1.233 +        this->setUsedRval();
   1.234 +        return argv_ - 1;
   1.235 +    }
   1.236 +
   1.237 +  public:
   1.238 +    // These methods are publicly exposed, but they are *not* to be used when
   1.239 +    // implementing a JSNative method and encapsulating access to |vp| within
   1.240 +    // it.  You probably don't want to use these!
   1.241 +
   1.242 +    void setCallee(Value aCalleev) const {
   1.243 +        this->clearUsedRval();
   1.244 +        argv_[-2] = aCalleev;
   1.245 +    }
   1.246 +
   1.247 +    void setThis(Value aThisv) const {
   1.248 +        argv_[-1] = aThisv;
   1.249 +    }
   1.250 +
   1.251 +    MutableHandleValue mutableThisv() const {
   1.252 +        return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
   1.253 +    }
   1.254 +};
   1.255 +
   1.256 +} // namespace detail
   1.257 +
   1.258 +class MOZ_STACK_CLASS CallReceiver : public detail::CallReceiverBase<detail::IncludeUsedRval>
   1.259 +{
   1.260 +  private:
   1.261 +    friend CallReceiver CallReceiverFromVp(Value *vp);
   1.262 +    friend CallReceiver CallReceiverFromArgv(Value *argv);
   1.263 +};
   1.264 +
   1.265 +MOZ_ALWAYS_INLINE CallReceiver
   1.266 +CallReceiverFromArgv(Value *argv)
   1.267 +{
   1.268 +    CallReceiver receiver;
   1.269 +    receiver.clearUsedRval();
   1.270 +    receiver.argv_ = argv;
   1.271 +    return receiver;
   1.272 +}
   1.273 +
   1.274 +MOZ_ALWAYS_INLINE CallReceiver
   1.275 +CallReceiverFromVp(Value *vp)
   1.276 +{
   1.277 +    return CallReceiverFromArgv(vp + 2);
   1.278 +}
   1.279 +
   1.280 +/*
   1.281 + * JS::CallArgs encapsulates everything JS::CallReceiver does, plus access to
   1.282 + * the function call's arguments.  The principal way to create a CallArgs is
   1.283 + * like so, using JS::CallArgsFromVp:
   1.284 + *
   1.285 + *   static bool
   1.286 + *   FunctionReturningArgcTimesArg0(JSContext *cx, unsigned argc, JS::Value *vp)
   1.287 + *   {
   1.288 + *       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   1.289 + *
   1.290 + *       // Guard against no arguments or a non-numeric arg0.
   1.291 + *       if (args.length() == 0 || !args[0].isNumber()) {
   1.292 + *           args.rval().setInt32(0);
   1.293 + *           return true;
   1.294 + *       }
   1.295 + *
   1.296 + *       args.rval().set(JS::NumberValue(args.length() * args[0].toNumber()));
   1.297 + *       return true;
   1.298 + *   }
   1.299 + *
   1.300 + * CallArgs is exposed publicly and used internally.  Not all parts of its
   1.301 + * public interface are meant to be used by embedders!  See inline comments to
   1.302 + * for details.
   1.303 + */
   1.304 +namespace detail {
   1.305 +
   1.306 +template<UsedRval WantUsedRval>
   1.307 +class MOZ_STACK_CLASS CallArgsBase :
   1.308 +        public mozilla::Conditional<WantUsedRval == detail::IncludeUsedRval,
   1.309 +                                    CallReceiver,
   1.310 +                                    CallReceiverBase<NoUsedRval> >::Type
   1.311 +{
   1.312 +  protected:
   1.313 +    unsigned argc_;
   1.314 +
   1.315 +  public:
   1.316 +    /* Returns the number of arguments. */
   1.317 +    unsigned length() const { return argc_; }
   1.318 +
   1.319 +    /* Returns the i-th zero-indexed argument. */
   1.320 +    MutableHandleValue operator[](unsigned i) const {
   1.321 +        MOZ_ASSERT(i < argc_);
   1.322 +        return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
   1.323 +    }
   1.324 +
   1.325 +    /*
   1.326 +     * Returns the i-th zero-indexed argument, or |undefined| if there's no
   1.327 +     * such argument.
   1.328 +     */
   1.329 +    HandleValue get(unsigned i) const {
   1.330 +        return i < length()
   1.331 +               ? HandleValue::fromMarkedLocation(&this->argv_[i])
   1.332 +               : UndefinedHandleValue;
   1.333 +    }
   1.334 +
   1.335 +    /*
   1.336 +     * Returns true if the i-th zero-indexed argument is present and is not
   1.337 +     * |undefined|.
   1.338 +     */
   1.339 +    bool hasDefined(unsigned i) const {
   1.340 +        return i < argc_ && !this->argv_[i].isUndefined();
   1.341 +    }
   1.342 +
   1.343 +  public:
   1.344 +    // These methods are publicly exposed, but we're less sure of the interface
   1.345 +    // here than we'd like (because they're hackish and drop assertions).  Try
   1.346 +    // to avoid using these if you can.
   1.347 +
   1.348 +    Value *array() const { return this->argv_; }
   1.349 +    Value *end() const { return this->argv_ + argc_; }
   1.350 +};
   1.351 +
   1.352 +} // namespace detail
   1.353 +
   1.354 +class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
   1.355 +{
   1.356 +  private:
   1.357 +    friend CallArgs CallArgsFromVp(unsigned argc, Value *vp);
   1.358 +    friend CallArgs CallArgsFromSp(unsigned argc, Value *sp);
   1.359 +
   1.360 +    static CallArgs create(unsigned argc, Value *argv) {
   1.361 +        CallArgs args;
   1.362 +        args.clearUsedRval();
   1.363 +        args.argv_ = argv;
   1.364 +        args.argc_ = argc;
   1.365 +        return args;
   1.366 +    }
   1.367 +
   1.368 +};
   1.369 +
   1.370 +MOZ_ALWAYS_INLINE CallArgs
   1.371 +CallArgsFromVp(unsigned argc, Value *vp)
   1.372 +{
   1.373 +    return CallArgs::create(argc, vp + 2);
   1.374 +}
   1.375 +
   1.376 +// This method is only intended for internal use in SpiderMonkey.  We may
   1.377 +// eventually move it to an internal header.  Embedders should use
   1.378 +// JS::CallArgsFromVp!
   1.379 +MOZ_ALWAYS_INLINE CallArgs
   1.380 +CallArgsFromSp(unsigned argc, Value *sp)
   1.381 +{
   1.382 +    return CallArgs::create(argc, sp - argc);
   1.383 +}
   1.384 +
   1.385 +} // namespace JS
   1.386 +
   1.387 +/*
   1.388 + * Macros to hide interpreter stack layout details from a JSNative using its
   1.389 + * JS::Value *vp parameter.  DO NOT USE THESE!  Instead use JS::CallArgs and
   1.390 + * friends, above.  These macros will be removed when we change JSNative to
   1.391 + * take a const JS::CallArgs&.
   1.392 + */
   1.393 +
   1.394 +#define JS_THIS_OBJECT(cx,vp)   (JSVAL_TO_OBJECT(JS_THIS(cx,vp)))
   1.395 +
   1.396 +/*
   1.397 + * Note: if this method returns null, an error has occurred and must be
   1.398 + * propagated or caught.
   1.399 + */
   1.400 +MOZ_ALWAYS_INLINE JS::Value
   1.401 +JS_THIS(JSContext *cx, JS::Value *vp)
   1.402 +{
   1.403 +    return JSVAL_IS_PRIMITIVE(vp[1]) ? JS_ComputeThis(cx, vp) : vp[1];
   1.404 +}
   1.405 +
   1.406 +/*
   1.407 + * |this| is passed to functions in ES5 without change.  Functions themselves
   1.408 + * do any post-processing they desire to box |this|, compute the global object,
   1.409 + * &c.  This macro retrieves a function's unboxed |this| value.
   1.410 + *
   1.411 + * This macro must not be used in conjunction with JS_THIS or JS_THIS_OBJECT,
   1.412 + * or vice versa.  Either use the provided this value with this macro, or
   1.413 + * compute the boxed |this| value using those.  JS_THIS_VALUE must not be used
   1.414 + * if the function is being called as a constructor.
   1.415 + *
   1.416 + * But: DO NOT USE THIS!  Instead use JS::CallArgs::thisv(), above.
   1.417 + *
   1.418 + */
   1.419 +#define JS_THIS_VALUE(cx,vp)    ((vp)[1])
   1.420 +
   1.421 +#endif /* js_CallArgs_h */

mercurial