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 static char *
  22 function_name(const char *lambda)
  23 {
  24         char *fn;
  25         size_t len;
  26 
  27         len = snprintf(NULL, 0, METHOD_NAME_FMT,
  28             v8plus_js_class_name, lambda);
  29         if ((fn = (char *)malloc(len + 1)) == NULL)
  30                 v8plus_panic("out of memory for function name for %s", lambda);
  31 
  32         (void) snprintf(fn, len + 1, METHOD_NAME_FMT,
  33                     v8plus_js_class_name, lambda);
  34 
  35         return (fn);
  36 }
  37 
  38 void
  39 v8plus::ObjectWrap::init(v8::Handle<v8::Object> target)
  40 {
  41         uint_t i;
  42 
  43         if (v8plus_static_method_count > 0) {
  44                 const v8plus_static_descr_t *sdp;
  45 
  46                 _stbl = new (std::nothrow)
  47                     v8plus_static_descr_t[v8plus_static_method_count];
  48                 if (_stbl == NULL)
  49                         v8plus_panic("out of memory for static method table");
  50 
  51                 for (i = 0; i < v8plus_static_method_count; i++) {
  52                         v8::Local<v8::FunctionTemplate> fth =
  53                             v8::FunctionTemplate::New(_static_entry);
  54                         v8::Local<v8::Function> fh = fth->GetFunction();
  55                         sdp = &v8plus_static_methods[i];
  56 
  57                         _stbl[i].sd_name = function_name(sdp->sd_name);
  58                         _stbl[i].sd_c_func = sdp->sd_c_func;
  59 
  60                         fh->SetName(v8::String::New(_stbl[i].sd_name));
  61 
  62                         target->Set(v8::String::NewSymbol(sdp->sd_name), fh);
  63                 }
  64         }
  65 
  66 
  67         if (v8plus_method_count > 0) {
  68                 v8::Local<v8::FunctionTemplate> tpl =
  69                     v8::FunctionTemplate::New(_new);
  70                 const v8plus_method_descr_t *mdp;
  71 
  72                 _mtbl = new (std::nothrow)
  73                     v8plus_method_descr_t[v8plus_method_count];
  74                 if (_mtbl == NULL)
  75                         v8plus_panic("out of memory for method table");
  76 
  77                 tpl->SetClassName(v8::String::NewSymbol(v8plus_js_class_name));
  78                 tpl->InstanceTemplate()->SetInternalFieldCount(
  79                     v8plus_method_count);
  80 
  81                 for (i = 0; i < v8plus_method_count; i++) {
  82                         v8::Local<v8::FunctionTemplate> fth =
  83                             v8::FunctionTemplate::New(_entry);
  84                         v8::Local<v8::Function> fh = fth->GetFunction();
  85                         mdp = &v8plus_methods[i];
  86 
  87                         _mtbl[i].md_name = function_name(mdp->md_name);
  88                         _mtbl[i].md_c_func = mdp->md_c_func;
  89 
  90                         fh->SetName(v8::String::New(_mtbl[i].md_name));
  91 
  92                         tpl->PrototypeTemplate()->Set(
  93                             v8::String::NewSymbol(mdp->md_name), fh);
  94                 }
  95 
  96                 _constructor =
  97                     v8::Persistent<v8::Function>::New(tpl->GetFunction());
  98 
  99                 target->Set(v8::String::NewSymbol(v8plus_js_factory_name),
 100                     v8::FunctionTemplate::New(
 101                     v8plus::ObjectWrap::cons)->GetFunction());
 102         }
 103 }
 104 
 105 v8::Handle<v8::Value>
 106 v8plus::ObjectWrap::_new(const v8::Arguments &args)
 107 {
 108         v8::HandleScope scope;
 109         v8plus::ObjectWrap *op = new v8plus::ObjectWrap();
 110         nvlist_t *c_excp;
 111         nvlist_t *c_args;
 112 
 113         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 114                 return (V8PLUS_THROW_DEFAULT());
 115 
 116         c_excp = v8plus_ctor(c_args, &op->_c_impl);
 117         nvlist_free(c_args);
 118         if (op->_c_impl == NULL) {
 119                 if (c_excp == NULL) {
 120                         return (V8PLUS_THROW_DEFAULT());
 121                 } else {
 122                         return (V8PLUS_THROW_DECORATED(c_excp));
 123                 }
 124         }
 125 
 126         _objhash.insert(std::make_pair(op->_c_impl, op));
 127         op->Wrap(args.This());
 128 
 129         return (args.This());
 130 }
 131 
 132 v8plus::ObjectWrap::~ObjectWrap()
 133 {
 134         v8plus_dtor(_c_impl);
 135         (void) _objhash.erase(_c_impl);
 136 }
 137 
 138 v8::Handle<v8::Value>
 139 v8plus::ObjectWrap::cons(const v8::Arguments &args)
 140 {
 141         v8::HandleScope scope;
 142         const unsigned argc = 1;
 143         v8::Handle<v8::Value> argv[argc] = { args[0] };
 144         v8::Local<v8::Object> instance = _constructor->NewInstance(argc, argv);
 145 
 146         return (scope.Close(instance));
 147 }
 148 
 149 v8plus::ObjectWrap *
 150 v8plus::ObjectWrap::objlookup(const void *cop)
 151 {
 152         std::unordered_map<void *, v8plus::ObjectWrap *>::iterator it;
 153 
 154         if ((it = _objhash.find(const_cast<void *>(cop))) == _objhash.end())
 155                 v8plus_panic("unable to find C++ wrapper for %p\n", cop);
 156 
 157         return (it->second);
 158 }
 159 
 160 /*
 161  * This is the entry point for all methods.  We will start by demultiplexing
 162  * out the C method from the function name by which we were called.  There is
 163  * probably some mechanism by which overly clever JavaScript code could make
 164  * this not match the actual name; this will kill your Node process, so don't
 165  * get cute.
 166  */
 167 v8::Handle<v8::Value>
 168 v8plus::ObjectWrap::_entry(const v8::Arguments &args)
 169 {
 170         v8::HandleScope scope;
 171         v8plus::ObjectWrap *op =
 172             node::ObjectWrap::Unwrap<v8plus::ObjectWrap>(args.This());
 173         nvlist_t *c_args;
 174         nvlist_t *c_out;
 175         nvlist_t *excp;
 176         nvpair_t *rpp;
 177         v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
 178         v8::String::Utf8Value selfsv(self);
 179         const char *fn = *selfsv;
 180         const v8plus_method_descr_t *mdp;
 181         v8plus_c_method_f c_method = NULL;
 182         uint_t i;
 183 
 184         for (i = 0; i < v8plus_method_count; i++) {
 185                 mdp = &_mtbl[i];
 186                 if (strcmp(mdp->md_name, fn) == 0) {
 187                         c_method = mdp->md_c_func;
 188                         break;
 189                 }
 190         }
 191 
 192         if (c_method == NULL)
 193                 v8plus_panic("impossible method name %s\n", fn);
 194 
 195         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 196                 return (V8PLUS_THROW_DEFAULT());
 197 
 198         c_out = c_method(op->_c_impl, c_args);
 199         nvlist_free(c_args);
 200 
 201         if (c_out == NULL) {
 202                 if (_v8plus_errno == V8PLUSERR_NOERROR)
 203                         return (scope.Close(v8::Undefined()));
 204                 else
 205                         return (V8PLUS_THROW_DEFAULT());
 206         } else {
 207                 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
 208                         v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
 209                         nvlist_free(c_out);
 210                         return (x);
 211                 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
 212                         v8::Handle<v8::Value> r =
 213                             v8plus::nvpair_to_v8_Value(rpp);
 214                         nvlist_free(c_out);
 215                         return (scope.Close(r));
 216                 } else {
 217                         v8plus_panic("bad encoded object in return");
 218                 }
 219         }
 220 
 221         /*NOTREACHED*/
 222         return (v8::Undefined());
 223 }
 224 
 225 v8::Handle<v8::Value>
 226 v8plus::ObjectWrap::_static_entry(const v8::Arguments &args)
 227 {
 228         v8::HandleScope scope;
 229         nvlist_t *c_args;
 230         nvlist_t *c_out;
 231         nvlist_t *excp;
 232         nvpair_t *rpp;
 233         v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
 234         v8::String::Utf8Value selfsv(self);
 235         const char *fn = *selfsv;
 236         const v8plus_static_descr_t *sdp;
 237         v8plus_c_static_f c_static = NULL;
 238         uint_t i;
 239 
 240         for (i = 0; i < v8plus_static_method_count; i++) {
 241                 sdp = &_stbl[i];
 242                 if (strcmp(sdp->sd_name, fn) == 0) {
 243                         c_static = sdp->sd_c_func;
 244                         break;
 245                 }
 246         }
 247 
 248         if (c_static == NULL)
 249                 v8plus_panic("impossible static method name %s\n", fn);
 250 
 251         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 252                 return (V8PLUS_THROW_DEFAULT());
 253 
 254         c_out = c_static(c_args);
 255         nvlist_free(c_args);
 256 
 257         if (c_out == NULL) {
 258                 if (_v8plus_errno == V8PLUSERR_NOERROR)
 259                         return (scope.Close(v8::Undefined()));
 260                 else
 261                         return (V8PLUS_THROW_DEFAULT());
 262         } else {
 263                 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
 264                         v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
 265                         nvlist_free(c_out);
 266                         return (x);
 267                 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
 268                         v8::Handle<v8::Value> r =
 269                             v8plus::nvpair_to_v8_Value(rpp);
 270                         nvlist_free(c_out);
 271                         return (scope.Close(r));
 272                 } else {
 273                         v8plus_panic("bad encoded object in return");
 274                 }
 275         }
 276 
 277         /*NOTREACHED*/
 278         return (v8::Undefined());
 279 }
 280 
 281 v8::Handle<v8::Value>
 282 v8plus::ObjectWrap::call(const char *name,
 283     int argc, v8::Handle<v8::Value> argv[])
 284 {
 285         v8::Handle<v8::Value> v = v8::Undefined();
 286         v8::Local<v8::Value> f = handle_->Get(v8::String::NewSymbol(name));
 287 
 288         /*
 289          * XXX - we'd like to throw here, but for some reason our TryCatch
 290          * block doesn't seem to handle the exception.
 291          */
 292         if (!f->IsFunction())
 293                 return (v8::Undefined());
 294 
 295 #ifdef NODE_MAKECALLBACK_RETURN
 296         v =
 297 #endif
 298         node::MakeCallback(handle_, name, argc, argv);
 299 
 300         return (v);
 301 }
 302 
 303 void
 304 v8plus::ObjectWrap::public_Ref(void)
 305 {
 306         this->Ref();
 307 }
 308 
 309 void
 310 v8plus::ObjectWrap::public_Unref(void)
 311 {
 312         this->Unref();
 313 }
 314 
 315 extern "C" void
 316 init(v8::Handle<v8::Object> target)
 317 {
 318         v8plus::ObjectWrap::init(target);
 319 }