js/public/CallNonGenericMethod.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/public/CallNonGenericMethod.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,117 @@
     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 +#ifndef js_CallNonGenericMethod_h
    1.11 +#define js_CallNonGenericMethod_h
    1.12 +
    1.13 +#include "jstypes.h"
    1.14 +
    1.15 +#include "js/CallArgs.h"
    1.16 +
    1.17 +namespace JS {
    1.18 +
    1.19 +// Returns true if |v| is considered an acceptable this-value.
    1.20 +typedef bool (*IsAcceptableThis)(HandleValue v);
    1.21 +
    1.22 +// Implements the guts of a method; guaranteed to be provided an acceptable
    1.23 +// this-value, as determined by a corresponding IsAcceptableThis method.
    1.24 +typedef bool (*NativeImpl)(JSContext *cx, CallArgs args);
    1.25 +
    1.26 +namespace detail {
    1.27 +
    1.28 +// DON'T CALL THIS DIRECTLY.  It's for use only by CallNonGenericMethod!
    1.29 +extern JS_PUBLIC_API(bool)
    1.30 +CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
    1.31 +
    1.32 +} // namespace detail
    1.33 +
    1.34 +// Methods usually act upon |this| objects only from a single global object and
    1.35 +// compartment.  Sometimes, however, a method must act upon |this| values from
    1.36 +// multiple global objects or compartments.  In such cases the |this| value a
    1.37 +// method might see will be wrapped, such that various access to the object --
    1.38 +// to its class, its private data, its reserved slots, and so on -- will not
    1.39 +// work properly without entering that object's compartment.  This method
    1.40 +// implements a solution to this problem.
    1.41 +//
    1.42 +// To implement a method that accepts |this| values from multiple compartments,
    1.43 +// define two functions.  The first function matches the IsAcceptableThis type
    1.44 +// and indicates whether the provided value is an acceptable |this| for the
    1.45 +// method; it must be a pure function only of its argument.
    1.46 +//
    1.47 +//   static const JSClass AnswerClass = { ... };
    1.48 +//
    1.49 +//   static bool
    1.50 +//   IsAnswerObject(const Value &v)
    1.51 +//   {
    1.52 +//       if (!v.isObject())
    1.53 +//           return false;
    1.54 +//       return JS_GetClass(&v.toObject()) == &AnswerClass;
    1.55 +//   }
    1.56 +//
    1.57 +// The second function implements the NativeImpl signature and defines the
    1.58 +// behavior of the method when it is provided an acceptable |this| value.
    1.59 +// Aside from some typing niceties -- see the CallArgs interface for details --
    1.60 +// its interface is the same as that of JSNative.
    1.61 +//
    1.62 +//   static bool
    1.63 +//   answer_getAnswer_impl(JSContext *cx, JS::CallArgs args)
    1.64 +//   {
    1.65 +//       args.rval().setInt32(42);
    1.66 +//       return true;
    1.67 +//   }
    1.68 +//
    1.69 +// The implementation function is guaranteed to be called *only* with a |this|
    1.70 +// value which is considered acceptable.
    1.71 +//
    1.72 +// Now to implement the actual method, write a JSNative that calls the method
    1.73 +// declared below, passing the appropriate template and runtime arguments.
    1.74 +//
    1.75 +//   static bool
    1.76 +//   answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp)
    1.77 +//   {
    1.78 +//       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1.79 +//       return JS::CallNonGenericMethod<IsAnswerObject, answer_getAnswer_impl>(cx, args);
    1.80 +//   }
    1.81 +//
    1.82 +// Note that, because they are used as template arguments, the predicate
    1.83 +// and implementation functions must have external linkage. (This is
    1.84 +// unfortunate, but GCC wasn't inlining things as one would hope when we
    1.85 +// passed them as function arguments.)
    1.86 +//
    1.87 +// JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable.  If
    1.88 +// it is, it will call the provided implementation function, which will return
    1.89 +// a value and indicate success.  If it is not, it will attempt to unwrap
    1.90 +// |this| and call the implementation function on the unwrapped |this|.  If
    1.91 +// that succeeds, all well and good.  If it doesn't succeed, a TypeError will
    1.92 +// be thrown.
    1.93 +//
    1.94 +// Note: JS::CallNonGenericMethod will only work correctly if it's called in
    1.95 +//       tail position in a JSNative.  Do not call it from any other place.
    1.96 +//
    1.97 +template<IsAcceptableThis Test, NativeImpl Impl>
    1.98 +MOZ_ALWAYS_INLINE bool
    1.99 +CallNonGenericMethod(JSContext *cx, CallArgs args)
   1.100 +{
   1.101 +    HandleValue thisv = args.thisv();
   1.102 +    if (Test(thisv))
   1.103 +        return Impl(cx, args);
   1.104 +
   1.105 +    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
   1.106 +}
   1.107 +
   1.108 +MOZ_ALWAYS_INLINE bool
   1.109 +CallNonGenericMethod(JSContext *cx, IsAcceptableThis Test, NativeImpl Impl, CallArgs args)
   1.110 +{
   1.111 +    HandleValue thisv = args.thisv();
   1.112 +    if (Test(thisv))
   1.113 +        return Impl(cx, args);
   1.114 +
   1.115 +    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
   1.116 +}
   1.117 +
   1.118 +} // namespace JS
   1.119 +
   1.120 +#endif /* js_CallNonGenericMethod_h */

mercurial