js/public/CallNonGenericMethod.h

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     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 #ifndef js_CallNonGenericMethod_h
     8 #define js_CallNonGenericMethod_h
    10 #include "jstypes.h"
    12 #include "js/CallArgs.h"
    14 namespace JS {
    16 // Returns true if |v| is considered an acceptable this-value.
    17 typedef bool (*IsAcceptableThis)(HandleValue v);
    19 // Implements the guts of a method; guaranteed to be provided an acceptable
    20 // this-value, as determined by a corresponding IsAcceptableThis method.
    21 typedef bool (*NativeImpl)(JSContext *cx, CallArgs args);
    23 namespace detail {
    25 // DON'T CALL THIS DIRECTLY.  It's for use only by CallNonGenericMethod!
    26 extern JS_PUBLIC_API(bool)
    27 CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
    29 } // namespace detail
    31 // Methods usually act upon |this| objects only from a single global object and
    32 // compartment.  Sometimes, however, a method must act upon |this| values from
    33 // multiple global objects or compartments.  In such cases the |this| value a
    34 // method might see will be wrapped, such that various access to the object --
    35 // to its class, its private data, its reserved slots, and so on -- will not
    36 // work properly without entering that object's compartment.  This method
    37 // implements a solution to this problem.
    38 //
    39 // To implement a method that accepts |this| values from multiple compartments,
    40 // define two functions.  The first function matches the IsAcceptableThis type
    41 // and indicates whether the provided value is an acceptable |this| for the
    42 // method; it must be a pure function only of its argument.
    43 //
    44 //   static const JSClass AnswerClass = { ... };
    45 //
    46 //   static bool
    47 //   IsAnswerObject(const Value &v)
    48 //   {
    49 //       if (!v.isObject())
    50 //           return false;
    51 //       return JS_GetClass(&v.toObject()) == &AnswerClass;
    52 //   }
    53 //
    54 // The second function implements the NativeImpl signature and defines the
    55 // behavior of the method when it is provided an acceptable |this| value.
    56 // Aside from some typing niceties -- see the CallArgs interface for details --
    57 // its interface is the same as that of JSNative.
    58 //
    59 //   static bool
    60 //   answer_getAnswer_impl(JSContext *cx, JS::CallArgs args)
    61 //   {
    62 //       args.rval().setInt32(42);
    63 //       return true;
    64 //   }
    65 //
    66 // The implementation function is guaranteed to be called *only* with a |this|
    67 // value which is considered acceptable.
    68 //
    69 // Now to implement the actual method, write a JSNative that calls the method
    70 // declared below, passing the appropriate template and runtime arguments.
    71 //
    72 //   static bool
    73 //   answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp)
    74 //   {
    75 //       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    76 //       return JS::CallNonGenericMethod<IsAnswerObject, answer_getAnswer_impl>(cx, args);
    77 //   }
    78 //
    79 // Note that, because they are used as template arguments, the predicate
    80 // and implementation functions must have external linkage. (This is
    81 // unfortunate, but GCC wasn't inlining things as one would hope when we
    82 // passed them as function arguments.)
    83 //
    84 // JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable.  If
    85 // it is, it will call the provided implementation function, which will return
    86 // a value and indicate success.  If it is not, it will attempt to unwrap
    87 // |this| and call the implementation function on the unwrapped |this|.  If
    88 // that succeeds, all well and good.  If it doesn't succeed, a TypeError will
    89 // be thrown.
    90 //
    91 // Note: JS::CallNonGenericMethod will only work correctly if it's called in
    92 //       tail position in a JSNative.  Do not call it from any other place.
    93 //
    94 template<IsAcceptableThis Test, NativeImpl Impl>
    95 MOZ_ALWAYS_INLINE bool
    96 CallNonGenericMethod(JSContext *cx, CallArgs args)
    97 {
    98     HandleValue thisv = args.thisv();
    99     if (Test(thisv))
   100         return Impl(cx, args);
   102     return detail::CallMethodIfWrapped(cx, Test, Impl, args);
   103 }
   105 MOZ_ALWAYS_INLINE bool
   106 CallNonGenericMethod(JSContext *cx, IsAcceptableThis Test, NativeImpl Impl, CallArgs args)
   107 {
   108     HandleValue thisv = args.thisv();
   109     if (Test(thisv))
   110         return Impl(cx, args);
   112     return detail::CallMethodIfWrapped(cx, Test, Impl, args);
   113 }
   115 } // namespace JS
   117 #endif /* js_CallNonGenericMethod_h */

mercurial