![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* js_value.cc 00002 Jeremy Barnes, 21 July 2010 00003 Copyright (c) 2010 Datacratic. All rights reserved. 00004 00005 Javascript value handling. 00006 */ 00007 00008 #include "js_value.h" 00009 #include "js_utils.h" 00010 #include "jml/utils/string_functions.h" 00011 #include "jml/arch/demangle.h" 00012 #include "jml/arch/backtrace.h" 00013 #include "soa/types/date.h" 00014 #include "soa/types/string.h" 00015 #include <boost/lexical_cast.hpp> 00016 #include <boost/foreach.hpp> 00017 #include "soa/jsoncpp/json.h" 00018 #include "node/node_buffer.h" 00019 #include <cxxabi.h> 00020 using namespace std; 00021 using namespace ML; 00022 using namespace Datacratic::JS; 00023 00024 namespace node { 00025 00026 // Define as a weak symbol to avoid linker errors when linking without node 00027 __attribute__((__weak__)) 00028 Buffer * Buffer::New(size_t) 00029 { 00030 throw Exception("node needs to be linked in to use non-ASCII strings"); 00031 } 00032 00033 __attribute__((__weak__)) 00034 bool Buffer::HasInstance(v8::Handle<v8::Value> val) 00035 { 00036 return false; // if node isn't linked in, then it can't be a buffer 00037 } 00038 00039 } // namespace node 00040 00041 namespace Datacratic { 00042 namespace JS { 00043 00044 00045 /*****************************************************************************/ 00046 /* JSVALUE */ 00047 /*****************************************************************************/ 00048 00049 JSValue::operator v8::Handle<v8::Object>() const 00050 { 00051 return toObject(*this); 00052 } 00053 00054 00055 /*****************************************************************************/ 00056 /* JSOBJECT */ 00057 /*****************************************************************************/ 00058 00059 void 00060 JSObject:: 00061 initialize() 00062 { 00063 *this = v8::Object::New(); 00064 } 00065 00066 void 00067 JSObject:: 00068 add(const std::string & key, const std::string & value) 00069 { 00070 (*this)->Set(v8::String::NewSymbol(key.c_str()), 00071 v8::String::New(value.c_str(), value.length())); 00072 } 00073 00074 void 00075 JSObject:: 00076 add(const std::string & key, const JSValue & value) 00077 { 00078 (*this)->Set(v8::String::NewSymbol(key.c_str()), 00079 value); 00080 } 00081 00082 00083 /*****************************************************************************/ 00084 /* CONVERSIONS */ 00085 /*****************************************************************************/ 00086 00087 void to_js(JSValue & jsval, signed int value) 00088 { 00089 jsval = v8::Integer::New(value); 00090 } 00091 00092 void to_js(JSValue & jsval, unsigned int value) 00093 { 00094 jsval = v8::Integer::NewFromUnsigned(value); 00095 } 00096 00097 void to_js(JSValue & jsval, signed long value) 00098 { 00099 if (value <= INT_MAX && value >= INT_MIN) 00100 jsval = v8::Integer::New(value); 00101 else jsval = v8::Number::New((double) value); 00102 } 00103 00104 void to_js(JSValue & jsval, unsigned long value) 00105 { 00106 if (value <= UINT_MAX) 00107 jsval = v8::Integer::NewFromUnsigned(value); 00108 else jsval = v8::Number::New((double) value); 00109 } 00110 00111 void to_js(JSValue & jsval, signed long long value) 00112 { 00113 if (value <= INT_MAX && value >= INT_MIN) 00114 jsval = v8::Integer::New(value); 00115 else jsval = v8::Number::New((double) value); 00116 } 00117 00118 void to_js(JSValue & jsval, unsigned long long value) 00119 { 00120 if (value <= UINT_MAX) 00121 jsval = v8::Integer::NewFromUnsigned(value); 00122 else jsval = v8::Number::New((double) value); 00123 } 00124 00125 void to_js(JSValue & jsval, float value) 00126 { 00127 jsval = v8::Number::New(value); 00128 } 00129 00130 void to_js(JSValue & jsval, double value) 00131 { 00132 jsval = v8::Number::New(value); 00133 } 00134 00135 void to_js_bool(JSValue & jsval, bool value) 00136 { 00137 jsval = v8::Boolean::New(value); 00138 } 00139 00140 void to_js(JSValue & jsval, const std::string & value) 00141 { 00142 bool isAscii = true; 00143 for (unsigned i = 0; i < value.size() && isAscii; ++i) 00144 if (value[i] == 0 || value[i] > 127) 00145 isAscii = false; 00146 if (isAscii) 00147 jsval = v8::String::New(value.c_str(), value.length()); 00148 else { 00149 // We can't represent this in ASCII. In this case, we need to use a 00150 // buffer. 00151 node::Buffer * buffer 00152 = node::Buffer::New(value.size()); 00153 std::copy(value.begin(), value.end(), node::Buffer::Data(buffer)); 00154 jsval = buffer->handle_; 00155 } 00156 } 00157 00158 void to_js(JSValue & jsval, const Utf8String & value) 00159 { 00160 jsval = v8::String::New(value.rawData(), value.rawLength()); 00161 } 00162 00163 void to_js(JSValue & jsval, const char * value) 00164 { 00165 jsval = v8::String::New(value); 00166 } 00167 00168 void to_js(JSValue & jsval, const Json::Value & value) 00169 { 00170 switch(value.type()) 00171 { 00172 case Json::objectValue: 00173 { 00174 v8::HandleScope scope; 00175 v8::Local<v8::Object> obj = v8::Object::New(); 00176 BOOST_FOREACH(string key, value.getMemberNames()) 00177 { 00178 JSValue member; 00179 to_js(member, value[key]); 00180 obj->Set(v8::String::NewSymbol(key.c_str()), member); 00181 } 00182 jsval = scope.Close(obj); 00183 } 00184 break; 00185 case Json::arrayValue: 00186 { 00187 v8::HandleScope scope; 00188 v8::Local<v8::Array> arr = v8::Array::New(); 00189 for(int i=0;i< value.size(); ++i) 00190 { 00191 JSValue elem; 00192 to_js(elem, value[i]); 00193 arr->Set(i, elem); 00194 } 00195 jsval = scope.Close(arr); 00196 } 00197 break; 00198 case Json::realValue: 00199 to_js(jsval, value.asDouble()); 00200 break; 00201 case Json::stringValue: 00202 to_js(jsval, value.asString()); 00203 break; 00204 case Json::intValue: 00205 to_js(jsval, value.asInt()); 00206 break; 00207 case Json::uintValue: 00208 to_js(jsval, value.asUInt()); 00209 break; 00210 case Json::booleanValue: 00211 to_js(jsval, value.asBool()); 00212 break; 00213 case Json::nullValue: 00214 jsval = v8::Null(); 00215 break; 00216 default: 00217 throw ML::Exception("Can't convert from JsonCpp to JSValue"); 00218 break; 00219 } 00220 } 00221 00222 void to_js(JSValue & jsval, Date value) 00223 { 00224 jsval = v8::Date::New(value.secondsSinceEpoch() * 1000.0); 00225 } 00226 00227 namespace { 00228 00229 int64_t check_to_int2(const JSValue & val) 00230 { 00231 //cerr << "check_to_int " << cstr(val) << endl; 00232 00233 int64_t ival = val->IntegerValue(); 00234 double dval = val->NumberValue(); 00235 00236 //cerr << " ival = " << ival << endl; 00237 //cerr << " dval = " << dval << endl; 00238 00239 if (ival != 0 && ival == dval) return ival; 00240 00241 if (dval > std::numeric_limits<uint64_t>::max() 00242 || dval < std::numeric_limits<uint64_t>::min()) 00243 throw ML::Exception("Cannot fit " + cstr(val) + " into an integer"); 00244 00245 v8::Local<v8::Number> num; 00246 00247 if (val->IsArray()) 00248 throw Exception("cannot convert array to integer"); 00249 00250 //bool debug = val->IsArray(); 00251 //if (debug) cerr << "is array" << endl; 00252 00253 if (val->IsNumber() || v8::Number::Cast(*val)) { 00254 //if (debug) 00255 //cerr << "is number" << endl; 00256 double d = val->NumberValue(); 00257 if (!isfinite(d)) 00258 throw Exception("cannot convert double value " 00259 + cstr(val) + " to integer"); 00260 return d; 00261 } 00262 if (val->IsString()) { 00263 //if (debug) 00264 //cerr << "is string" << endl; 00265 00266 int64_t ival = val->IntegerValue(); 00267 if (ival != 0) return ival; 00268 string s = lowercase(cstr(val)); 00269 try { 00270 return boost::lexical_cast<int64_t>(s); 00271 } catch (const boost::bad_lexical_cast & error) { 00272 throw Exception("cannot convert string value \"" 00273 + cstr(val) + "\" (\"" + s + "\") to integer"); 00274 } 00275 } 00276 00277 #define try_type(x) if (val->x()) cerr << #x << endl; 00278 00279 try_type(IsUndefined); 00280 try_type(IsNull); 00281 try_type(IsTrue); 00282 try_type(IsFalse); 00283 try_type(IsString); 00284 try_type(IsFunction); 00285 try_type(IsArray); 00286 try_type(IsObject); 00287 try_type(IsBoolean); 00288 try_type(IsNumber); 00289 try_type(IsExternal); 00290 try_type(IsInt32); 00291 try_type(IsDate); 00292 00293 if (val->IsObject()) { 00294 cerr << "object: " << cstr(val->ToObject()->ObjectProtoToString()) 00295 << endl; 00296 cerr << "val->NumberValue() = " << val->NumberValue() << endl; 00297 } 00298 00299 backtrace(); 00300 00301 throw Exception("cannot convert value \"" 00302 + cstr(val) + "\" to integer"); 00303 } 00304 00305 template<typename T> 00306 T check_to_int(const JSValue & val) 00307 { 00308 if (val.IsEmpty()) 00309 throw Exception("from_js: value is empty"); 00310 00311 00312 00313 int64_t result1 = check_to_int2(val); 00314 T result2 = result1; 00315 if (result1 != result2) 00316 throw Exception("value " + cstr(val) + " does not fit in type " 00317 + ML::type_name<T>()); 00318 return result2; 00319 } 00320 00321 } // file scope 00322 00323 signed int from_js(const JSValue & val, signed int *) 00324 { 00325 //cerr << "from_js signed int" << endl; 00326 return check_to_int<signed int>(val); 00327 } 00328 00329 unsigned int from_js(const JSValue & val, unsigned *) 00330 { 00331 //cerr << "from_js unsigned int" << endl; 00332 return check_to_int<unsigned int>(val); 00333 } 00334 00335 signed long from_js(const JSValue & val, signed long *) 00336 { 00337 //cerr << "from_js signed long" << endl; 00338 return check_to_int<signed long>(val); 00339 } 00340 00341 unsigned long from_js(const JSValue & val, unsigned long *) 00342 { 00343 //cerr << "from_js unsigned long" << endl; 00344 return check_to_int<unsigned long>(val); 00345 } 00346 00347 signed long long from_js(const JSValue & val, signed long long *) 00348 { 00349 //cerr << "from_js signed long" << endl; 00350 return check_to_int<signed long long>(val); 00351 } 00352 00353 unsigned long long from_js(const JSValue & val, unsigned long long *) 00354 { 00355 //cerr << "from_js unsigned long" << endl; 00356 return check_to_int<unsigned long long>(val); 00357 } 00358 00359 float from_js(const JSValue & val, float *) 00360 { 00361 //cerr << "from_js float" << endl; 00362 return from_js(val, (double *)0); 00363 } 00364 00365 double from_js(const JSValue & val, double *) 00366 { 00367 //cerr << "from_js double" << endl; 00368 const double result = val->NumberValue(); 00369 if (std::isnan(result)) { 00370 if (val->IsNumber()) return result; 00371 if (val->IsString()) { 00372 string s = lowercase(cstr(val)); 00373 if (s == "nan" || s == "-nan") 00374 return result; 00375 throw ML::Exception("string value \"%s\" is not converible to " 00376 "floating point", 00377 s.c_str()); 00378 } 00379 throw Exception("value \"%s\" not convertible to floating point", 00380 cstr(val).c_str()); 00381 } 00382 return result; 00383 } 00384 00385 bool from_js(const JSValue & val, bool *) 00386 { 00387 bool result = val->BooleanValue(); 00388 return result; 00389 } 00390 00391 std::string from_js(const JSValue & val, std::string *) 00392 { 00393 if (node::Buffer::HasInstance(val)) { 00394 //cerr << "from_js with buffer" << endl; 00395 return string(node::Buffer::Data(val), node::Buffer::Length(val)); 00396 } 00397 else 00398 { 00399 return *v8::String::AsciiValue(val); 00400 } 00401 } 00402 00403 Json::Value from_js(const JSValue & val, Json::Value *) 00404 { 00405 if (val.IsEmpty()) 00406 throw ML::Exception("empty val"); 00407 00408 //cerr << cstr(val) << endl; 00409 00410 if(val->IsObject()) 00411 { 00412 if(v8::Date::Cast(*val)->IsDate()) 00413 { 00414 return from_js(val, (Datacratic::Date*)(0)).secondsSinceEpoch(); 00415 } 00416 if(val->IsArray()) 00417 { 00418 Json::Value result (Json::arrayValue); 00419 00420 auto arrPtr = v8::Array::Cast(*val); 00421 for(int i=0; i<arrPtr->Length(); ++i) 00422 { 00423 result[i] = from_js(arrPtr->Get(i), (Json::Value *)0); 00424 } 00425 00426 return result; 00427 } 00428 else 00429 { 00430 Json::Value result (Json::objectValue); 00431 auto objPtr = v8::Object::Cast(*val); 00432 v8::Handle<v8::Array> prop_names = objPtr->GetPropertyNames(); 00433 00434 for (unsigned i = 0; i < prop_names->Length(); ++i) 00435 { 00436 v8::Handle<v8::String> key 00437 = prop_names->Get(v8::Uint32::New(i))->ToString(); 00438 if (!objPtr->HasOwnProperty(key)) continue; 00439 result[from_js(key, (string *)0)] = 00440 from_js(objPtr->Get(key), (Json::Value *)0); 00441 } 00442 00443 return result; 00444 } 00445 } 00446 if(val->IsBoolean()) 00447 { 00448 return from_js(val, (bool *)0); 00449 } 00450 if(val->IsString()) 00451 { 00452 return from_js(val, (Utf8String *)0); 00453 } 00454 if(val->IsInt32()) 00455 { 00456 return from_js(val, (int32_t *)0); 00457 } 00458 if(val->IsUint32()) 00459 { 00460 return from_js(val, (uint32_t *)0); 00461 } 00462 if(val->IsNumber()) 00463 { 00464 return from_js(val, (double *)0); 00465 } 00466 if (val->IsNull() || val->IsUndefined()) 00467 return Json::Value(); 00468 throw ML::Exception("can't convert from JSValue %s to Json::Value", 00469 cstr(val).c_str()); 00470 } 00471 00472 Date from_js(const JSValue & val, Date *) 00473 { 00474 if(!v8::Date::Cast(*val)->IsDate()) 00475 throw ML::Exception("Couldn't convert from " + cstr(val) + " to Datacratic::Date"); 00476 return Date::fromSecondsSinceEpoch(v8::Date::Cast(*val)->NumberValue() 00477 / 1000.0); 00478 } 00479 00480 Utf8String from_js(const JSValue & val, Utf8String *) 00481 { 00482 return Utf8String(*v8::String::Utf8Value(val)) ; 00483 } 00484 00485 Json::Value from_js_ref(const JSValue & val, Json::Value *) 00486 { 00487 return from_js(val, (Json::Value *)0); 00488 } 00489 00490 00491 } // namespace JS 00492 } // namespace Datacratic