RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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