1 /* 2 * Copyright (c) 2012 Joyent, Inc. All rights reserved. 3 */ 4 5 #include <sys/types.h> 6 #include <string.h> 7 #include <new> 8 #include <unordered_map> 9 #include <stdlib.h> 10 #include <node.h> 11 #include "v8plus_impl.h" 12 #include "v8plus_glue.h" 13 14 #define METHOD_NAME_FMT "__v8plus_%s_%s" 15 16 v8::Persistent<v8::Function> v8plus::ObjectWrap::_constructor; 17 v8plus_method_descr_t *v8plus::ObjectWrap::_mtbl; 18 v8plus_static_descr_t *v8plus::ObjectWrap::_stbl; 19 std::unordered_map<void *, v8plus::ObjectWrap *> v8plus::ObjectWrap::_objhash; 20 21 uv_async_t v8plus::ObjectWrap::_uv_async; 22 pthread_mutex_t v8plus::ObjectWrap::_callq_mutex; 23 std::queue<v8plus_async_call_t *> v8plus::ObjectWrap::_callq; 24 boolean_t v8plus::ObjectWrap::_crossthread_init_done = _B_FALSE; 25 unsigned long v8plus::ObjectWrap::_uv_event_thread; 26 27 static char * 28 function_name(const char *lambda) 29 { 30 char *fn; 31 size_t len; 32 33 len = snprintf(NULL, 0, METHOD_NAME_FMT, 34 v8plus_js_class_name, lambda); 35 if ((fn = (char *)malloc(len + 1)) == NULL) 36 v8plus_panic("out of memory for function name for %s", lambda); 37 38 (void) snprintf(fn, len + 1, METHOD_NAME_FMT, 39 v8plus_js_class_name, lambda); 40 41 return (fn); 42 } 43 44 void 45 v8plus::ObjectWrap::init(v8::Handle<v8::Object> target) 46 { 47 uint_t i; 48 49 if (v8plus_static_method_count > 0) { 50 const v8plus_static_descr_t *sdp; 51 52 _stbl = new (std::nothrow) 53 v8plus_static_descr_t[v8plus_static_method_count]; 54 if (_stbl == NULL) 55 v8plus_panic("out of memory for static method table"); 56 57 for (i = 0; i < v8plus_static_method_count; i++) { 58 v8::Local<v8::FunctionTemplate> fth = 59 v8::FunctionTemplate::New(_static_entry); 60 v8::Local<v8::Function> fh = fth->GetFunction(); 61 sdp = &v8plus_static_methods[i]; 62 63 _stbl[i].sd_name = function_name(sdp->sd_name); 64 _stbl[i].sd_c_func = sdp->sd_c_func; 65 66 fh->SetName(v8::String::New(_stbl[i].sd_name)); 67 68 target->Set(v8::String::NewSymbol(sdp->sd_name), fh); 69 } 70 } 71 72 73 if (v8plus_method_count > 0) { 74 v8::Local<v8::FunctionTemplate> tpl = 75 v8::FunctionTemplate::New(_new); 76 const v8plus_method_descr_t *mdp; 77 78 _mtbl = new (std::nothrow) 79 v8plus_method_descr_t[v8plus_method_count]; 80 if (_mtbl == NULL) 81 v8plus_panic("out of memory for method table"); 82 83 tpl->SetClassName(v8::String::NewSymbol(v8plus_js_class_name)); 84 tpl->InstanceTemplate()->SetInternalFieldCount( 85 v8plus_method_count); 86 87 for (i = 0; i < v8plus_method_count; i++) { 88 v8::Local<v8::FunctionTemplate> fth = 89 v8::FunctionTemplate::New(_entry); 90 v8::Local<v8::Function> fh = fth->GetFunction(); 91 mdp = &v8plus_methods[i]; 92 93 _mtbl[i].md_name = function_name(mdp->md_name); 94 _mtbl[i].md_c_func = mdp->md_c_func; 95 96 fh->SetName(v8::String::New(_mtbl[i].md_name)); 97 98 tpl->PrototypeTemplate()->Set( 99 v8::String::NewSymbol(mdp->md_name), fh); 100 } 101 102 _constructor = 103 v8::Persistent<v8::Function>::New(tpl->GetFunction()); 104 105 target->Set(v8::String::NewSymbol(v8plus_js_factory_name), 106 v8::FunctionTemplate::New( 107 v8plus::ObjectWrap::cons)->GetFunction()); 108 } 109 } 110 111 v8::Handle<v8::Value> 112 v8plus::ObjectWrap::_new(const v8::Arguments &args) 113 { 114 v8::HandleScope scope; 115 v8plus::ObjectWrap *op = new v8plus::ObjectWrap(); 116 nvlist_t *c_excp; 117 nvlist_t *c_args; 118 119 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL) 120 return (V8PLUS_THROW_DEFAULT()); 121 122 if (_crossthread_init_done == _B_FALSE) { 123 /* 124 * Initialise structures for off-event-loop method calls. 125 * 126 * Note that uv_async_init() must be called inside the libuv 127 * Event Loop, so we do it here. We also want to record the 128 * thread ID of the Event Loop thread so as to determine what 129 * kind of method calls to make later. 130 */ 131 _uv_event_thread = pthread_self(); 132 if (uv_async_init(uv_default_loop(), &_uv_async, 133 v8plus_async_callback) != 0) 134 v8plus_panic("unable to initialise uv_async_t"); 135 if (pthread_mutex_init(&_callq_mutex, NULL) != 0) 136 v8plus_panic("unable to initialise mutex"); 137 _crossthread_init_done = _B_TRUE; 138 } 139 140 c_excp = v8plus_ctor(c_args, &op->_c_impl); 141 nvlist_free(c_args); 142 if (op->_c_impl == NULL) { 143 if (c_excp == NULL) { 144 return (V8PLUS_THROW_DEFAULT()); 145 } else { 146 return (V8PLUS_THROW_DECORATED(c_excp)); 147 } 148 } 149 150 _objhash.insert(std::make_pair(op->_c_impl, op)); 151 op->Wrap(args.This()); 152 153 return (args.This()); 154 } 155 156 v8plus::ObjectWrap::~ObjectWrap() 157 { 158 v8plus_dtor(_c_impl); 159 (void) _objhash.erase(_c_impl); 160 } 161 162 v8::Handle<v8::Value> 163 v8plus::ObjectWrap::cons(const v8::Arguments &args) 164 { 165 v8::HandleScope scope; 166 const unsigned argc = 1; 167 v8::Handle<v8::Value> argv[argc] = { args[0] }; 168 v8::Local<v8::Object> instance = _constructor->NewInstance(argc, argv); 169 170 return (scope.Close(instance)); 171 } 172 173 v8plus::ObjectWrap * 174 v8plus::ObjectWrap::objlookup(const void *cop) 175 { 176 std::unordered_map<void *, v8plus::ObjectWrap *>::iterator it; 177 178 if ((it = _objhash.find(const_cast<void *>(cop))) == _objhash.end()) 179 v8plus_panic("unable to find C++ wrapper for %p\n", cop); 180 181 return (it->second); 182 } 183 184 /* 185 * This is the entry point for all methods. We will start by demultiplexing 186 * out the C method from the function name by which we were called. There is 187 * probably some mechanism by which overly clever JavaScript code could make 188 * this not match the actual name; this will kill your Node process, so don't 189 * get cute. 190 */ 191 v8::Handle<v8::Value> 192 v8plus::ObjectWrap::_entry(const v8::Arguments &args) 193 { 194 v8::HandleScope scope; 195 v8plus::ObjectWrap *op = 196 node::ObjectWrap::Unwrap<v8plus::ObjectWrap>(args.This()); 197 nvlist_t *c_args; 198 nvlist_t *c_out; 199 nvlist_t *excp; 200 nvpair_t *rpp; 201 v8::Local<v8::String> self = args.Callee()->GetName()->ToString(); 202 v8::String::Utf8Value selfsv(self); 203 const char *fn = *selfsv; 204 const v8plus_method_descr_t *mdp; 205 v8plus_c_method_f c_method = NULL; 206 uint_t i; 207 208 for (i = 0; i < v8plus_method_count; i++) { 209 mdp = &_mtbl[i]; 210 if (strcmp(mdp->md_name, fn) == 0) { 211 c_method = mdp->md_c_func; 212 break; 213 } 214 } 215 216 if (c_method == NULL) 217 v8plus_panic("impossible method name %s\n", fn); 218 219 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL) 220 return (V8PLUS_THROW_DEFAULT()); 221 222 c_out = c_method(op->_c_impl, c_args); 223 nvlist_free(c_args); 224 225 if (c_out == NULL) { 226 if (_v8plus_errno == V8PLUSERR_NOERROR) 227 return (scope.Close(v8::Undefined())); 228 else 229 return (V8PLUS_THROW_DEFAULT()); 230 } else { 231 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) { 232 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp); 233 nvlist_free(c_out); 234 return (x); 235 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) { 236 v8::Handle<v8::Value> r = 237 v8plus::nvpair_to_v8_Value(rpp); 238 nvlist_free(c_out); 239 return (scope.Close(r)); 240 } else { 241 v8plus_panic("bad encoded object in return"); 242 } 243 } 244 245 /*NOTREACHED*/ 246 return (v8::Undefined()); 247 } 248 249 v8::Handle<v8::Value> 250 v8plus::ObjectWrap::_static_entry(const v8::Arguments &args) 251 { 252 v8::HandleScope scope; 253 nvlist_t *c_args; 254 nvlist_t *c_out; 255 nvlist_t *excp; 256 nvpair_t *rpp; 257 v8::Local<v8::String> self = args.Callee()->GetName()->ToString(); 258 v8::String::Utf8Value selfsv(self); 259 const char *fn = *selfsv; 260 const v8plus_static_descr_t *sdp; 261 v8plus_c_static_f c_static = NULL; 262 uint_t i; 263 264 for (i = 0; i < v8plus_static_method_count; i++) { 265 sdp = &_stbl[i]; 266 if (strcmp(sdp->sd_name, fn) == 0) { 267 c_static = sdp->sd_c_func; 268 break; 269 } 270 } 271 272 if (c_static == NULL) 273 v8plus_panic("impossible static method name %s\n", fn); 274 275 if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL) 276 return (V8PLUS_THROW_DEFAULT()); 277 278 c_out = c_static(c_args); 279 nvlist_free(c_args); 280 281 if (c_out == NULL) { 282 if (_v8plus_errno == V8PLUSERR_NOERROR) 283 return (scope.Close(v8::Undefined())); 284 else 285 return (V8PLUS_THROW_DEFAULT()); 286 } else { 287 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) { 288 v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp); 289 nvlist_free(c_out); 290 return (x); 291 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) { 292 v8::Handle<v8::Value> r = 293 v8plus::nvpair_to_v8_Value(rpp); 294 nvlist_free(c_out); 295 return (scope.Close(r)); 296 } else { 297 v8plus_panic("bad encoded object in return"); 298 } 299 } 300 301 /*NOTREACHED*/ 302 return (v8::Undefined()); 303 } 304 305 v8::Handle<v8::Value> 306 v8plus::ObjectWrap::call(const char *name, 307 int argc, v8::Handle<v8::Value> argv[]) 308 { 309 v8::Handle<v8::Value> v = v8::Undefined(); 310 v8::Local<v8::Value> f = handle_->Get(v8::String::NewSymbol(name)); 311 312 /* 313 * XXX - we'd like to throw here, but for some reason our TryCatch 314 * block doesn't seem to handle the exception. 315 */ 316 if (!f->IsFunction()) 317 return (v8::Undefined()); 318 319 #ifdef NODE_MAKECALLBACK_RETURN 320 v = 321 #endif 322 node::MakeCallback(handle_, name, argc, argv); 323 324 return (v); 325 } 326 327 void 328 v8plus::ObjectWrap::public_Ref(void) 329 { 330 this->Ref(); 331 } 332 333 void 334 v8plus::ObjectWrap::public_Unref(void) 335 { 336 this->Unref(); 337 } 338 339 boolean_t 340 v8plus::ObjectWrap::in_event_thread(void) 341 { 342 if (_crossthread_init_done != _B_TRUE) 343 v8plus_panic("cross thread call init not done!"); 344 345 return (_uv_event_thread == pthread_self() ? _B_TRUE : _B_FALSE); 346 } 347 348 v8plus_async_call_t * 349 v8plus::ObjectWrap::next_async_call(void) 350 { 351 v8plus_async_call_t *ret = NULL; 352 353 if (pthread_mutex_lock(&_callq_mutex) != 0) 354 v8plus_panic("could not lock callq mutex"); 355 356 if (!_callq.empty()) { 357 ret = _callq.front(); 358 _callq.pop(); 359 } 360 361 if (pthread_mutex_unlock(&_callq_mutex) != 0) 362 v8plus_panic("could not release callq mutex"); 363 364 return (ret); 365 } 366 367 void 368 v8plus::ObjectWrap::post_async_call(v8plus_async_call_t *ac) 369 { 370 if (pthread_mutex_lock(&_callq_mutex) != 0) 371 v8plus_panic("could not lock callq mutex"); 372 373 _callq.push(ac); 374 375 if (pthread_mutex_unlock(&_callq_mutex) != 0) 376 v8plus_panic("could not release callq mutex"); 377 378 uv_async_send(&_uv_async); 379 } 380 381 extern "C" void 382 init(v8::Handle<v8::Object> target) 383 { 384 v8plus::ObjectWrap::init(target); 385 }