1 /*
   2  * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
   3  */
   4 
   5 #include <sys/types.h>
   6 #include <stdarg.h>
   7 #include <stdlib.h>
   8 #include <string.h>
   9 #include <alloca.h>
  10 #include <dlfcn.h>
  11 #include <libnvpair.h>
  12 #include <node.h>
  13 #include <v8.h>
  14 #include <unordered_map>
  15 #include <string>
  16 #include "v8plus_impl.h"
  17 
  18 #define V8PLUS_OBJ_TYPE_MEMBER  ".__v8plus_type"
  19 #define V8_EXCEPTION_CTOR_FMT \
  20         "_ZN2v89Exception%u%sENS_6HandleINS_6StringEEE"
  21 
  22 typedef struct cb_hdl {
  23         v8::Handle<v8::Function> ch_hdl;
  24         uint_t ch_refs;
  25         boolean_t ch_persist;
  26 } cb_hdl_t;
  27 
  28 static std::unordered_map<uint64_t, cb_hdl_t> cbhash;
  29 static uint64_t cbnext;
  30 static void (*__real_nvlist_free)(nvlist_t *);
  31 
  32 
  33 static const char *
  34 cstr(const v8::String::Utf8Value &v)
  35 {
  36         return (*v);
  37 }
  38 
  39 /*
  40  * Convenience macros for adding stuff to an nvlist and returning on failure.
  41  */
  42 #define LA_U(_l, _n, _e) \
  43         if (((_e) = nvlist_add_boolean((_l), (_n))) != 0) \
  44                 return (_e)
  45 
  46 #define LA_N(_l, _n, _e) \
  47         if (((_e) = nvlist_add_byte((_l), (_n), 0)) != 0) \
  48                 return (_e)
  49 
  50 #define LA_V(_l, _t, _n, _v, _e) \
  51         if (((_e) = nvlist_add_##_t((_l), (_n), (_v))) != 0) \
  52                 return (_e)
  53 
  54 #define LA_VA(_l, _t, _n, _v, _c, _e) \
  55         if (((_e) = nvlist_add_##_t##_array((_l), (_n), (_v), (_c))) != 0) \
  56                 return (_e)
  57 
  58 /*
  59  * Add an element named <name> to list <lp> with a transcoded value
  60  * corresponding to <vh> if possible.  Only primitive types, objects that are
  61  * thin wrappers for primitive types, and objects containing members whose
  62  * types are all any of the above can be transcoded.
  63  *
  64  * Booleans and their Object type are encoded as boolean_value.
  65  * Numbers and their Object type are encoded as double.
  66  * Strings and their Object type are encoded as C strings (and assumed UTF-8).
  67  * Any Object (including an Array) is encoded as an nvlist whose elements
  68  * are the Object's own properties.
  69  * Null is encoded as a byte with value 0.
  70  * Undefined is encoded as the valueless boolean.
  71  *
  72  * Returns EINVAL if any argument fails these tests, or any other error code
  73  * that may be returned by nvlist_add_XXX(3nvpair).
  74  */
  75 static int
  76 nvlist_add_v8_Value(nvlist_t *lp, const char *name,
  77     const v8::Handle<v8::Value> &vh)
  78 {
  79         int err = 0;
  80 
  81         if (vh->IsBoolean()) {
  82                 boolean_t vv = vh->BooleanValue() ? _B_TRUE : _B_FALSE;
  83                 LA_V(lp, boolean_value, name, vv, err);
  84         } else if (vh->IsNumber()) {
  85                 double vv = vh->NumberValue();
  86                 LA_V(lp, double, name, vv, err);
  87         } else if (vh->IsString()) {
  88                 v8::String::Utf8Value s(vh);
  89                 const char *vv = cstr(s);
  90                 LA_V(lp, string, name, vv, err);
  91         } else if (vh->IsUndefined()) {
  92                 LA_U(lp, name, err);
  93         } else if (vh->IsNull()) {
  94                 LA_N(lp, name, err);
  95         } else if (vh->IsNumberObject()) {
  96                 double vv = vh->NumberValue();
  97                 LA_V(lp, double, name, vv, err);
  98         } else if (vh->IsStringObject()) {
  99                 v8::String::Utf8Value s(vh);
 100                 const char *vv = cstr(s);
 101                 LA_V(lp, string, name, vv, err);
 102         } else if (vh->IsBooleanObject()) {
 103                 boolean_t vv = vh->BooleanValue() ? _B_TRUE : _B_FALSE;
 104                 LA_V(lp, boolean_value, name, vv, err);
 105         } else if (vh->IsFunction()) {
 106                 cb_hdl_t ch;
 107 
 108                 ch.ch_hdl = v8::Handle<v8::Function>::Cast(vh);
 109                 ch.ch_refs = 1;
 110                 ch.ch_persist = _B_FALSE;
 111 
 112                 while (cbhash.find(cbnext) != cbhash.end())
 113                         ++cbnext;
 114                 cbhash.insert(std::make_pair(cbnext, ch));
 115 
 116                 LA_VA(lp, string, V8PLUS_JSF_COOKIE, NULL, 0, err);
 117                 LA_VA(lp, uint64, name, &cbnext, 1, err);
 118         } else if (vh->IsObject()) {
 119                 v8::Local<v8::Object> oh = vh->ToObject();
 120                 v8::Local<v8::Array> keys = oh->GetOwnPropertyNames();
 121                 v8::Local<v8::String> th = oh->GetConstructorName();
 122                 v8::String::Utf8Value tv(th);
 123                 const char *type = cstr(tv);
 124                 nvlist_t *vlp;
 125                 uint_t i;
 126 
 127                 if ((err = nvlist_alloc(&vlp, NV_UNIQUE_NAME, 0)) != 0)
 128                         return (err);
 129 
 130                 /* XXX this is vile; can we handle this generally? */
 131                 if (strcmp(type, "Object") != 0) {
 132                         if (strcmp(type, "Array") == 0) {
 133                                 if ((err = nvlist_add_string(vlp,
 134                                     V8PLUS_OBJ_TYPE_MEMBER, type)) != 0) {
 135                                         nvlist_free(vlp);
 136                                         return (err);
 137                                 }
 138                         } else {
 139                                 /*
 140                                  * XXX This is (C) programmer error.  Should
 141                                  * we plumb up a way to throw here?
 142                                  */
 143                                 (void) v8plus_panic("can't handle %s", type);
 144                         }
 145                 }
 146 
 147                 for (i = 0; i < keys->Length(); i++) {
 148                         char knname[16];
 149                         v8::Local<v8::Value> mk;
 150                         v8::Local<v8::Value> mv;
 151                         const char *k;
 152 
 153                         (void) snprintf(knname, sizeof (knname), "%u", i);
 154                         mk = keys->Get(v8::String::New(knname));
 155                         mv = oh->Get(mk);
 156                         v8::String::Utf8Value mks(mk);
 157                         k = cstr(mks);
 158 
 159                         if ((err = nvlist_add_v8_Value(vlp, k, mv)) != 0) {
 160                                 nvlist_free(vlp);
 161                                 return (err);
 162                         }
 163                 }
 164 
 165                 LA_V(lp, nvlist, name, vlp, err);
 166         } else {
 167                 return (EINVAL);
 168         }
 169 
 170         return (0);
 171 }
 172 
 173 #undef  LA_U
 174 #undef  LA_N
 175 #undef  LA_V
 176 
 177 nvlist_t *
 178 v8plus::v8_Arguments_to_nvlist(const v8::Arguments &args)
 179 {
 180         char name[16];
 181         nvlist_t *lp;
 182         int err;
 183         uint_t i;
 184 
 185         if ((err = nvlist_alloc(&lp, NV_UNIQUE_NAME, 0)) != 0)
 186                 return (v8plus_nverr(err, NULL));
 187 
 188         for (i = 0; i < (uint_t)args.Length(); i++) {
 189                 (void) snprintf(name, sizeof (name), "%u", i);
 190                 if ((err = nvlist_add_v8_Value(lp, name, args[i])) != 0) {
 191                         nvlist_free(lp);
 192                         return (v8plus_nverr(err, name));
 193                 }
 194         }
 195 
 196         return (lp);
 197 }
 198 
 199 static void
 200 decorate_object(v8::Local<v8::Object> &oh, const nvlist_t *lp)
 201 {
 202         nvpair_t *pp = NULL;
 203 
 204         while ((pp =
 205             nvlist_next_nvpair(const_cast<nvlist_t *>(lp), pp)) != NULL) {
 206                 oh->Set(v8::String::New(nvpair_name(pp)),
 207                     v8plus::nvpair_to_v8_Value(pp));
 208         }
 209 }
 210 
 211 #define RETURN_JS(_p, _jt, _ct, _xt, _pt) \
 212         do { \
 213                 _ct _v; \
 214                 (void) nvpair_value_##_pt(const_cast<nvpair_t *>(_p), &_v); \
 215                 return (v8::_jt::New((_xt)_v)); \
 216         } while (0)
 217 
 218 v8::Handle<v8::Value>
 219 v8plus::nvpair_to_v8_Value(const nvpair_t *pp)
 220 {
 221         const char *type;
 222 
 223         switch (nvpair_type(const_cast<nvpair_t *>(pp))) {
 224         case DATA_TYPE_BOOLEAN:
 225                 return (v8::Undefined());
 226         case DATA_TYPE_BOOLEAN_VALUE:
 227                 RETURN_JS(pp, Boolean, boolean_t, bool, boolean_value);
 228         case DATA_TYPE_BYTE:
 229         {
 230                 uint8_t _v = (uint8_t)-1;
 231 
 232                 if (nvpair_value_byte(const_cast<nvpair_t *>(pp), &_v) != 0 ||
 233                     _v != 0) {
 234                         v8plus_panic("bad byte value %02x\n", _v);
 235                 }
 236 
 237                 return (v8::Null());
 238         }
 239         case DATA_TYPE_INT8:
 240                 RETURN_JS(pp, Number, int8_t, double, int8);
 241         case DATA_TYPE_UINT8:
 242                 RETURN_JS(pp, Number, uint8_t, double, uint8);
 243         case DATA_TYPE_INT16:
 244                 RETURN_JS(pp, Number, int16_t, double, int16);
 245         case DATA_TYPE_UINT16:
 246                 RETURN_JS(pp, Number, uint16_t, double, uint16);
 247         case DATA_TYPE_INT32:
 248                 RETURN_JS(pp, Number, int32_t, double, int32);
 249         case DATA_TYPE_UINT32:
 250                 RETURN_JS(pp, Number, uint32_t, double, uint32);
 251         case DATA_TYPE_INT64:
 252                 RETURN_JS(pp, Number, int64_t, double, int64);
 253         case DATA_TYPE_UINT64:
 254                 RETURN_JS(pp, Number, uint64_t, double, uint64);
 255         case DATA_TYPE_DOUBLE:
 256                 RETURN_JS(pp, Number, double, double, double);
 257         case DATA_TYPE_STRING:
 258                 RETURN_JS(pp, String, char *, const char *, string);
 259         case DATA_TYPE_UINT64_ARRAY:
 260         {
 261                 std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
 262                 uint64_t *vp;
 263                 uint_t nv;
 264                 int err;
 265 
 266                 if ((err = nvpair_value_uint64_array(const_cast<nvpair_t *>(pp),
 267                     &vp, &nv)) != 0)
 268                         v8plus_panic("bad JSFUNC pair: %s", strerror(err));
 269                 if (nv != 1)
 270                         v8plus_panic("bad uint64 array length %u", nv);
 271                 if ((it = cbhash.find(*vp)) == cbhash.end())
 272                         v8plus_panic("callback hash tag %llu not found", *vp);
 273 
 274                 return (it->second.ch_hdl);
 275         }
 276         case DATA_TYPE_NVLIST:
 277         {
 278                 nvlist_t *lp;
 279                 v8::Local<v8::Object> oh;
 280 
 281                 (void) nvpair_value_nvlist(const_cast<nvpair_t *>(pp), &lp);
 282 
 283                 if (nvlist_lookup_string(const_cast<nvlist_t *>(lp),
 284                     V8PLUS_OBJ_TYPE_MEMBER, const_cast<char **>(&type)) != 0)
 285                         type = "Object";
 286 
 287                 if (strcmp(type, "Array") == 0)
 288                         oh = v8::Array::New()->ToObject();
 289                 else if (strcmp(type, "Object") != 0)
 290                         v8plus_panic("bad object type %s\n", type);
 291                 else
 292                         oh = v8::Object::New();
 293 
 294                 decorate_object(oh, lp);
 295                 return (oh);
 296         }
 297         default:
 298                 v8plus_panic("bad data type %d\n",
 299                     nvpair_type(const_cast<nvpair_t *>(pp)));
 300         }
 301 
 302         /*NOTREACHED*/
 303         return (v8::Undefined());
 304 }
 305 
 306 #undef  RETURN_JS
 307 
 308 static uint_t
 309 nvlist_length(const nvlist_t *lp)
 310 {
 311         uint_t l = 0;
 312         nvpair_t *pp = NULL;
 313 
 314         while ((pp =
 315             nvlist_next_nvpair(const_cast<nvlist_t *>(lp), pp)) != NULL)
 316                 ++l;
 317 
 318         return (l);
 319 }
 320 
 321 static void
 322 nvlist_to_v8_argv(const nvlist_t *lp, int *argcp, v8::Handle<v8::Value> *argv)
 323 {
 324         nvpair_t *pp;
 325         char name[16];
 326         int i;
 327 
 328         for (i = 0; i < *argcp; i++) {
 329                 (void) snprintf(name, sizeof (name), "%u", i);
 330                 if (nvlist_lookup_nvpair(const_cast<nvlist_t *>(lp),
 331                     name, &pp) != 0)
 332                         break;
 333                 argv[i] = v8plus::nvpair_to_v8_Value(pp);
 334         }
 335 
 336         *argcp = i;
 337 }
 338 
 339 static v8::Local<v8::Value>
 340 sexception(const char *type, const nvlist_t *lp, const char *msg)
 341 {
 342         char *ctor_name;
 343         v8::Local<v8::Value> (*excp_ctor)(v8::Handle<v8::String>);
 344         void *obj_hdl;
 345         size_t len;
 346         v8::Local<v8::Value> excp;
 347         v8::Local<v8::Object> obj;
 348         v8::Local<v8::String> jsmsg = v8::String::New(msg);
 349 
 350         if (type == NULL) {
 351                 type = v8plus_excptype(_v8plus_errno);
 352                 if (type == NULL)
 353                         type = "Error";
 354         }
 355 
 356         len = snprintf(NULL, 0, V8_EXCEPTION_CTOR_FMT,
 357             (uint_t)strlen(type), type);
 358         ctor_name = reinterpret_cast<char *>(alloca(len + 1));
 359         (void) snprintf(ctor_name, len + 1, V8_EXCEPTION_CTOR_FMT,
 360             (uint_t)strlen(type), type);
 361 
 362         obj_hdl = dlopen(NULL, RTLD_NOLOAD);
 363         if (obj_hdl == NULL)
 364                 v8plus_panic("%s\n", dlerror());
 365 
 366         excp_ctor = (v8::Local<v8::Value>(*)(v8::Handle<v8::String>))(
 367             dlsym(obj_hdl, ctor_name));
 368 
 369         if (excp_ctor == NULL) {
 370                 (void) dlclose(obj_hdl);
 371                 if (strcmp(type, "Error") == 0) {
 372                         v8plus_panic("Unable to find %s, aborting\n",
 373                             ctor_name);
 374                 } else {
 375                         excp = v8::Exception::Error(v8::String::New(
 376                             "Nested exception: illegal exception type"));
 377                         return (excp);
 378                 }
 379         }
 380 
 381         excp = excp_ctor(jsmsg);
 382         (void) dlclose(obj_hdl);
 383 
 384         if (lp == NULL)
 385                 return (excp);
 386 
 387         obj = excp->ToObject();
 388         decorate_object(obj, lp);
 389 
 390         return (excp);
 391 }
 392 
 393 v8::Local<v8::Value>
 394 v8plus::exception(const char *type, const nvlist_t *lp, const char *fmt, ...)
 395 {
 396         v8::Local<v8::Value> exception;
 397         char *msg;
 398         size_t len;
 399         va_list ap;
 400 
 401         if (fmt != NULL) {
 402                 va_start(ap, fmt);
 403                 len = vsnprintf(NULL, 0, fmt, ap);
 404                 va_end(ap);
 405                 msg = reinterpret_cast<char *>(alloca(len + 1));
 406 
 407                 va_start(ap, fmt);
 408                 (void) vsnprintf(msg, len + 1, fmt, ap);
 409                 va_end(ap);
 410         } else {
 411                 msg = _v8plus_errmsg;
 412         }
 413 
 414         exception = sexception(type, lp, msg);
 415 
 416         return (exception);
 417 }
 418 
 419 extern "C" nvlist_t *
 420 v8plus_call(v8plus_jsfunc_t f, const nvlist_t *lp)
 421 {
 422         std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
 423         const int max_argc = nvlist_length(lp);
 424         int argc, err;
 425         v8::Handle<v8::Value> argv[max_argc];
 426         v8::Handle<v8::Value> res;
 427         nvlist_t *rp;
 428 
 429         if ((it = cbhash.find(f)) == cbhash.end())
 430                 v8plus_panic("callback hash tag %llu not found", f);
 431 
 432         argc = max_argc;
 433         nvlist_to_v8_argv(lp, &argc, argv);
 434 
 435         if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
 436                 return (v8plus_nverr(err, NULL));
 437 
 438         v8::TryCatch tc;
 439         res = it->second.ch_hdl->Call(v8::Context::GetCurrent()->Global(),
 440             argc, argv);
 441         if (tc.HasCaught()) {
 442                 err = nvlist_add_v8_Value(rp, "err", tc.Exception());
 443                 tc.Reset();
 444                 if (err != 0) {
 445                         nvlist_free(rp);
 446                         return (v8plus_nverr(err, "err"));
 447                 }
 448         } else if ((err = nvlist_add_v8_Value(rp, "res", res)) != 0) {
 449                 nvlist_free(rp);
 450                 return (v8plus_nverr(err, "res"));
 451         }
 452 
 453         return (rp);
 454 }
 455 
 456 extern "C" nvlist_t *
 457 v8plus_method_call_direct(void *cop, const char *name, const nvlist_t *lp)
 458 {
 459         v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 460         const int max_argc = nvlist_length(lp);
 461         int argc, err;
 462         v8::Handle<v8::Value> argv[max_argc];
 463         v8::Handle<v8::Value> res;
 464         nvlist_t *rp;
 465 
 466         if (v8plus::ObjectWrap::in_event_thread() != _B_TRUE)
 467                 v8plus_panic("direct method call outside of event loop");
 468 
 469         argc = max_argc;
 470         nvlist_to_v8_argv(lp, &argc, argv);
 471 
 472         if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
 473                 return (v8plus_nverr(err, NULL));
 474 
 475         v8::TryCatch tc;
 476         res = op->call(name, argc, argv);
 477         if (tc.HasCaught()) {
 478                 err = nvlist_add_v8_Value(rp, "err", tc.Exception());
 479                 tc.Reset();
 480                 if (err != 0) {
 481                         nvlist_free(rp);
 482                         return (v8plus_nverr(err, "err"));
 483                 }
 484         } else if ((err = nvlist_add_v8_Value(rp, "res", res)) != 0) {
 485                 nvlist_free(rp);
 486                 return (v8plus_nverr(err, "res"));
 487         }
 488 
 489         return (rp);
 490 }
 491 
 492 extern "C" void
 493 v8plus_async_callback(uv_async_t *async, __attribute__((unused)) int status)
 494 {
 495         if (v8plus::ObjectWrap::in_event_thread() != _B_TRUE)
 496                 v8plus_panic("async callback called outside of event loop");
 497 
 498         for (;;) {
 499                 v8plus_async_call_t *ac;
 500 
 501                 if ((ac = v8plus::ObjectWrap::next_async_call()) == NULL)
 502                         break;
 503 
 504                 if (pthread_mutex_lock(&ac->ac_mtx) != 0)
 505                         v8plus_panic("could not lock async call mutex");
 506 
 507                 if (ac->ac_run == _B_TRUE)
 508                         v8plus_panic("async call already run");
 509 
 510                 ac->ac_return = v8plus_method_call_direct(ac->ac_cop,
 511                     ac->ac_name, ac->ac_lp);
 512                 ac->ac_run = _B_TRUE;
 513 
 514                 if (pthread_cond_broadcast(&ac->ac_cv) != 0)
 515                         v8plus_panic("could not signal async call condvar");
 516                 if (pthread_mutex_unlock(&ac->ac_mtx) != 0)
 517                         v8plus_panic("could not unlock async call mutex");
 518         }
 519 }
 520 
 521 extern "C" nvlist_t *
 522 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
 523 {
 524         v8plus_async_call_t ac;
 525 
 526         if (v8plus::ObjectWrap::in_event_thread() == _B_TRUE) {
 527                 /*
 528                  * We're running in the event loop thread, so we can make the
 529                  * call directly.
 530                  */
 531                 return (v8plus_method_call_direct(cop, name, lp));
 532         }
 533 
 534         /*
 535          * As we cannot manipulate v8plus/V8/Node structures directly from
 536          * outside the event loop thread, we push the call arguments onto a
 537          * queue and post to the event loop thread.  We then sleep on our
 538          * condition variable until the event loop thread makes the call
 539          * for us and wakes us up.
 540          */
 541         ac.ac_cop = cop;
 542         ac.ac_name = name;
 543         ac.ac_lp = lp;
 544         if (pthread_mutex_init(&ac.ac_mtx, NULL) != 0)
 545                 v8plus_panic("could not init async call mutex");
 546         if (pthread_cond_init(&ac.ac_cv, NULL) != 0)
 547                 v8plus_panic("could not init async call condvar");
 548         ac.ac_run = _B_FALSE;
 549         ac.ac_return = NULL;
 550 
 551         v8plus::ObjectWrap::post_async_call(&ac);
 552 
 553         if (pthread_mutex_lock(&ac.ac_mtx) != 0)
 554                 v8plus_panic("could not lock async call mutex");
 555         while (ac.ac_run == _B_FALSE) {
 556                 if (pthread_cond_wait(&ac.ac_cv, &ac.ac_mtx) != 0)
 557                         v8plus_panic("could not wait on async call condvar");
 558         }
 559 
 560         return (ac.ac_return);
 561 }
 562 
 563 extern "C" int
 564 nvlist_lookup_v8plus_jsfunc(const nvlist_t *lp, const char *name,
 565     v8plus_jsfunc_t *vp)
 566 {
 567         uint64_t *lvp;
 568         uint_t nv;
 569         int err;
 570 
 571         err = nvlist_lookup_uint64_array(const_cast<nvlist_t *>(lp),
 572             name, &lvp, &nv);
 573         if (err != 0)
 574                 return (err);
 575 
 576         if (nv != 1)
 577                 v8plus_panic("bad array size %u for callback hash tag", nv);
 578 
 579         *vp = *lvp;
 580         return (0);
 581 }
 582 
 583 extern "C" void
 584 v8plus_jsfunc_hold(v8plus_jsfunc_t f)
 585 {
 586         v8::Persistent<v8::Function> pfh;
 587         std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
 588 
 589         if ((it = cbhash.find(f)) == cbhash.end())
 590                 v8plus_panic("callback hash tag %llu not found", f);
 591 
 592         if (!it->second.ch_persist) {
 593                 pfh = v8::Persistent<v8::Function>::New(it->second.ch_hdl);
 594                 it->second.ch_hdl = pfh;
 595                 it->second.ch_persist = _B_TRUE;
 596         }
 597         ++it->second.ch_refs;
 598 }
 599 
 600 extern "C" void
 601 v8plus_jsfunc_rele(v8plus_jsfunc_t f)
 602 {
 603         v8::Local<v8::Function> lfh;
 604         std::unordered_map<uint64_t, cb_hdl_t>::iterator it;
 605 
 606         if ((it = cbhash.find(f)) == cbhash.end())
 607                 v8plus_panic("callback hash tag %llu not found", f);
 608 
 609         if (it->second.ch_refs == 0)
 610                 v8plus_panic("releasing unheld callback hash tag %llu", f);
 611 
 612         if (--it->second.ch_refs == 0) {
 613                 if (it->second.ch_persist) {
 614                         v8::Persistent<v8::Function> pfh(it->second.ch_hdl);
 615                         pfh.Dispose();
 616                 }
 617                 cbhash.erase(it);
 618         }
 619 }
 620 
 621 static size_t
 622 library_name(const char *base, const char *version, char *buf, size_t len)
 623 {
 624 #ifdef __MACH__
 625         return (snprintf(buf, len, "lib%s.%s%sdylib", base,
 626             version ? version : "", version ? "." : ""));
 627 #else
 628         return (snprintf(buf, len, "lib%s.so%s%s", base,
 629             version ? "." : "", version ? version : ""));
 630 #endif
 631 }
 632 
 633 /*
 634  * This is really gross: we need to free up JS function slots when then list
 635  * is freed, but there's no way for us to know that's happening.  So we
 636  * interpose on nvlist_free() here, checking for function slots to free iff
 637  * this is a list that has a V8 JS function handle in it.  Lists created by
 638  * someone else, even if they have uint64 arrays in them, are passed through.
 639  * This whole thing makes me want to cry.  Why can't we just have a decent
 640  * JS VM?!
 641  */
 642 extern "C" void
 643 nvlist_free(nvlist_t *lp)
 644 {
 645         uint64_t *vp;
 646         uint_t nv;
 647         nvpair_t *pp = NULL;
 648 
 649         if (lp == NULL)
 650                 return;
 651 
 652         if (__real_nvlist_free == NULL) {
 653                 char *libname;
 654                 size_t len;
 655                 void *dlhdl;
 656 
 657                 len = library_name("nvpair", "1", NULL, 0) + 1;
 658                 libname = reinterpret_cast<char *>(alloca(len));
 659                 (void) library_name("nvpair", "1", libname, len);
 660 
 661                 dlhdl = dlopen(libname, RTLD_LAZY | RTLD_LOCAL);
 662                 if (dlhdl == NULL) {
 663                         v8plus_panic("unable to dlopen libnvpair: %s",
 664                             dlerror());
 665                 }
 666                 __real_nvlist_free = (void (*)(nvlist_t *))
 667                     dlsym(dlhdl, "nvlist_free");
 668                 if (__real_nvlist_free == NULL)
 669                         v8plus_panic("unable to find nvlist_free");
 670         }
 671 
 672         if (nvlist_exists(lp, V8PLUS_JSF_COOKIE)) {
 673                 while ((pp = nvlist_next_nvpair(lp, pp)) != NULL) {
 674                         if (nvpair_type(pp) != DATA_TYPE_UINT64_ARRAY)
 675                                 continue;
 676                         if (nvpair_value_uint64_array(pp, &vp, &nv) != 0) {
 677                                 v8plus_panic(
 678                                     "unable to obtain callbach hash tag");
 679                         }
 680                         if (nv != 1) {
 681                                 v8plus_panic(
 682                                     "bad array size %u for callback hash tag",
 683                                     nv);
 684                         }
 685                         v8plus_jsfunc_rele(*vp);
 686                 }
 687         }
 688 
 689         __real_nvlist_free(lp);
 690 }
 691 
 692 extern "C" int
 693 nvpair_value_v8plus_jsfunc(const nvpair_t *pp, v8plus_jsfunc_t *vp)
 694 {
 695         uint64_t *lvp;
 696         uint_t nv;
 697         int err;
 698 
 699         if ((err = nvpair_value_uint64_array((nvpair_t *)pp, &lvp, &nv)) != 0)
 700                 return (err);
 701 
 702         *vp = *lvp;
 703 
 704         return (0);
 705 }
 706 
 707 extern "C" void
 708 v8plus_obj_hold(const void *cop)
 709 {
 710         v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 711         op->public_Ref();
 712 }
 713 
 714 extern "C" void
 715 v8plus_obj_rele(const void *cop)
 716 {
 717         v8plus::ObjectWrap *op = v8plus::ObjectWrap::objlookup(cop);
 718         op->public_Unref();
 719 }