![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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__ */