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 }