RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* js_utils.h -*- C++ -*- 00002 Jeremy Barnes, 21 July 2010 00003 Copyright (c) 2010 Datacratic. All rights reserved. 00004 00005 Utility functions for js. 00006 */ 00007 00008 #pragma once 00009 00010 #include <v8/v8.h> 00011 #include <string> 00012 #include "jml/arch/exception.h" 00013 #include "jml/utils/exc_assert.h" 00014 #include "jml/compiler/compiler.h" 00015 #include "jml/arch/demangle.h" 00016 #include "jml/arch/format.h" 00017 #include "jml/utils/positioned_types.h" 00018 #include <boost/shared_ptr.hpp> 00019 #include "js_value.h" 00020 #include <iostream> 00021 #include <set> 00022 #include <unordered_map> 00023 #include "js_registry.h" 00024 #include <boost/tuple/tuple.hpp> 00025 #include <boost/type_traits/remove_reference.hpp> 00026 #include <boost/type_traits/is_abstract.hpp> 00027 #include <boost/type_traits/is_pod.hpp> 00028 #include <boost/bind.hpp> 00029 #include <tuple> 00030 00031 namespace ML { 00032 00033 template<typename F, class Underlying> class distribution; 00034 00035 template<typename T, size_t I, typename Sz, bool Sf, typename P, class A> 00036 struct compact_vector; 00037 00038 } // namespace ML 00039 00040 namespace Datacratic { 00041 namespace JS { 00042 00043 00044 /*****************************************************************************/ 00045 /* UTILITIES */ 00046 /*****************************************************************************/ 00047 00048 std::string cstr(const std::string & str); 00049 00050 std::string cstr(const JSValue & val); 00051 00052 template<typename T> 00053 std::string cstr(const v8::Local<T> & str) 00054 { 00055 return cstr(JSValue(str)); 00056 } 00057 00058 template<typename T> 00059 std::string cstr(const v8::Handle<T> & str) 00060 { 00061 return cstr(JSValue(str)); 00062 } 00063 00064 struct JSPassException : public std::exception { 00065 // v8::Persistent<v8::Value> jsException; 00066 // v8::Persistent<v8::Value> jStackTrace; 00067 // v8::Persistent<v8::Message> jsMessage; 00068 }; 00069 00071 void passJsException(const v8::TryCatch & tc); 00072 00082 v8::Handle<v8::Value> translateCurrentException(); 00083 00085 v8::Handle<v8::Value> injectBacktrace(v8::Handle<v8::Value> value); 00086 00089 #define HANDLE_JS_EXCEPTIONS \ 00090 catch (...) { \ 00091 return translateCurrentException(); \ 00092 } 00093 00094 #define HANDLE_JS_EXCEPTIONS_SETTER \ 00095 catch (...) { \ 00096 translateCurrentException(); \ 00097 return; \ 00098 } 00099 00101 v8::Handle<v8::Value> 00102 mapException(const ML::Exception & exc); 00103 00105 v8::Handle<v8::Value> 00106 mapException(const std::exception & exc); 00107 00110 extern struct NullHandle { 00111 00112 template<typename T> 00113 operator v8::Handle<T>() 00114 { 00115 return v8::Handle<T>(); 00116 } 00117 00118 template<typename T> 00119 operator v8::Local<T>() 00120 { 00121 return v8::Local<T>(); 00122 } 00123 00124 } NULL_HANDLE; 00125 00129 inline v8::Handle<v8::Object> 00130 toObject(v8::Handle<v8::Value> handle) 00131 { 00132 ExcAssert(!handle.IsEmpty()); 00133 if (!handle->IsObject()) 00134 throw ML::Exception("value " + cstr(handle) + " is not an object"); 00135 v8::Handle<v8::Object> object = handle->ToObject(); 00136 if (object.IsEmpty()) 00137 throw ML::Exception("value " + cstr(handle) + " is not an object"); 00138 return object; 00139 } 00140 00144 inline v8::Handle<v8::Array> 00145 toArray(v8::Handle<v8::Value> handle) 00146 { 00147 ExcAssert(!handle.IsEmpty()); 00148 if (!handle->IsArray()) 00149 throw ML::Exception("value " + cstr(handle) + " is not an array"); 00150 v8::Handle<v8::Array> array(v8::Array::Cast(*handle)); 00151 if (array.IsEmpty()) 00152 throw ML::Exception("value " + cstr(handle) + " is not an array"); 00153 return array; 00154 } 00155 00156 template<typename T> 00157 v8::Handle<v8::Value> 00158 toJS(const T & t) 00159 { 00160 JSValue val; 00161 to_js(val, t); 00162 return val; 00163 } 00164 00165 template<typename T> 00166 void to_js(JSValue & val, const std::shared_ptr<T> & p) 00167 { 00168 if (!p) 00169 val = v8::Null(); 00170 else 00171 val = registry.getWrapper(p); 00172 } 00173 00174 template<typename T> 00175 void to_js(JSValue & val, const std::vector<T> & v) 00176 { 00177 v8::HandleScope scope; 00178 v8::Local<v8::Array> arr(v8::Array::New(v.size())); 00179 for (unsigned i = 0; i < v.size(); ++i) 00180 arr->Set(v8::Uint32::New(i), toJS(v[i])); 00181 val = scope.Close(arr); 00182 } 00183 00184 template<typename T> 00185 void to_js(JSValue & val, const std::set<T> & s) 00186 { 00187 v8::HandleScope scope; 00188 v8::Local<v8::Array> arr(v8::Array::New(s.size())); 00189 int count = 0; 00190 for(auto i = s.begin(); i != s.end(); ++i) 00191 { 00192 arr->Set(v8::Uint32::New(count), toJS(*i)); 00193 count++; 00194 } 00195 val = scope.Close(arr); 00196 } 00197 00198 template<typename T> 00199 void to_js(JSValue & val, const std::map<std::string, T> & s) 00200 { 00201 v8::HandleScope scope; 00202 v8::Local<v8::Object> obj= v8::Object::New(); 00203 for(auto i = s.begin(); i != s.end(); ++i) 00204 { 00205 obj->Set(v8::String::NewSymbol(i->first.c_str()), toJS(i->second)); 00206 } 00207 val = scope.Close(obj); 00208 } 00209 00210 template<typename T> 00211 std::map<std::string, T> 00212 from_js(const JSValue & val, const std::map<std::string, T> * = 0) 00213 { 00214 if(!val->IsObject()) { 00215 throw ML::Exception("invalid JSValue for map extraction"); 00216 } 00217 00218 std::map<std::string, T> result; 00219 00220 v8::HandleScope scope; 00221 00222 auto objPtr = v8::Object::Cast(*val); 00223 00224 v8::Local<v8::Array> properties = objPtr->GetOwnPropertyNames(); 00225 00226 for(int i=0; i<properties->Length(); ++i) 00227 { 00228 v8::Local<v8::Value> key = properties->Get(i); 00229 v8::Local<v8::Value> val = objPtr->Get(key); 00230 T val2 = from_js(JSValue(val), (T *)0); 00231 result[cstr(key)] = val2; 00232 } 00233 00234 return result; 00235 } 00236 00237 template<typename T> 00238 void to_js(JSValue & val, const std::unordered_map<std::string, T> & s) 00239 { 00240 v8::HandleScope scope; 00241 v8::Local<v8::Object> obj= v8::Object::New(); 00242 for(auto i = s.begin(); i != s.end(); ++i) 00243 { 00244 obj->Set(v8::String::NewSymbol(i->first.c_str()), toJS(i->second)); 00245 } 00246 val = scope.Close(obj); 00247 } 00248 00249 template<typename K, typename V, typename H> 00250 void to_js(JSValue & val, const std::unordered_map<K, V, H> & s) 00251 { 00252 v8::HandleScope scope; 00253 v8::Local<v8::Object> obj= v8::Object::New(); 00254 for(auto i = s.begin(); i != s.end(); ++i) 00255 { 00256 obj->Set(toJS(i->first), toJS(i->second)); 00257 } 00258 val = scope.Close(obj); 00259 } 00260 00261 template<typename K, typename V, typename H> 00262 std::map<K, V, H> 00263 from_js(const JSValue & val, const std::map<K, V, H> * = 0) 00264 { 00265 if(!val->IsObject()) { 00266 throw ML::Exception("invalid JSValue for map extraction"); 00267 } 00268 00269 std::unordered_map<K, V, H> result; 00270 00271 v8::HandleScope scope; 00272 00273 auto objPtr = v8::Object::Cast(*val); 00274 00275 v8::Local<v8::Array> properties = objPtr->GetOwnPropertyNames(); 00276 00277 for(int i=0; i<properties->Length(); ++i) 00278 { 00279 v8::Local<v8::Value> key = properties->Get(i); 00280 v8::Local<v8::Value> val = objPtr->Get(key); 00281 K key2 = from_js(JSValue(key), (K *)0); 00282 V val2 = from_js(JSValue(val), (V *)0); 00283 result[key2] = val2; 00284 } 00285 00286 return result; 00287 } 00288 00289 template<typename T, typename V> 00290 void to_js(JSValue & val, const boost::tuple<T, V> & v) 00291 { 00292 v8::HandleScope scope; 00293 v8::Local<v8::Array> arr(v8::Array::New(2)); 00294 arr->Set(v8::Uint32::New(0), toJS(v.template get<0>())); 00295 arr->Set(v8::Uint32::New(1), toJS(v.template get<1>())); 00296 val = scope.Close(arr); 00297 } 00298 00299 template<typename T, typename V> 00300 void to_js(JSValue & val, const std::pair<T, V> & v) 00301 { 00302 v8::HandleScope scope; 00303 v8::Local<v8::Array> arr(v8::Array::New(2)); 00304 arr->Set(v8::Uint32::New(0), toJS( v.first )); 00305 arr->Set(v8::Uint32::New(1), toJS( v.second )); 00306 val = scope.Close(arr); 00307 } 00308 00309 template<typename T, typename V> 00310 std::pair<T,V> 00311 from_js(const JSValue & val, const std::pair<T,V> * = 0) 00312 { 00313 if(!val->IsArray()) { 00314 throw ML::Exception("invalid JSValue for pair extraction"); 00315 } 00316 00317 auto arrPtr = v8::Array::Cast(*val); 00318 if(arrPtr->Length() != 2) { 00319 throw ML::Exception("invalid length for pair extraction"); 00320 } 00321 00322 return std::make_pair(from_js(JSValue(arrPtr->Get(0)),(T *) 0), 00323 from_js(JSValue(arrPtr->Get(1)),(V *) 0)); 00324 } 00325 00326 template<class Tuple, int Arg, int Size> 00327 struct TupleOpsJs { 00328 00329 static void unpack(v8::Local<v8::Array> & arr, 00330 const Tuple & tuple) 00331 { 00332 JSValue val; 00333 to_js(val, std::get<Arg>(tuple)); 00334 arr->Set(v8::Uint32::New(Arg), val); 00335 TupleOpsJs<Tuple, Arg + 1, Size>::unpack(arr, tuple); 00336 } 00337 00338 static void pack(v8::Array & array, 00339 Tuple & tuple) 00340 { 00341 if (Arg >= array.Length()) return; 00342 auto & el = std::get<Arg>(tuple); 00343 el = from_js(JSValue(array.Get(Arg)), &el); 00344 TupleOpsJs<Tuple, Arg + 1, Size>::pack(array, tuple); 00345 } 00346 }; 00347 00348 template<class Tuple, int Size> 00349 struct TupleOpsJs<Tuple, Size, Size> { 00350 00351 static void unpack(v8::Local<v8::Array> & array, 00352 const Tuple & tuple) 00353 { 00354 } 00355 00356 static void pack(v8::Array & array, 00357 Tuple & tuple) 00358 { 00359 } 00360 }; 00361 00362 template<typename... Args> 00363 void to_js(JSValue & val, const std::tuple<Args...> & v) 00364 { 00365 v8::HandleScope scope; 00366 v8::Local<v8::Array> arr(v8::Array::New(sizeof...(Args))); 00367 TupleOpsJs<std::tuple<Args...>, 0, sizeof...(Args)>::unpack(arr, v); 00368 val = scope.Close(arr); 00369 } 00370 00371 template<typename... Args> 00372 std::tuple<Args...> 00373 from_js(const JSValue & val, const std::tuple<Args...> * v = 0) 00374 { 00375 if (!val->IsArray()) 00376 throw ML::Exception("invalid JSValue for tuple extraction"); 00377 00378 auto arrPtr = v8::Array::Cast(*val); 00379 00380 std::tuple<Args...> result; 00381 TupleOpsJs<std::tuple<Args...>, 0, sizeof...(Args)>::pack(*arrPtr, result); 00382 return result; 00383 } 00384 00388 struct JSArgs { 00389 JSArgs(const v8::Arguments & args) 00390 : This(args.This()), args1(&args), args2(0), argc(args.Length()) 00391 { 00392 } 00393 00394 JSArgs(const v8::Handle<v8::Object> & This, 00395 int argc, const v8::Handle<v8::Value> * argv) 00396 : This(This), args1(0), args2(argv), argc(argc) 00397 { 00398 } 00399 00400 v8::Handle<v8::Value> operator [] (unsigned index) const 00401 { 00402 if (index >= argc) 00403 return v8::Undefined(); 00404 00405 if (args1) return (*args1)[index]; 00406 else return args2[index]; 00407 } 00408 00409 unsigned Length() const { return argc; } 00410 00411 v8::Handle<v8::Object> Holder() const 00412 { 00413 if (args1) return args1->Holder(); 00414 return This; 00415 } 00416 00417 v8::Handle<v8::Function> Callee() const 00418 { 00419 if (args1) return args1->Callee(); 00420 return v8::Handle<v8::Function>(); 00421 } 00422 00423 v8::Handle<v8::Object> This; 00424 const v8::Arguments * args1; 00425 const v8::Handle<v8::Value> * args2; 00426 unsigned argc; 00427 }; 00428 00429 00431 v8::Persistent<v8::Function> 00432 from_js(const JSValue & val, v8::Persistent<v8::Function> * = 0); 00433 00435 v8::Handle<v8::Function> 00436 from_js(const JSValue & val, v8::Handle<v8::Function> * = 0); 00437 00439 v8::Local<v8::Function> 00440 from_js(const JSValue & val, v8::Local<v8::Function> * = 0); 00441 00442 v8::Handle<v8::Array> 00443 from_js(const JSValue & val, v8::Handle<v8::Array> * = 0); 00444 00445 template<typename T> 00446 std::vector<T> 00447 from_js(const JSValue & val, const std::vector<T> * = 0) 00448 { 00449 if(!val->IsArray()) { 00450 throw ML::Exception("invalid JSValue for vector extraction"); 00451 } 00452 00453 std::vector<T> result; 00454 auto arrPtr = v8::Array::Cast(*val); 00455 for(int i=0; i<arrPtr->Length(); ++i) 00456 { 00457 result.push_back( from_js(JSValue(arrPtr->Get(i)), (T *)0) ); 00458 } 00459 return result; 00460 } 00461 00462 template<typename T> 00463 std::set<T> 00464 from_js(const JSValue & val, const std::set<T> * = 0) 00465 { 00466 if(!val->IsArray()) { 00467 throw ML::Exception("invalid JSValue for set extraction"); 00468 } 00469 00470 std::set<T> result; 00471 auto arrPtr = v8::Array::Cast(*val); 00472 for(int i=0; i<arrPtr->Length(); ++i) 00473 { 00474 result.insert( from_js(JSValue(arrPtr->Get(i)), (T *)0) ); 00475 } 00476 return result; 00477 } 00478 00479 template<typename T> 00480 void from_js(const JSValue & jsval, const T * value, 00481 typename boost::enable_if<typename boost::is_same<T, void>::type, void *>::type = 0) 00482 { 00483 } 00484 00485 template<typename T> 00486 void from_js(const JSValue & jsval, T * value, 00487 typename boost::enable_if<typename boost::is_same<T, void>::type, void *>::type = 0) 00488 { 00489 } 00490 00491 template<typename T, typename U> 00492 ML::distribution<T, U> 00493 from_js_ref(const JSValue & val, ML::distribution<T, U> * = 0) 00494 { 00495 return from_js(val, (const ML::distribution<T, U> *)0); 00496 } 00497 00498 template<typename V8Value> 00499 void to_js(JSValue & val, const v8::Handle<V8Value> & val2) 00500 { 00501 val = val2; 00502 } 00503 00504 template<typename V8Value> 00505 void to_js(JSValue & val, const v8::Local<V8Value> & val2) 00506 { 00507 val = val2; 00508 } 00509 00510 template<typename V8Value> 00511 void to_js(JSValue & val, const v8::Persistent<V8Value> & val2) 00512 { 00513 val = val2; 00514 } 00515 00516 template<typename T, size_t I, typename Sz, bool Sf, typename P, class A> 00517 void to_js(JSValue & val, const ML::compact_vector<T, I, Sz, Sf, P, A> & v) 00518 { 00519 v8::HandleScope scope; 00520 00521 v8::Local<v8::Array> arr(v8::Array::New(v.size())); 00522 for (unsigned i = 0; i < v.size(); ++i) 00523 arr->Set(v8::Uint32::New(i), toJS(v[i])); 00524 val = scope.Close(arr); 00525 } 00526 00527 //template<typename T> 00528 //void to_js(JSValue & val, T * const &) 00529 //{ 00530 // to_js(val, (T *)0); 00531 //} 00532 00533 //void from_js(JSValue & jsval, const void * = 0); 00534 //void from_js(const JSValue & jsval, void * = 0); 00535 00536 struct ValuePromise { 00537 ValuePromise() 00538 : argnum(-1) 00539 { 00540 } 00541 00542 ValuePromise(const JSValue & value) 00543 : value(value), argnum(-1) 00544 { 00545 } 00546 00547 ValuePromise(const JSValue & value, 00548 const std::string & name, 00549 int argnum) 00550 : value(value), name(name), argnum(argnum) 00551 { 00552 } 00553 00554 JSValue value; 00555 std::string name; 00556 int argnum; 00557 00558 template<typename T> 00559 operator T () const 00560 { 00561 try { 00562 return from_js(this->value, (T *)0); 00563 } catch (const std::exception & exc) { 00564 if (argnum == -1) 00565 throw ML::Exception("value \"%s\" could not be " 00566 "converted to a %s: %s", 00567 cstr(this->value).c_str(), 00568 ML::type_name<T>().c_str(), 00569 exc.what()); 00570 throw ML::Exception("argument %d (%s): value \"%s\" could not be " 00571 "converted to a %s: %s", 00572 this->argnum, this->name.c_str(), 00573 cstr(this->value).c_str(), 00574 ML::type_name<T>().c_str(), 00575 exc.what()); 00576 } 00577 } 00578 00579 template<typename T> 00580 decltype(from_js_ref(*(JSValue *)0, (T *)0)) getRef() const 00581 { 00582 try { 00583 return from_js_ref(this->value, (T *)0); 00584 } catch (const std::exception & exc) { 00585 if (argnum == -1) 00586 throw ML::Exception("value \"%s\" could not be " 00587 "converted to a %s: %s", 00588 cstr(this->value).c_str(), 00589 ML::type_name<T>().c_str(), 00590 exc.what()); 00591 throw ML::Exception("argument %d (%s): value \"%s\" could not be " 00592 "converted to a %s: %s", 00593 this->argnum, this->name.c_str(), 00594 cstr(this->value).c_str(), 00595 ML::type_name<T>().c_str(), 00596 exc.what()); 00597 } 00598 } 00599 }; 00600 00601 ValuePromise getArg(const JSArgs & args, int argnum, 00602 const std::string & name); 00603 00604 00605 std::string 00606 getArg(const JSArgs & args, int argnum, const std::string & defvalue, 00607 const std::string & name); 00608 00609 template<typename T, typename A> 00610 T getArg(const JSArgs & args, int argnum, 00611 const std::string & name, 00612 T (*fn) (A)) 00613 { 00614 ValuePromise vp = getArg(args, argnum, name); 00615 return fn(vp.value); 00616 } 00617 00618 template<typename T> 00619 T getArg(const JSArgs & args, int argnum, const T & defvalue, 00620 const std::string & name) 00621 { 00622 if (args.Length() <= argnum) 00623 return defvalue; 00624 00625 return getArg(args, argnum, name); 00626 } 00627 00628 template<typename T> 00629 typename boost::remove_reference<T>::type 00630 getArg(const JSArgs & args, int argnum, const std::string & name, 00631 typename boost::disable_if<typename boost::is_abstract<typename boost::remove_reference<T>::type>::type>::type * = 0) 00632 { 00633 return getArg(args, argnum, name) 00634 .operator typename boost::remove_reference<T>::type (); 00635 } 00636 00637 template<typename T> 00638 decltype(from_js_ref(*(JSValue *)0, (T *)0)) 00639 getArg(const JSArgs & args, int argnum, const std::string & name, 00640 typename boost::enable_if<typename boost::is_abstract<typename boost::remove_reference<T>::type>::type>::type * = 0) 00641 { 00642 return getArg(args, argnum, name).getRef<T>(); 00643 } 00644 00706 template<typename Base, typename Shared, typename T, T Shared::* Member, 00707 unsigned options = v8::ReadOnly | v8::DontDelete> 00708 struct ROPropertyHandler { 00709 00710 ROPropertyHandler(v8::Persistent<v8::FunctionTemplate> t, 00711 const char * name) 00712 { 00713 t->InstanceTemplate() 00714 ->SetAccessor(v8::String::NewSymbol(name), getter, 0, 00715 v8::Handle<v8::Value>(), v8::DEFAULT, 00716 v8::PropertyAttribute(options)); 00717 } 00718 00719 static v8::Handle<v8::Value> 00720 getter(v8::Local<v8::String> property, 00721 const v8::AccessorInfo & info) 00722 { 00723 try { 00724 return toJS((*Base::getShared(info.This())).*Member); 00725 } HANDLE_JS_EXCEPTIONS; 00726 } 00727 }; 00728 00729 template<typename Base, typename Shared, typename T, T (Shared::* Fn) () const, 00730 unsigned options = v8::ReadOnly | v8::DontDelete> 00731 struct GetterHandler { 00732 00733 GetterHandler(v8::Persistent<v8::FunctionTemplate> t, 00734 const char * name) 00735 { 00736 t->InstanceTemplate() 00737 ->SetAccessor(v8::String::NewSymbol(name), getter, 0, 00738 v8::Handle<v8::Value>(), v8::DEFAULT, 00739 v8::PropertyAttribute(options)); 00740 } 00741 00742 static v8::Handle<v8::Value> 00743 getter(v8::Local<v8::String> property, 00744 const v8::AccessorInfo & info) 00745 { 00746 try { 00747 return toJS(((*Base::getShared(info.This())).*Fn) ()); 00748 } HANDLE_JS_EXCEPTIONS; 00749 } 00750 }; 00751 00752 template<typename Base, typename Shared, typename T, T Shared::* Member, 00753 unsigned options = v8::DontDelete> 00754 struct RWPropertyHandler { 00755 00756 RWPropertyHandler(v8::Persistent<v8::FunctionTemplate> t, 00757 const char * name) 00758 { 00759 t->InstanceTemplate() 00760 ->SetAccessor(v8::String::NewSymbol(name), getter, setter, 00761 v8::Handle<v8::Value>(), v8::DEFAULT, 00762 v8::PropertyAttribute(options)); 00763 } 00764 00765 static v8::Handle<v8::Value> 00766 getter(v8::Local<v8::String> property, 00767 const v8::AccessorInfo & info) 00768 { 00769 try { 00770 return toJS((*Base::getShared(info.This())).*Member); 00771 } HANDLE_JS_EXCEPTIONS; 00772 } 00773 00774 static void 00775 setter(v8::Local<v8::String> property, 00776 v8::Local<v8::Value> value, 00777 const v8::AccessorInfo & info) 00778 { 00779 try { 00780 *Base::getShared(info.This()).*Member = from_js(JSValue(value), (T *)0); 00781 } catch (...) { 00782 std::cerr << "error setting field" << std::endl; 00783 throw; 00784 } 00785 } 00786 00787 }; 00788 00789 00790 inline ValuePromise 00791 fromJS(const v8::Handle<v8::Value> & value) 00792 { 00793 return ValuePromise(value); 00794 } 00795 00796 #if 0 00797 template<typename T> 00798 std::shared_ptr<T> 00799 from_js(const v8::Handle<v8::Value> & value, 00800 const std::shared_ptr<T> *) 00801 { 00802 return registry.getObject(value); 00803 } 00804 #endif 00805 00819 v8::Handle<v8::Function> 00820 getFunction(const std::string & script_source); 00821 00837 v8::Handle<v8::Function> 00838 getFunction(const std::string & script_source, 00839 v8::Handle<v8::Object> global); 00840 00841 00842 00843 // Convert a member pointer to a value 00844 template<typename T, typename Obj> 00845 v8::Local<v8::Value> pmToValue(T (Obj::* ptr)) 00846 { 00847 BOOST_STATIC_ASSERT(sizeof(T (Obj::*)) == sizeof(void *)); 00848 00849 union { 00850 void * vptr; 00851 size_t sz; 00852 T (Obj::* ptr); 00853 } x; 00854 00855 x.ptr = ptr; 00856 00857 ExcAssert(x.sz <= sizeof(Obj)); 00858 00859 return v8::External::Wrap(x.vptr); 00860 } 00861 00862 // Convert a value back into a member pointer 00863 template<typename T, typename Obj> 00864 T Obj::* valueToPm(v8::Handle<v8::Value> val) 00865 { 00866 union { 00867 void * vptr; 00868 size_t sz; 00869 T (Obj::* ptr); 00870 } x; 00871 00872 x.vptr = v8::External::Unwrap(val); 00873 00874 ExcAssert(x.sz <= sizeof(Obj)); 00875 00876 return x.ptr; 00877 } 00878 00879 // Note: these will be leaked... 00880 // Holds a pointer to a member function AND a set of default argument values 00881 // to use when not enough are supplied. 00882 template<typename T, typename Obj, typename... Args> 00883 struct PmfInfo { 00884 template<typename... Defaults> 00885 PmfInfo(T (Obj::* pmf) (Args...), 00886 Defaults... defaults) 00887 : pmf(pmf), defaultArgs(sizeof...(Args)) 00888 { 00889 //using namespace std; 00890 //cerr << "adding " << sizeof...(Defaults) << " default args to " 00891 // << sizeof...(Args) << " existing" << endl; 00892 addDefaults(sizeof...(Args) - sizeof...(Defaults), 00893 sizeof...(Args), defaults...); 00894 } 00895 00896 template<typename Arg1, typename... Rest> 00897 void addDefaults(int argNum, size_t numArgs, Arg1 arg1, Rest... rest) 00898 { 00899 addDefault(arg1, argNum, numArgs); 00900 addDefaults(argNum + 1, numArgs, rest...); 00901 } 00902 00903 // End of recursion; we should have reached the end of them both 00904 void addDefaults(int argNum, size_t numArgs) 00905 { 00906 ExcAssertEqual(argNum, numArgs); 00907 } 00908 00909 template<typename X> 00910 void addDefault(X arg, int argNum, size_t numArgs) 00911 { 00912 defaultArgs[argNum] = v8::Persistent<v8::Value>::New(JS::toJS(arg)); 00913 } 00914 00915 T (Obj::* pmf) (Args...); 00916 std::vector<JSValue> defaultArgs; 00917 }; 00918 00919 // Convert a member pointer to a value 00920 template<typename T, typename Obj, typename... Args, typename... Defaults> 00921 v8::Local<v8::Value> pmfToValue(T (Obj::* pmf) (Args...), 00922 Defaults... defaults) 00923 { 00924 PmfInfo<T, Obj, Args...> * res 00925 = new PmfInfo<T, Obj, Args...>(pmf, defaults...); 00926 return v8::External::Wrap(res); 00927 } 00928 00929 template<typename T, typename Obj, typename... Args, typename... Defaults> 00930 v8::Local<v8::Value> pmfToValue(T (Obj::* pmf) (Args...) const, 00931 Defaults... defaults) 00932 { 00933 PmfInfo<T, const Obj, Args...> * res 00934 = new PmfInfo<T, const Obj, Args...>(pmf, defaults...); 00935 return v8::External::Wrap(res); 00936 } 00937 00938 template<typename T, typename Obj, typename... Args> 00939 struct PmfReturnValue { 00940 typedef T (Obj::* type) (Args...); 00941 }; 00942 00943 // Convert a value back into a member pointer 00944 template<typename T, typename Obj, typename... Args> 00945 //T (Obj::*) (Args...) 00946 typename PmfReturnValue<T, Obj, Args...>::type 00947 valueToPmf(v8::Handle<v8::Value> val) 00948 { 00949 return reinterpret_cast<PmfInfo<T, Obj, Args...> *>(v8::External::Unwrap(val)) 00950 ->pmf; 00951 } 00952 00953 template<typename T, typename Obj, typename... Args> 00954 const PmfInfo<T, Obj, Args...> * 00955 valueToPmfInfo(v8::Handle<v8::Value> val) 00956 { 00957 return reinterpret_cast<PmfInfo<T, Obj, Args...> *>(v8::External::Unwrap(val)); 00958 } 00959 00960 template<typename T, typename... Args> 00961 v8::Local<v8::Value> 00962 lambdaToValue(const boost::function<T (Args...)> & lambda) 00963 { 00964 // TODO: this memory will be leaked... 00965 boost::function<T (Args...)> * fn 00966 = new boost::function<T (Args...)>(lambda); 00967 return v8::External::Wrap(fn); 00968 } 00969 00970 template<typename T, typename... Args> 00971 const boost::function<T (Args...)> & 00972 valueToLambda(const v8::Handle<v8::Value> & val) 00973 { 00974 return *reinterpret_cast<boost::function<T (Args...)> *> 00975 (v8::External::Unwrap(val)); 00976 } 00977 00978 template<typename Obj, typename Base, typename RT> 00979 static v8::Handle<v8::Value> 00980 lambdaGetter(v8::Local<v8::String> property, 00981 const v8::AccessorInfo & info) 00982 { 00983 try { 00984 boost::function<RT (const Obj &)> fn 00985 = valueToLambda<RT, const Obj &>(info.Data()); 00986 Obj & o = *Base::getShared(info.This()); 00987 RT value = fn(o); 00988 return JS::toJS(value); 00989 } HANDLE_JS_EXCEPTIONS; 00990 } 00991 00992 template<typename T, typename Obj, typename Base> 00993 struct PropertyGetter { 00994 static v8::Handle<v8::Value> 00995 getter(v8::Local<v8::String> property, 00996 const v8::AccessorInfo & info) 00997 { 00998 try { 00999 T (Obj::* pm) = valueToPm<T, Obj>(info.Data()); 01000 Obj & o = *Base::getShared(info.This()); 01001 T value = o.*pm; 01002 return JS::toJS(value); 01003 } HANDLE_JS_EXCEPTIONS; 01004 } 01005 01006 static v8::Handle<v8::Value> 01007 pmfGetter(v8::Local<v8::String> property, 01008 const v8::AccessorInfo & info) 01009 { 01010 try { 01011 auto pmf = valueToPmf<T, Obj>(info.Data()); 01012 Obj & o = *Base::getShared(info.This()); 01013 T value = (o.*pmf) (); 01014 return JS::toJS(value); 01015 } HANDLE_JS_EXCEPTIONS; 01016 } 01017 01018 }; 01019 01020 01021 template<typename T, typename Obj, typename Base> 01022 struct PropertySetter { 01023 static void 01024 setter(v8::Local<v8::String> property, 01025 v8::Local<v8::Value> value, 01026 const v8::AccessorInfo & info) 01027 { 01028 try { 01029 T (Obj::* pm) = valueToPm<T, Obj>(info.Data()); 01030 Obj & o = *Base::getShared(info.This()); 01031 T & var = o.*pm; 01032 var = from_js(JSValue(value), (T *)0); 01033 } HANDLE_JS_EXCEPTIONS_SETTER; 01034 } 01035 01036 static void 01037 pmfSetter(v8::Local<v8::String> property, 01038 v8::Local<v8::Value> value, 01039 const v8::AccessorInfo & info) 01040 { 01041 try { 01042 void (Obj::* setter) (const T &) = valueToPmf<void, Obj, const T &> 01043 (info.Data()); 01044 Obj & o = *Base::getShared(info.This()); 01045 (o.*setter) (from_js(JSValue(value), (const T *)0)); 01046 } HANDLE_JS_EXCEPTIONS; 01047 } 01048 }; 01049 01050 // We make from_js_ref make a temporary copy of anything that's just a POD 01051 // field; anything else needs to a) have a from_js_ref specialization, or 01052 // b) be extractable as a pointer to avoid copying 01053 template<typename T> 01054 T 01055 from_js_ref(const JSValue & val, T *, 01056 typename boost::enable_if<typename boost::is_pod<T>::type>::type * = 0) 01057 { 01058 return from_js(val, (T*)0); 01059 } 01060 01061 // Allow std::string to be passed by value as well 01062 01063 inline std::string 01064 from_js_ref(const JSValue & val, std::string *) 01065 { 01066 return from_js(val, (std::string *)0); 01067 } 01068 01069 // And vectors 01070 01071 template<typename T> 01072 inline std::vector<T> 01073 from_js_ref(const JSValue & val, std::vector<T> *) 01074 { 01075 return from_js(val, (std::vector<T> *)0); 01076 } 01077 01078 // Anything else we require that we can extract a pointer to a real object 01079 // to avoid copying complex objects; this can be overridden by defining a 01080 // function like for std::string above 01081 template<typename T> 01082 const T & 01083 from_js_ref(const JSValue & val, T *, 01084 typename boost::disable_if<typename boost::is_pod<T>::type>::type * = 0) 01085 { 01086 return *from_js(val, (T**)0); 01087 } 01088 01089 using ML::TypeList; 01090 using ML::InPosition; 01091 using ML::MakeInPositionList; 01092 01093 // Template that, given an InPosition<Arg, Index> argument, will actually 01094 // extract the argument from a JS::JsArgs and pass it on 01095 template<typename Param> 01096 struct CallWithJsArgs { 01097 }; 01098 01099 // Implementation of that template with the InPosition argument unpacked 01100 template<typename Arg, int Index> 01101 struct CallWithJsArgs<InPosition<Arg, Index> > { 01102 01103 static Arg 01104 getArgAtPosition(const JS::JSArgs & args, 01105 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01106 { 01107 try { 01108 if (Index >= args.Length() && Index < defaults.size() 01109 && !defaults[Index].IsEmpty()) { 01110 //using namespace std; 01111 //cerr << "defaults.size() = " << defaults.size() << endl; 01112 //cerr << "using default value " << cstr(defaults.at(Index)) 01113 // << " for arg " << Index << endl; 01114 //cerr << "&defaults[0] = " << &defaults[0] << endl; 01115 return from_js(defaults[Index], (Arg *)0); 01116 } 01117 return from_js(JSValue(args[Index]), (Arg *)0); 01118 } catch (const std::exception & exc) { 01119 throw ML::Exception("calling %s.%s(): argument %d (%s): " 01120 "value \"%s\" could not be " 01121 "converted to a %s: %s", 01122 cstr(args.Holder()->GetConstructorName()).c_str(), 01123 cstr(args.Callee()->GetName()).c_str(), 01124 Index, "name", 01125 cstr(args[Index]).c_str(), 01126 ML::type_name<Arg>().c_str(), 01127 exc.what()); 01128 } 01129 } 01130 }; 01131 01132 template<typename Arg, int Index> 01133 struct CallWithJsArgs<InPosition<const Arg &, Index> > { 01134 01135 static decltype(from_js_ref(*(JSValue *)0, (Arg *)0)) 01136 getArgAtPosition(const JS::JSArgs & args, 01137 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01138 { 01139 //using namespace std; 01140 //cerr << "getting arg " << argNum << " for reference" << endl; 01141 //cerr << "value is " << cstr(args[argNum]) << endl; 01142 std::string message; 01143 try { 01144 if (Index >= args.Length() && Index < defaults.size() 01145 && !defaults[Index].IsEmpty()) 01146 return from_js_ref(defaults[Index], (Arg *)0); 01147 JSValue v(args[Index]); 01148 return from_js_ref(v, (Arg *)0); 01149 } catch (const std::exception & exc) { 01150 message 01151 = ML::format("calling %s.%s(): argument %d: " 01152 "value \"%s\" could not be " 01153 "converted to a %s: %s", 01154 cstr(args.Holder()->GetConstructorName()).c_str(), 01155 cstr(args.Callee()->GetName()).c_str(), 01156 Index, 01157 cstr(args[Index]).c_str(), 01158 ML::type_name<Arg>().c_str(), 01159 exc.what()); 01160 } 01161 //cerr << "got message: " << message << endl; 01162 throw ML::Exception(message); 01163 } 01164 }; 01165 01166 // Given a boost::function type Fn and a TypeList of InPosition values, 01167 // this calls the function with the JS arguments unpacked 01168 template<typename List> 01169 struct CallPmfWithTypePositionList { 01170 }; 01171 01172 // Implementation of that template with the List argument unpacked 01173 template<typename... ArgsWithPosition> 01174 struct CallPmfWithTypePositionList<TypeList<ArgsWithPosition...> > { 01175 01176 template<typename R, typename... Args, typename Obj> 01177 static R call(R (Obj::* pmf) (Args...), Obj & obj, 01178 const JS::JSArgs & args, 01179 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01180 { 01181 return (obj.*pmf)(CallWithJsArgs<ArgsWithPosition> 01182 ::getArgAtPosition(args, defaults)...); 01183 } 01184 01185 template<typename... Args, typename Obj> 01186 static void call(void (Obj::* pmf) (Args...), Obj & obj, 01187 const JS::JSArgs & args, 01188 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01189 { 01190 (obj.*pmf)(CallWithJsArgs<ArgsWithPosition> 01191 ::getArgAtPosition(args, defaults)...); 01192 } 01193 01194 template<typename R, typename... Args, typename Obj> 01195 static R call(R (Obj::* pmf) (Args...) const, const Obj & obj, 01196 const JS::JSArgs & args, 01197 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01198 { 01199 return (obj.*pmf)(CallWithJsArgs<ArgsWithPosition> 01200 ::getArgAtPosition(args, defaults)...); 01201 } 01202 01203 template<typename... Args, typename Obj> 01204 static void call(void (Obj::* pmf) (Args...) const, const Obj & obj, 01205 const JS::JSArgs & args, 01206 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01207 { 01208 (obj.*pmf)(CallWithJsArgs<ArgsWithPosition> 01209 ::getArgAtPosition(args, defaults)...); 01210 } 01211 }; 01212 01213 01214 template<typename Obj, typename... Args> 01215 v8::Handle<v8::Value> 01216 callPmf(void (Obj::*pmf) (Args...) const, const Obj & obj, 01217 const v8::Arguments & args, 01218 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01219 { 01220 typedef typename MakeInPositionList<0, Args...>::List TypePositionList; 01221 CallPmfWithTypePositionList<TypePositionList> 01222 ::call(pmf, obj, args, defaults); 01223 return args.This(); 01224 } 01225 01226 template<typename Obj, typename... Args> 01227 v8::Handle<v8::Value> 01228 callPmf(void (Obj::*pmf) (Args...), Obj & obj, 01229 const v8::Arguments & args, 01230 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01231 { 01232 typedef typename MakeInPositionList<0, Args...>::List TypePositionList; 01233 CallPmfWithTypePositionList<TypePositionList> 01234 ::call(pmf, obj, args, defaults); 01235 return args.This(); 01236 } 01237 01238 template<typename Obj, typename R, typename... Args> 01239 v8::Handle<v8::Value> 01240 callPmf(R (Obj::*pmf) (Args...) const, const Obj & obj, 01241 const v8::Arguments & args, 01242 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01243 { 01244 typedef typename MakeInPositionList<0, Args...>::List TypePositionList; 01245 return JS::toJS(CallPmfWithTypePositionList<TypePositionList> 01246 ::call(pmf, obj, args, defaults)); 01247 } 01248 01249 template<typename Obj, typename R, typename... Args> 01250 v8::Handle<v8::Value> 01251 callPmf(R (Obj::*pmf) (Args...), Obj & obj, 01252 const v8::Arguments & args, 01253 const std::vector<JSValue> & defaults = std::vector<JSValue>()) 01254 { 01255 typedef typename MakeInPositionList<0, Args...>::List TypePositionList; 01256 return JS::toJS(CallPmfWithTypePositionList<TypePositionList> 01257 ::call(pmf, obj, args, defaults)); 01258 } 01259 01260 template<typename R, typename Obj, typename Base, typename... Args> 01261 struct MemberFunctionCaller { 01262 01263 static v8::Handle<v8::Value> 01264 call(const v8::Arguments & args) 01265 { 01266 try { 01267 auto info = valueToPmfInfo<R, Obj, Args...>(args.Data()); 01268 Obj & o = *Base::getShared(args); 01269 v8::Handle<v8::Value> result = callPmf(info->pmf, o, args, 01270 info->defaultArgs); 01271 return result; 01272 } HANDLE_JS_EXCEPTIONS; 01273 } 01274 }; 01275 01276 01277 template<typename R, typename Obj, typename Base> 01278 struct LambdaCaller { 01279 01280 static v8::Handle<v8::Value> 01281 call(const v8::Arguments & args) 01282 { 01283 try { 01284 const boost::function<R (Obj &, const v8::Arguments &)> & fn 01285 = valueToLambda<R, Obj &, const v8::Arguments &>(args.Data()); 01286 Obj & o = *Base::getShared(args); 01287 return JS::toJS(fn(0, args)); 01288 } HANDLE_JS_EXCEPTIONS; 01289 } 01290 }; 01291 01292 template<typename Obj, typename Base> 01293 struct LambdaCaller<void, Obj, Base> { 01294 01295 static v8::Handle<v8::Value> 01296 call(const v8::Arguments & args) 01297 { 01298 try { 01299 const boost::function<void (Obj &, const v8::Arguments &)> & fn 01300 = valueToLambda<void, Obj &, 01301 const v8::Arguments &>(args.Data()); 01302 Obj & o = *Base::getShared(args); 01303 fn(0, args); 01304 return args.This(); 01305 } HANDLE_JS_EXCEPTIONS; 01306 } 01307 }; 01308 01310 v8::Handle<v8::Value> 01311 callGetterFn(v8::Local<v8::String> property, 01312 const v8::AccessorInfo & info); 01313 01314 01315 /*****************************************************************************/ 01316 /* CALL IN JS CONTEXT */ 01317 /*****************************************************************************/ 01318 01324 void callInJsThread(const boost::function<void ()> & fn); 01325 01330 template<typename Fn> 01331 void callInJsContext(const Fn & fn) 01332 { 01333 // If we happen to already be in the JS interpreter then we can simply call 01334 // the functio from this thread. 01335 if (v8::Locker::IsLocked()) { 01336 fn(); 01337 return; 01338 } 01339 01340 callInJsThread(fn); 01341 } 01342 01348 boost::function<void ()> 01349 createCrossThreadCallback(v8::Handle<v8::Function> fn, 01350 v8::Handle<v8::Object> This); 01351 01352 boost::function<void ()> 01353 createCrossThreadCallback(v8::Handle<v8::Function> fn, 01354 v8::Handle<v8::Object> This, 01355 v8::Handle<v8::Value> arg1); 01356 01357 boost::function<void ()> 01358 createCrossThreadCallback(v8::Handle<v8::Function> fn, 01359 v8::Handle<v8::Object> This, 01360 v8::Handle<v8::Value> arg1, 01361 v8::Handle<v8::Value> arg2); 01362 01363 boost::function<void ()> 01364 createCrossThreadCallback(v8::Handle<v8::Function> fn, 01365 v8::Handle<v8::Object> This, 01366 v8::Handle<v8::Value> arg1, 01367 v8::Handle<v8::Value> arg2, 01368 v8::Handle<v8::Value> arg3); 01369 01370 // Convert a callback to be called in JS context 01371 01372 template<typename R, typename... Args> 01373 boost::function<void (Args...)> 01374 createAsyncJsCallback(const boost::function<R (Args...)> & fn) 01375 { 01376 auto newFn = [=] (Args... args) 01377 { 01378 boost::function<void ()> cb 01379 = std::bind<void>(fn, args...); 01380 01381 callInJsContext(cb); 01382 }; 01383 01384 return newFn; 01385 } 01386 01387 template<typename T, typename Obj, typename Base> 01388 struct AsyncCallbackSetter { 01389 static void 01390 setter(v8::Local<v8::String> property, 01391 v8::Local<v8::Value> value, 01392 const v8::AccessorInfo & info) 01393 { 01394 try { 01395 T (Obj::* pm) = valueToPm<T, Obj>(info.Data()); 01396 Obj & o = *Base::getShared(info.This()); 01397 T & var = o.*pm; 01398 var = createAsyncJsCallback(from_js(JSValue(value), (T *)0)); 01399 } HANDLE_JS_EXCEPTIONS_SETTER; 01400 } 01401 }; 01402 01403 01404 // Returns an array with numbers from 0 to sz-1 that can be used as 01405 // the indexed array accessor to list entries. 01406 v8::Handle<v8::Array> 01407 getIndexArray(size_t sz); 01408 01409 // Print out a JS object including it's entire prototype chain 01410 void printObj(const v8::Handle<v8::Value> & val, 01411 std::ostream & stream, 01412 int nesting = 0); 01413 01414 template<typename Val> 01415 inline 01416 std::ostream & operator << (std::ostream & stream, 01417 const v8::Handle<Val> & val) 01418 { 01419 printObj(val, stream, 0); 01420 return stream; 01421 } 01422 01423 } // namespace JS 01424 } // namespace Datacratic