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