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         v8plus_crossthread_init();
 105 }
 106 
 107 v8::Handle<v8::Value>
 108 v8plus::ObjectWrap::_new(const v8::Arguments &args)
 109 {
 110         v8::HandleScope scope;
 111         v8plus::ObjectWrap *op = new v8plus::ObjectWrap();
 112         nvlist_t *c_excp;
 113         nvlist_t *c_args;
 114 
 115         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 116                 return (V8PLUS_THROW_DEFAULT());
 117 
 118         c_excp = v8plus_ctor(c_args, &op->_c_impl);
 119         nvlist_free(c_args);
 120         if (op->_c_impl == NULL) {
 121                 if (c_excp == NULL) {
 122                         return (V8PLUS_THROW_DEFAULT());
 123                 } else {
 124                         return (V8PLUS_THROW_DECORATED(c_excp));
 125                 }
 126         }
 127 
 128         _objhash.insert(std::make_pair(op->_c_impl, op));
 129         op->Wrap(args.This());
 130 
 131         return (args.This());
 132 }
 133 
 134 v8plus::ObjectWrap::~ObjectWrap()
 135 {
 136         v8plus_dtor(_c_impl);
 137         (void) _objhash.erase(_c_impl);
 138 }
 139 
 140 v8::Handle<v8::Value>
 141 v8plus::ObjectWrap::cons(const v8::Arguments &args)
 142 {
 143         v8::HandleScope scope;
 144         const unsigned argc = 1;
 145         v8::Handle<v8::Value> argv[argc] = { args[0] };
 146         v8::Local<v8::Object> instance = _constructor->NewInstance(argc, argv);
 147 
 148         return (scope.Close(instance));
 149 }
 150 
 151 v8plus::ObjectWrap *
 152 v8plus::ObjectWrap::objlookup(const void *cop)
 153 {
 154         std::unordered_map<void *, v8plus::ObjectWrap *>::iterator it;
 155 
 156         if ((it = _objhash.find(const_cast<void *>(cop))) == _objhash.end())
 157                 v8plus_panic("unable to find C++ wrapper for %p\n", cop);
 158 
 159         return (it->second);
 160 }
 161 
 162 /*
 163  * This is the entry point for all methods.  We will start by demultiplexing
 164  * out the C method from the function name by which we were called.  There is
 165  * probably some mechanism by which overly clever JavaScript code could make
 166  * this not match the actual name; this will kill your Node process, so don't
 167  * get cute.
 168  */
 169 v8::Handle<v8::Value>
 170 v8plus::ObjectWrap::_entry(const v8::Arguments &args)
 171 {
 172         v8::HandleScope scope;
 173         v8plus::ObjectWrap *op =
 174             node::ObjectWrap::Unwrap<v8plus::ObjectWrap>(args.This());
 175         nvlist_t *c_args;
 176         nvlist_t *c_out;
 177         nvlist_t *excp;
 178         nvpair_t *rpp;
 179         v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
 180         v8::String::Utf8Value selfsv(self);
 181         const char *fn = *selfsv;
 182         const v8plus_method_descr_t *mdp;
 183         v8plus_c_method_f c_method = NULL;
 184         uint_t i;
 185 
 186         for (i = 0; i < v8plus_method_count; i++) {
 187                 mdp = &_mtbl[i];
 188                 if (strcmp(mdp->md_name, fn) == 0) {
 189                         c_method = mdp->md_c_func;
 190                         break;
 191                 }
 192         }
 193 
 194         if (c_method == NULL)
 195                 v8plus_panic("impossible method name %s\n", fn);
 196 
 197         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 198                 return (V8PLUS_THROW_DEFAULT());
 199 
 200         c_out = c_method(op->_c_impl, c_args);
 201         nvlist_free(c_args);
 202 
 203         if (c_out == NULL) {
 204                 if (_v8plus_errno == V8PLUSERR_NOERROR)
 205                         return (scope.Close(v8::Undefined()));
 206                 else
 207                         return (V8PLUS_THROW_DEFAULT());
 208         } else {
 209                 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
 210                         v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
 211                         nvlist_free(c_out);
 212                         return (x);
 213                 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
 214                         v8::Handle<v8::Value> r =
 215                             v8plus::nvpair_to_v8_Value(rpp);
 216                         nvlist_free(c_out);
 217                         return (scope.Close(r));
 218                 } else {
 219                         v8plus_panic("bad encoded object in return");
 220                 }
 221         }
 222 
 223         /*NOTREACHED*/
 224         return (v8::Undefined());
 225 }
 226 
 227 v8::Handle<v8::Value>
 228 v8plus::ObjectWrap::_static_entry(const v8::Arguments &args)
 229 {
 230         v8::HandleScope scope;
 231         nvlist_t *c_args;
 232         nvlist_t *c_out;
 233         nvlist_t *excp;
 234         nvpair_t *rpp;
 235         v8::Local<v8::String> self = args.Callee()->GetName()->ToString();
 236         v8::String::Utf8Value selfsv(self);
 237         const char *fn = *selfsv;
 238         const v8plus_static_descr_t *sdp;
 239         v8plus_c_static_f c_static = NULL;
 240         uint_t i;
 241 
 242         for (i = 0; i < v8plus_static_method_count; i++) {
 243                 sdp = &_stbl[i];
 244                 if (strcmp(sdp->sd_name, fn) == 0) {
 245                         c_static = sdp->sd_c_func;
 246                         break;
 247                 }
 248         }
 249 
 250         if (c_static == NULL)
 251                 v8plus_panic("impossible static method name %s\n", fn);
 252 
 253         if ((c_args = v8plus::v8_Arguments_to_nvlist(args)) == NULL)
 254                 return (V8PLUS_THROW_DEFAULT());
 255 
 256         c_out = c_static(c_args);
 257         nvlist_free(c_args);
 258 
 259         if (c_out == NULL) {
 260                 if (_v8plus_errno == V8PLUSERR_NOERROR)
 261                         return (scope.Close(v8::Undefined()));
 262                 else
 263                         return (V8PLUS_THROW_DEFAULT());
 264         } else {
 265                 if (nvlist_lookup_nvlist(c_out, "err", &excp) == 0) {
 266                         v8::Handle<v8::Value> x = V8PLUS_THROW_DECORATED(excp);
 267                         nvlist_free(c_out);
 268                         return (x);
 269                 } else if (nvlist_lookup_nvpair(c_out, "res", &rpp) == 0) {
 270                         v8::Handle<v8::Value> r =
 271                             v8plus::nvpair_to_v8_Value(rpp);
 272                         nvlist_free(c_out);
 273                         return (scope.Close(r));
 274                 } else {
 275                         v8plus_panic("bad encoded object in return");
 276                 }
 277         }
 278 
 279         /*NOTREACHED*/
 280         return (v8::Undefined());
 281 }
 282 
 283 v8::Handle<v8::Value>
 284 v8plus::ObjectWrap::call(const char *name,
 285     int argc, v8::Handle<v8::Value> argv[])
 286 {
 287         v8::Handle<v8::Value> v = v8::Undefined();
 288         v8::Local<v8::Value> f = handle_->Get(v8::String::NewSymbol(name));
 289 
 290         /*
 291          * XXX - we'd like to throw here, but for some reason our TryCatch
 292          * block doesn't seem to handle the exception.
 293          */
 294         if (!f->IsFunction())
 295                 return (v8::Undefined());
 296 
 297 #ifdef NODE_MAKECALLBACK_RETURN
 298         v =
 299 #endif
 300         node::MakeCallback(handle_, name, argc, argv);
 301 
 302         return (v);
 303 }
 304 
 305 void
 306 v8plus::ObjectWrap::public_Ref(void)
 307 {
 308         this->Ref();
 309 }
 310 
 311 void
 312 v8plus::ObjectWrap::public_Unref(void)
 313 {
 314         this->Unref();
 315 }
 316 
 317 extern "C" void
 318 init(v8::Handle<v8::Object> target)
 319 {
 320         v8plus::ObjectWrap::init(target);
 321 }