RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/js/js_utils.cc
00001 /* js_utils.cc
00002    Jeremy Barnes, 21 July 2010
00003    Copyright (c) 2010 Datacratic.  All rights reserved.
00004 
00005    Implementation of Javascript utility functions.
00006 */
00007 
00008 #include "js_utils.h"
00009 #include "js_value.h"
00010 #include <cxxabi.h>
00011 #include "jml/arch/demangle.h"
00012 #include "jml/arch/exception_internals.h"
00013 #include "jml/arch/backtrace.h"
00014 #include "jml/utils/string_functions.h"
00015 
00016 using namespace std;
00017 using namespace v8;
00018 using namespace ML;
00019 
00020 namespace ML {
00021 
00022 extern __thread BacktraceInfo * current_backtrace;
00023 
00024 } // namespace ML
00025 
00026 namespace Datacratic {
00027 namespace JS {
00028 
00029 
00030 /*****************************************************************************/
00031 /* UTILITIES                                                                 */
00032 /*****************************************************************************/
00033 
00034 std::string cstr(const std::string & str)
00035 {
00036     return str;
00037 }
00038 
00039 std::string cstr(const JSValue & val)
00040 {
00041     return from_js(val, (string *)0);
00042 }
00043 
00044 v8::Handle<v8::Value>
00045 injectBacktrace(v8::Handle<v8::Value> value)
00046 {
00047     if (value.IsEmpty())
00048         throw ML::Exception("no object passed for backtrace injection");
00049 
00050     v8::Handle<v8::Object> obj(v8::Object::Cast(*value));
00051 
00052     if (obj.IsEmpty())
00053         throw ML::Exception("can't inject backtrace");
00054 
00055     v8::Handle<v8::Value> js_trace = obj->Get(v8::String::NewSymbol("stack"));
00056 
00057     vector<string> js_trace_elements = split(cstr(js_trace), '\n');
00058 
00059     // Frames to skip:
00060     // at [C++] ML::backtrace(int)
00061     // at [C++] Datacratic::JS::injectBacktrace(v8::Handle<v8::Value>)
00062     // at [C++] Datacratic::JS::mapException(ML::Exception const&)
00063     // at [C++] Datacratic::JS::translateCurrentException()
00064     int num_frames_to_skip = 0;
00065 
00066     vector<ML::BacktraceFrame> backtrace;
00067     if (current_backtrace && abi::__cxa_current_exception_type()) {
00068         // Skip:
00069         backtrace = ML::backtrace(*current_backtrace, num_frames_to_skip);
00070         delete current_backtrace;
00071         current_backtrace = 0;
00072     }
00073     else backtrace = ML::backtrace(num_frames_to_skip);
00074 
00075     string cpp_trace_str = js_trace_elements.at(0) + "\n";
00076 
00077     v8::Handle<v8::Array> cpp_trace(v8::Array::New(backtrace.size() + 1));
00078     for (unsigned i = 0;  i < backtrace.size();  ++i) {
00079         cpp_trace_str += "    at [C++] " + backtrace[i].print_for_trace() + "\n";
00080         cpp_trace->Set(v8::Uint32::New(i),
00081                        v8::String::New(backtrace[i].print().c_str()));
00082     }
00083     
00084     for (unsigned i = 1;  i < js_trace_elements.size();  ++i)
00085         cpp_trace_str += js_trace_elements[i] + "\n";
00086 
00087     cpp_trace->Set(v8::Uint32::New(backtrace.size()), js_trace);
00088 
00089     obj->Set(v8::String::NewSymbol("cpp_trace"), cpp_trace);
00090     obj->Set(v8::String::NewSymbol("js_trace"), js_trace);
00091     obj->Set(v8::String::NewSymbol("stack"), v8::String::New(cpp_trace_str.c_str()));
00092 
00093     return obj;
00094 }
00095 
00096 v8::Handle<Value>
00097 mapException(const std::exception & exc)
00098 {
00099     return v8::ThrowException
00100         (injectBacktrace
00101          (v8::Exception::Error(v8::String::New((type_name(exc)
00102                                                 + ": " + exc.what()).c_str()))));
00103 }
00104 
00105 v8::Handle<Value>
00106 mapException(const ML::Exception & exc)
00107 {
00108     //cerr << "mapping ML::Exception " << exc.what() << endl;
00109 
00110     return v8::ThrowException
00111         (injectBacktrace
00112          (v8::Exception::Error(v8::String::New(exc.what()))));
00113 }
00114 
00115 v8::Handle<v8::Value>
00116 translateCurrentException()
00117 {
00118     using namespace __cxxabiv1;
00119     using ML::Exception;
00120 
00121     __cxa_eh_globals * exc_globals = __cxa_get_globals();
00122     if (!exc_globals)
00123         throw Exception("no globals");
00124 
00125     if (!exc_globals->caughtExceptions)
00126         throw Exception("no exception");
00127         
00128     const __cxa_exception & exc = *exc_globals->caughtExceptions;
00129 
00130     void * thr_obj = exc.adjustedPtr;
00131     std::type_info * exc_ti = exc.exceptionType;
00132 
00133     if (!exc_ti)
00134         throw ML::Exception("translateCurrentException(): "
00135                             "no current exception");
00136 
00137     //cerr << "got exception of type" << exc_ti->name() << " at "
00138     //     << thr_obj << endl;
00139     
00140     if (typeid(JSPassException).__do_catch(exc_ti, &thr_obj, 0)) {
00141         //cerr << "passing through" << endl;
00142         return v8::Handle<v8::Value>();
00143     }
00144 
00145     if (typeid(ML::Exception).__do_catch(exc_ti, &thr_obj, 0)) {
00146         //cerr << "it's a ML::exception at " << thr_obj << endl;
00147         const ML::Exception & cast
00148             = *reinterpret_cast<const ML::Exception *>(thr_obj);
00149         return mapException(cast);
00150     }
00151 
00152     if (typeid(std::exception).__do_catch(exc_ti, &thr_obj, 0)) {
00153         //cerr << "it's a std::exception at " << thr_obj << endl;
00154         const std::exception & cast
00155             = *reinterpret_cast<const std::exception *>(thr_obj);
00156         return mapException(cast);
00157     }
00158 
00159     return v8::ThrowException
00160         (injectBacktrace
00161          (v8::Exception::Error
00162           (v8::String::New(("Exception of type "
00163                             + demangle(exc_ti->name())).c_str()))));
00164 }
00165 
00166 void passJsException(const v8::TryCatch & tc);
00167 
00168 struct NullHandle NULL_HANDLE;
00169 
00170 ValuePromise getArg(const JSArgs & args, int argnum,
00171                     const std::string & name)
00172 {
00173     if (args.Length() <= argnum)
00174         throw ML::Exception("argument %d (%s) must be present",
00175                             argnum, name.c_str());
00176 
00177     ValuePromise arg;
00178     arg.value  = args[argnum];
00179 
00180     if (arg.value->IsUndefined() || arg.value->IsNull())
00181         throw ML::Exception("argument %d (%s) was %s",
00182                             argnum, name.c_str(), cstr(arg.value).c_str());
00183 
00184     arg.argnum = argnum;
00185     arg.name   = name;
00186 
00187     return arg;
00188 }
00189 
00190 
00191 string getArg(const JSArgs & args, int argnum, const string & defvalue,
00192          const std::string & name)
00193 {
00194 
00195     return getArg<string>(args, argnum, defvalue, name);
00196 }
00197 
00199 v8::Persistent<v8::Function>
00200 from_js(const JSValue & val, v8::Persistent<v8::Function> *)
00201 {
00202     v8::Handle<v8::Function> fn(v8::Function::Cast(*val));
00203     if (fn.IsEmpty() || !fn->IsFunction()) {
00204         //cerr << "fn = " << cstr(fn) << endl;
00205         //cerr << "fn.IsEmpty() = " << fn.IsEmpty() << endl;
00206         //cerr << "val->IsFunction() = " << val->IsFunction() << endl;
00207         throw ML::Exception("expected a function; instead we got " + cstr(val));
00208     }
00209     
00210     return v8::Persistent<v8::Function>::New(fn);
00211 }
00212 
00213 v8::Local<v8::Function>
00214 from_js(const JSValue & val, v8::Local<v8::Function> *)
00215 {
00216     v8::Local<v8::Function> fn(v8::Function::Cast(*val));
00217     if (fn.IsEmpty() || !fn->IsFunction()) {
00218         //cerr << "fn = " << cstr(fn) << endl;
00219         //cerr << "fn.IsEmpty() = " << fn.IsEmpty() << endl;
00220         //cerr << "val->IsFunction() = " << val->IsFunction() << endl;
00221         throw ML::Exception("expected a function; instead we got " + cstr(val));
00222     }
00223 
00224     return fn;
00225 }
00226 
00227 v8::Handle<v8::Function>
00228 from_js(const JSValue & val, v8::Handle<v8::Function> *)
00229 {
00230     v8::Handle<v8::Function> fn(v8::Function::Cast(*val));
00231     if (fn.IsEmpty() || !fn->IsFunction()) {
00232         //cerr << "fn = " << cstr(fn) << endl;
00233         //cerr << "fn.IsEmpty() = " << fn.IsEmpty() << endl;
00234         //cerr << "val->IsFunction() = " << val->IsFunction() << endl;
00235         throw ML::Exception("expected a function; instead we got " + cstr(val));
00236     }
00237 
00238     return fn;
00239 }
00240 
00241 v8::Handle<v8::Array>
00242 from_js(const JSValue & val, v8::Handle<v8::Array> *)
00243 {
00244     v8::Handle<v8::Array> arr(v8::Array::Cast(*val));
00245     if (arr.IsEmpty() || !arr->IsArray())
00246         throw ML::Exception("expected an array; instead we got " + cstr(val));
00247 
00248     return arr;
00249 }
00250 
00251 v8::Handle<v8::Function>
00252 getFunction(const std::string & script_source)
00253 {
00254     using namespace v8;
00255 
00256     HandleScope scope;
00257     Handle<String> source = String::New(script_source.c_str());
00258 
00259     TryCatch tc;
00260     
00261     // Compile the source code.
00262     Handle<Script> script = Script::Compile(source);
00263 
00264     if (script.IsEmpty() && tc.HasCaught())
00265         throw ML::Exception("got exception compiling: "
00266                             + JS::cstr(tc.Exception()));
00267     if (script.IsEmpty())
00268         throw ML::Exception("compilation returned nothing");
00269     
00270     // Run the script to get the result (which should be a function)
00271     Handle<Value> result = script->Run();
00272 
00273     if (result.IsEmpty() && tc.HasCaught())
00274         throw ML::Exception("got exception compiling: "
00275                             + JS::cstr(tc.Exception()));
00276     if (result.IsEmpty())
00277         throw ML::Exception("compilation returned nothing");
00278     if (!result->IsFunction())
00279         throw ML::Exception("result of script isn't a function");
00280     
00281     v8::Local<v8::Function> fnresult(v8::Function::Cast(*result));
00282 
00283     return scope.Close(fnresult);
00284 }
00285 
00286 v8::Handle<v8::Array>
00287 getIndexArray(size_t sz)
00288 {
00289     v8::Handle<v8::Array> result(v8::Array::New(sz));
00290     
00291     for (unsigned i = 0;  i < sz;  ++i) {
00292         result->Set(v8::Uint32::New(i),
00293                     v8::Uint32::New(i));
00294     }
00295 
00296     return result;
00297 }
00298 
00300 v8::Handle<v8::Value>
00301 callGetterFn(v8::Local<v8::String> property,
00302              const v8::AccessorInfo & info)
00303 {
00304     try {
00305         HandleScope scope;
00306         if (!info.Data()->IsFunction())
00307             throw ML::Exception("isn't a function");
00308         v8::Local<v8::Function> fn(v8::Function::Cast(*info.Data()));
00309         if (fn.IsEmpty())
00310             throw JSPassException();
00311         const int argc = 1;
00312         v8::Local<v8::Value> argv[argc] = { property };
00313         return scope.Close(fn->Call(info.This(), argc, argv));
00314     } HANDLE_JS_EXCEPTIONS;
00315 }
00316 
00317 void printObj(const v8::Handle<v8::Value> & val,
00318               std::ostream & stream,
00319               int nesting)
00320 {
00321     string s(nesting * 4, ' ');
00322     stream << s << cstr(val) << endl;
00323     if (val->IsObject()) {
00324         auto objPtr = v8::Object::Cast(*val);
00325         if (!objPtr)
00326             return;
00327 
00328         v8::Local<v8::Array> properties = objPtr->GetPropertyNames();
00329 
00330         for(int i=0; i<properties->Length(); ++i) {
00331             v8::Local<v8::Value> key = properties->Get(i);
00332             v8::Local<v8::Value> val = objPtr->Get(key);
00333 
00334             stream << s << "  " << cstr(key) << ": " << cstr(val) << endl;
00335         }
00336 
00337         v8::Local<v8::Value> proto = objPtr->Get(v8::String::New("prototype"));
00338         stream << s << "  prototype " << cstr(proto) << endl;
00339         if (proto->IsObject())
00340             printObj(proto, stream, nesting + 1);
00341 
00342         v8::Local<v8::Value> proto2 = objPtr->GetPrototype(); 
00343         stream << s << "  .__proto__ " << cstr(proto2) << endl;
00344         if (proto2->IsObject())
00345             printObj(proto2, stream, nesting + 1);
00346     }
00347 }
00348 
00349 } // namespace JS
00350 } // namespace Datacratic
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator