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 */