michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef js_CallNonGenericMethod_h michael@0: #define js_CallNonGenericMethod_h michael@0: michael@0: #include "jstypes.h" michael@0: michael@0: #include "js/CallArgs.h" michael@0: michael@0: namespace JS { michael@0: michael@0: // Returns true if |v| is considered an acceptable this-value. michael@0: typedef bool (*IsAcceptableThis)(HandleValue v); michael@0: michael@0: // Implements the guts of a method; guaranteed to be provided an acceptable michael@0: // this-value, as determined by a corresponding IsAcceptableThis method. michael@0: typedef bool (*NativeImpl)(JSContext *cx, CallArgs args); michael@0: michael@0: namespace detail { michael@0: michael@0: // DON'T CALL THIS DIRECTLY. It's for use only by CallNonGenericMethod! michael@0: extern JS_PUBLIC_API(bool) michael@0: CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); michael@0: michael@0: } // namespace detail michael@0: michael@0: // Methods usually act upon |this| objects only from a single global object and michael@0: // compartment. Sometimes, however, a method must act upon |this| values from michael@0: // multiple global objects or compartments. In such cases the |this| value a michael@0: // method might see will be wrapped, such that various access to the object -- michael@0: // to its class, its private data, its reserved slots, and so on -- will not michael@0: // work properly without entering that object's compartment. This method michael@0: // implements a solution to this problem. michael@0: // michael@0: // To implement a method that accepts |this| values from multiple compartments, michael@0: // define two functions. The first function matches the IsAcceptableThis type michael@0: // and indicates whether the provided value is an acceptable |this| for the michael@0: // method; it must be a pure function only of its argument. michael@0: // michael@0: // static const JSClass AnswerClass = { ... }; michael@0: // michael@0: // static bool michael@0: // IsAnswerObject(const Value &v) michael@0: // { michael@0: // if (!v.isObject()) michael@0: // return false; michael@0: // return JS_GetClass(&v.toObject()) == &AnswerClass; michael@0: // } michael@0: // michael@0: // The second function implements the NativeImpl signature and defines the michael@0: // behavior of the method when it is provided an acceptable |this| value. michael@0: // Aside from some typing niceties -- see the CallArgs interface for details -- michael@0: // its interface is the same as that of JSNative. michael@0: // michael@0: // static bool michael@0: // answer_getAnswer_impl(JSContext *cx, JS::CallArgs args) michael@0: // { michael@0: // args.rval().setInt32(42); michael@0: // return true; michael@0: // } michael@0: // michael@0: // The implementation function is guaranteed to be called *only* with a |this| michael@0: // value which is considered acceptable. michael@0: // michael@0: // Now to implement the actual method, write a JSNative that calls the method michael@0: // declared below, passing the appropriate template and runtime arguments. michael@0: // michael@0: // static bool michael@0: // answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp) michael@0: // { michael@0: // JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: // return JS::CallNonGenericMethod(cx, args); michael@0: // } michael@0: // michael@0: // Note that, because they are used as template arguments, the predicate michael@0: // and implementation functions must have external linkage. (This is michael@0: // unfortunate, but GCC wasn't inlining things as one would hope when we michael@0: // passed them as function arguments.) michael@0: // michael@0: // JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable. If michael@0: // it is, it will call the provided implementation function, which will return michael@0: // a value and indicate success. If it is not, it will attempt to unwrap michael@0: // |this| and call the implementation function on the unwrapped |this|. If michael@0: // that succeeds, all well and good. If it doesn't succeed, a TypeError will michael@0: // be thrown. michael@0: // michael@0: // Note: JS::CallNonGenericMethod will only work correctly if it's called in michael@0: // tail position in a JSNative. Do not call it from any other place. michael@0: // michael@0: template michael@0: MOZ_ALWAYS_INLINE bool michael@0: CallNonGenericMethod(JSContext *cx, CallArgs args) michael@0: { michael@0: HandleValue thisv = args.thisv(); michael@0: if (Test(thisv)) michael@0: return Impl(cx, args); michael@0: michael@0: return detail::CallMethodIfWrapped(cx, Test, Impl, args); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool michael@0: CallNonGenericMethod(JSContext *cx, IsAcceptableThis Test, NativeImpl Impl, CallArgs args) michael@0: { michael@0: HandleValue thisv = args.thisv(); michael@0: if (Test(thisv)) michael@0: return Impl(cx, args); michael@0: michael@0: return detail::CallMethodIfWrapped(cx, Test, Impl, args); michael@0: } michael@0: michael@0: } // namespace JS michael@0: michael@0: #endif /* js_CallNonGenericMethod_h */