RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/js/js_utils.h
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
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator