RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/js/js_call.h
00001 /* js_call.h                                                       -*- C++ -*-
00002    Jeremy Barnes, 15 November 2010
00003    Copyright (c) 2010 Datacratic.  All rights reserved.
00004 
00005    Functions to allow generic calling to/from Javascript.
00006 */
00007 
00008 #ifndef __js__js_call_h__
00009 #define __js__js_call_h__
00010 
00011 #include "js_call_fwd.h"
00012 #include "v8.h"
00013 #include "soa/js/js_utils.h"
00014 #include "soa/js/js_value.h"
00015 #include "jml/arch/exception.h"
00016 #include <boost/function.hpp>
00017 #include <boost/bind.hpp>
00018 
00019 
00020 namespace Datacratic {
00021 namespace JS {
00022 
00023 
00024 
00025 /*****************************************************************************/
00026 /* CALL FROM JS                                                              */
00027 /*****************************************************************************/
00028 
00029 
00030 // Given a boost::function type Fn and a TypeList of InPosition values,
00031 // this calls the function with the JS arguments unpacked
00032 template<typename Fn, typename List>
00033 struct CallWithList {
00034 };
00035 
00036 // Implementation of that template with the List argument unpacked
00037 template<typename Fn, typename... ArgsWithPosition>
00038 struct CallWithList<Fn, TypeList<ArgsWithPosition...> > {
00039     static typename Fn::result_type
00040     call(const Fn & fn, const JS::JSArgs & args)
00041     {
00042         return fn(CallWithJsArgs<ArgsWithPosition>::getArgAtPosition(args)...);
00043     }
00044 };
00045 
00054 template<typename Return, typename... Args, int arity>
00055 struct callfromjs<boost::function<Return (Args...)>, arity> {
00056     
00057     typedef boost::function<Return (Args...)> Fn;
00058 
00059     static Return
00060     call(const Fn & fn, const JS::JSArgs & args)
00061     {
00062         return CallWithList<Fn, typename MakeInPositionList<0, Args...>::List>
00063             ::call(fn, args);
00064     }
00065 };
00066 
00068 template<typename... Args, int arity>
00069 struct callfromjs<boost::function<void (Args...)>, arity> {
00070     
00071     typedef boost::function<void (Args...)> Fn;
00072 
00073     static void
00074     call(const Fn & fn, const JS::JSArgs & args)
00075     {
00076         CallWithList<Fn, typename MakeInPositionList<0, Args...>::List>
00077             ::call(fn, args);
00078     }
00079 
00080 };
00081 
00082 
00083 /*****************************************************************************/
00084 /* CALL TO JS                                                                */
00085 /*****************************************************************************/
00086 
00087 struct calltojsbase {
00088     calltojsbase(v8::Handle<v8::Function> fn,
00089                  v8::Handle<v8::Object> This)
00090         : params(new Params(fn, This))
00091     {
00092     }
00093 
00094     struct Params {
00095         Params(v8::Handle<v8::Function> fn,
00096                v8::Handle<v8::Object> This)
00097             : fn(v8::Persistent<v8::Function>::New(fn)),
00098               This(v8::Persistent<v8::Object>::New(This))
00099         {
00100         }
00101 
00102         ~Params()
00103         {
00104             fn.Dispose();
00105             This.Dispose();
00106         }
00107         
00108         v8::Persistent<v8::Function> fn;
00109         v8::Persistent<v8::Object> This;
00110     };
00111 
00112     std::shared_ptr<Params> params;
00113 };
00114 
00115 template<typename... Args>
00116 struct ArgUnpacker {
00117 };
00118 
00119 // Implementation of that template with the List argument unpacked
00120 template<typename First, typename... Rest>
00121 struct ArgUnpacker<First, Rest...> {
00122     
00123     static void
00124     unpack(v8::Handle<v8::Value> * unpacked, First arg, Rest... rest)
00125     {
00126         *unpacked++ = JS::toJS(arg);
00127         ArgUnpacker<Rest...>::unpack(unpacked, rest...);
00128     }
00129 };
00130 
00131 template<>
00132 struct ArgUnpacker<> {
00133     static void
00134     unpack(v8::Handle<v8::Value> * unpacked)
00135     {
00136     }
00137 };
00138 
00139 
00140 template<typename Return, typename... Args, int arity>
00141 struct calltojs<Return (Args...), arity> : public calltojsbase {
00142     calltojs(v8::Handle<v8::Function> fn,
00143              v8::Handle<v8::Object> This)
00144         : calltojsbase(fn, This)
00145     {
00146     }
00147     
00148     Return operator () (Args... args) const
00149     {
00150         //if (!v8::Locker::IsLocked())
00151         //    throw ML::Exception("callback outside JS context");
00152 
00153         v8::HandleScope scope;
00154         JSValue result;
00155         {
00156             v8::TryCatch tc;
00157             v8::Handle<v8::Value> argv[arity];
00158             
00159             ArgUnpacker<Args...>::unpack(argv, args...);
00160 
00161             result = params->fn->Call(params->This, arity, argv);
00162             
00163             if (result.IsEmpty()) {
00164                 if(tc.HasCaught())
00165                 {
00166                     // Print JS error and stack trace
00167                     char msg[256];
00168                     tc.Message()->Get()->WriteAscii(msg, 0, 256);
00169                     std::cout << msg << std::endl;
00170                     char st_msg[2500];
00171                     tc.StackTrace()->ToString()->WriteAscii(st_msg, 0, 2500);
00172                     std::cout << st_msg << std::endl;
00173 
00174                     tc.ReThrow();
00175                     throw JSPassException();
00176                 }
00177                 throw ML::Exception("didn't return anything");
00178             }
00179         }
00180 
00181         return from_js(result, (Return *)0);
00182     }
00183 };
00184 
00185 
00186 /*****************************************************************************/
00187 /* JSOPS                                                                     */
00188 /*****************************************************************************/
00189 
00190 
00191 template<typename Base, typename Fn>
00192 struct JsOpsBase {
00193     typedef boost::function<Fn> Function;
00194 
00195     static void op(int op,
00196                    const void * arg1,
00197                    const void * arg2,
00198                    void * result)
00199     {
00200         if (op == 0) {
00201             *(v8::Handle<v8::Value> *)result
00202                 = Base::callBoost(*(const Function *)arg1,
00203                                   *(const JS::JSArgs *)arg2);
00204             return;
00205         }
00206         else if (op == 1) {
00207             *(Function *)result
00208                 = Base::asBoost(*(const v8::Handle<v8::Function> *)arg1,
00209                                 (const v8::Handle<v8::Object> *)arg2);
00210         }
00211         else throw ML::Exception("unknown op");
00212     }
00213 };
00214 
00215 template<typename Fn,
00216          typename Result = typename boost::function<Fn>::result_type>
00217 struct DefaultJsOps : public JsOpsBase<DefaultJsOps<Fn, Result>, Fn> {
00218     typedef typename JsOpsBase<DefaultJsOps<Fn, void>, Fn>::Function Function;
00219 
00220     static v8::Handle<v8::Value>
00221     callBoost(const Function & fn,
00222               const JS::JSArgs & args)
00223     {
00224         Result result
00225             = JS::callfromjs<Function, Function::arity>::call(fn, args);
00226 
00227         JS::JSValue jsresult;
00228         JS::to_js(jsresult, result);
00229         
00230         return jsresult;
00231     }
00232     
00233     static Function
00234     asBoost(const v8::Handle<v8::Function> & fn,
00235             const v8::Handle<v8::Object> * This)
00236     {
00237         v8::Handle<v8::Object> This2;
00238         if (!This)
00239             This2 = v8::Object::New();
00240         return JS::calltojs<Fn, Function::arity>(fn, This ? *This : This2);
00241     }
00242 };
00243 
00244 template<typename Fn>
00245 struct DefaultJsOps<Fn, void> : public JsOpsBase<DefaultJsOps<Fn, void>, Fn> {
00246     typedef typename JsOpsBase<DefaultJsOps<Fn, void>, Fn>::Function Function;
00247 
00248     static v8::Handle<v8::Value>
00249     callBoost(const Function & fn,
00250               const JS::JSArgs & args)
00251     {
00252         JS::callfromjs<Function, Function::arity>::call(fn, args);
00253         return v8::Undefined();
00254     }
00255     
00256     static Function
00257     asBoost(const v8::Handle<v8::Function> & fn,
00258             const v8::Handle<v8::Object> * This)
00259     {
00260         v8::Handle<v8::Object> This2;
00261         if (!This)
00262             This2 = v8::Object::New();
00263         return JS::calltojs<Fn, Function::arity>(fn, This ? *This : This2);
00264     }
00265 };
00266 
00267 } // namespace JS
00268 
00269 template<typename Fn>
00270 struct RegisterJsOps {
00271     RegisterJsOps(JS::JSOperations ops = JS::DefaultJsOps<Fn>::op)
00272     {
00273         JS::registerJsOps(typeid(Fn), ops);
00274     }
00275 };
00276 
00277 } // namespace Datacratic
00278 
00279 #endif /* __js__js_call_h__ */
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator