1 /*
   2  * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
   3  */
   4 
   5 #include <sys/ccompile.h>
   6 #include <sys/debug.h>
   7 #include <sys/queue.h>
   8 #include <sys/types.h>
   9 #include <stdarg.h>
  10 #include <string.h>
  11 #include <strings.h>
  12 #include <errno.h>
  13 #include <uv.h>
  14 #include <pthread.h>
  15 #include "v8plus_glue.h"
  16 
  17 __thread v8plus_errno_t _v8plus_errno;
  18 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
  19 
  20 typedef struct v8plus_uv_ctx {
  21         void *vuc_obj;
  22         void *vuc_ctx;
  23         void *vuc_result;
  24         v8plus_worker_f vuc_worker;
  25         v8plus_completion_f vuc_completion;
  26 } v8plus_uv_ctx_t;
  27 
  28 static STAILQ_HEAD(v8plus_callq_head, v8plus_async_call) _v8plus_callq =
  29     STAILQ_HEAD_INITIALIZER(_v8plus_callq);
  30 static pthread_mutex_t _v8plus_callq_mtx;
  31 static pthread_t _v8plus_uv_event_thread;
  32 static uv_async_t _v8plus_uv_async;
  33 
  34 typedef enum v8plus_async_call_type {
  35         ACT_OBJECT_CALL = 1,
  36         ACT_OBJECT_RELEASE,
  37         ACT_JSFUNC_CALL,
  38         ACT_JSFUNC_RELEASE,
  39 } v8plus_async_call_type_t;
  40 
  41 typedef struct v8plus_async_call {
  42         v8plus_async_call_type_t vac_type;
  43         boolean_t vac_noreply;
  44 
  45         /*
  46          * For ACT_OBJECT_{CALL,RELEASE}:
  47          */
  48         void *vac_cop;
  49         const char *vac_name;
  50         /*
  51          * For ACT_JSFUNC_{CALL,RELEASE}:
  52          */
  53         v8plus_jsfunc_t vac_func;
  54 
  55         /*
  56          * Common call arguments:
  57          */
  58         const nvlist_t *vac_lp;
  59 
  60         pthread_cond_t vac_cv;
  61         pthread_mutex_t vac_mtx;
  62 
  63         boolean_t vac_run;
  64         nvlist_t *vac_return;
  65 
  66         STAILQ_ENTRY(v8plus_async_call) vac_callq_entry;
  67 } v8plus_async_call_t;
  68 
  69 boolean_t
  70 v8plus_in_event_thread(void)
  71 {
  72         return (_v8plus_uv_event_thread == pthread_self() ? B_TRUE : B_FALSE);
  73 }
  74 
  75 static void
  76 v8plus_async_callback(uv_async_t *async __UNUSED, int status __UNUSED)
  77 {
  78         if (v8plus_in_event_thread() != B_TRUE)
  79                 v8plus_panic("async callback called outside of event loop");
  80 
  81         for (;;) {
  82                 v8plus_async_call_t *vac = NULL;
  83 
  84                 /*
  85                  * Fetch the next queued method:
  86                  */
  87                 if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
  88                         v8plus_panic("could not lock async queue mutex");
  89                 if (!STAILQ_EMPTY(&_v8plus_callq)) {
  90                         vac = STAILQ_FIRST(&_v8plus_callq);
  91                         STAILQ_REMOVE_HEAD(&_v8plus_callq, vac_callq_entry);
  92                 }
  93                 if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
  94                         v8plus_panic("could not unlock async queue mutex");
  95 
  96                 if (vac == NULL)
  97                         break;
  98 
  99                 /*
 100                  * Run the queued method:
 101                  */
 102                 if (vac->vac_run == B_TRUE)
 103                         v8plus_panic("async call already run");
 104 
 105                 switch (vac->vac_type) {
 106                 case ACT_OBJECT_CALL:
 107                         vac->vac_return = v8plus_method_call_direct(
 108                             vac->vac_cop, vac->vac_name, vac->vac_lp);
 109                         break;
 110                 case ACT_OBJECT_RELEASE:
 111                         v8plus_obj_rele(vac->vac_cop);
 112                         break;
 113                 case ACT_JSFUNC_CALL:
 114                         vac->vac_return = v8plus_call_direct(
 115                             vac->vac_func, vac->vac_lp);
 116                         break;
 117                 case ACT_JSFUNC_RELEASE:
 118                         v8plus_jsfunc_rele(vac->vac_func);
 119                         break;
 120                 }
 121 
 122                 if (vac->vac_noreply == B_TRUE) {
 123                         /*
 124                          * The caller posted this event and is not sleeping
 125                          * on a reply.  Just free the call structure and move
 126                          * on.
 127                          */
 128                         free(vac);
 129                         if (vac->vac_lp != NULL)
 130                                 nvlist_free((nvlist_t *)vac->vac_lp);
 131                         continue;
 132                 }
 133 
 134                 if (pthread_mutex_lock(&vac->vac_mtx) != 0)
 135                         v8plus_panic("could not lock async call mutex");
 136                 vac->vac_run = B_TRUE;
 137                 if (pthread_cond_broadcast(&vac->vac_cv) != 0)
 138                         v8plus_panic("could not signal async call condvar");
 139                 if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 140                         v8plus_panic("could not unlock async call mutex");
 141         }
 142 }
 143 
 144 /*
 145  * As we cannot manipulate v8plus/V8/Node structures directly from outside the
 146  * event loop thread, we push the call arguments onto a queue and post to the
 147  * event loop thread.  We then sleep on our condition variable until the event
 148  * loop thread makes the call for us and wakes us up.
 149  *
 150  * This routine implements the parts of this interaction common to all
 151  * variants.
 152  */
 153 static nvlist_t *
 154 v8plus_cross_thread_call(v8plus_async_call_t *vac)
 155 {
 156         /*
 157          * Common call structure initialisation:
 158          */
 159         if (pthread_mutex_init(&vac->vac_mtx, NULL) != 0)
 160                 v8plus_panic("could not init async call mutex");
 161         if (pthread_cond_init(&vac->vac_cv, NULL) != 0)
 162                 v8plus_panic("could not init async call condvar");
 163         vac->vac_run = B_FALSE;
 164 
 165         /*
 166          * Post request to queue:
 167          */
 168         if (pthread_mutex_lock(&_v8plus_callq_mtx) != 0)
 169                 v8plus_panic("could not lock async queue mutex");
 170         STAILQ_INSERT_TAIL(&_v8plus_callq, vac, vac_callq_entry);
 171         if (pthread_mutex_unlock(&_v8plus_callq_mtx) != 0)
 172                 v8plus_panic("could not unlock async queue mutex");
 173         uv_async_send(&_v8plus_uv_async);
 174 
 175         if (vac->vac_noreply == B_TRUE) {
 176                 /*
 177                  * The caller does not care about the reply, and has allocated
 178                  * the v8plus_async_call_t structure from the heap.  The
 179                  * async callback will free the storage when it completes.
 180                  */
 181                 return (NULL);
 182         }
 183 
 184         /*
 185          * Wait for our request to be serviced on the event loop thread:
 186          */
 187         if (pthread_mutex_lock(&vac->vac_mtx) != 0)
 188                 v8plus_panic("could not lock async call mutex");
 189         while (vac->vac_run == B_FALSE) {
 190                 if (pthread_cond_wait(&vac->vac_cv, &vac->vac_mtx) != 0)
 191                         v8plus_panic("could not wait on async call condvar");
 192         }
 193         if (pthread_mutex_unlock(&vac->vac_mtx) != 0)
 194                 v8plus_panic("could not unlock async call mutex");
 195 
 196         if (pthread_cond_destroy(&vac->vac_cv) != 0)
 197                 v8plus_panic("could not destroy async call condvar");
 198         if (pthread_mutex_destroy(&vac->vac_mtx) != 0)
 199                 v8plus_panic("could not destroy async call mutex");
 200 
 201         return (vac->vac_return);
 202 }
 203 
 204 nvlist_t *
 205 v8plus_method_call(void *cop, const char *name, const nvlist_t *lp)
 206 {
 207         v8plus_async_call_t vac;
 208 
 209         if (v8plus_in_event_thread() == B_TRUE) {
 210                 /*
 211                  * We're running in the event loop thread, so we can make the
 212                  * call directly.
 213                  */
 214                 return (v8plus_method_call_direct(cop, name, lp));
 215         }
 216 
 217         bzero(&vac, sizeof (vac));
 218         vac.vac_type = ACT_OBJECT_CALL;
 219         vac.vac_noreply = B_FALSE;
 220         vac.vac_cop = cop;
 221         vac.vac_name = name;
 222         vac.vac_lp = lp;
 223 
 224         return (v8plus_cross_thread_call(&vac));
 225 }
 226 
 227 nvlist_t *
 228 v8plus_call(v8plus_jsfunc_t func, const nvlist_t *lp)
 229 {
 230         v8plus_async_call_t vac;
 231 
 232         if (v8plus_in_event_thread() == B_TRUE) {
 233                 /*
 234                  * We're running in the event loop thread, so we can make the
 235                  * call directly.
 236                  */
 237                 return (v8plus_call_direct(func, lp));
 238         }
 239 
 240         bzero(&vac, sizeof (vac));
 241         vac.vac_type = ACT_JSFUNC_CALL;
 242         vac.vac_noreply = B_FALSE;
 243         vac.vac_func = func;
 244         vac.vac_lp = lp;
 245 
 246         return (v8plus_cross_thread_call(&vac));
 247 }
 248 
 249 void
 250 v8plus_obj_rele(const void *cop)
 251 {
 252         v8plus_async_call_t *vac;
 253 
 254         if (v8plus_in_event_thread() == B_TRUE) {
 255                 return (v8plus_obj_rele_direct(cop));
 256         }
 257 
 258         vac = calloc(1, sizeof (*vac));
 259         if (vac == NULL)
 260                 v8plus_panic("could not allocate async call structure");
 261 
 262         vac->vac_type = ACT_OBJECT_RELEASE;
 263         vac->vac_noreply = B_TRUE;
 264         vac->vac_cop = (void *)cop;
 265 
 266         (void) v8plus_cross_thread_call(vac);
 267 }
 268 
 269 void
 270 v8plus_jsfunc_rele(v8plus_jsfunc_t f)
 271 {
 272         v8plus_async_call_t *vac;
 273 
 274         if (v8plus_in_event_thread() == B_TRUE) {
 275                 return (v8plus_jsfunc_rele_direct(f));
 276         }
 277 
 278         vac = calloc(1, sizeof (*vac));
 279         if (vac == NULL)
 280                 v8plus_panic("could not allocate async call structure");
 281 
 282         vac->vac_type = ACT_JSFUNC_RELEASE;
 283         vac->vac_noreply = B_TRUE;
 284         vac->vac_func = f;
 285 
 286         (void) v8plus_cross_thread_call(vac);
 287 }
 288 
 289 /*
 290  * Initialise structures for off-event-loop method calls.
 291  *
 292  * Note that uv_async_init() must be called inside the libuv event loop, so we
 293  * do it here.  We also want to record the thread ID of the Event Loop thread
 294  * so as to determine what kind of method calls to make later.
 295  */
 296 void
 297 v8plus_crossthread_init(void)
 298 {
 299         _v8plus_uv_event_thread = pthread_self();
 300         if (uv_async_init(uv_default_loop(), &_v8plus_uv_async,
 301             v8plus_async_callback) != 0)
 302                 v8plus_panic("unable to initialise uv_async_t");
 303         if (pthread_mutex_init(&_v8plus_callq_mtx, NULL) != 0)
 304                 v8plus_panic("unable to initialise mutex");
 305 }
 306 
 307 nvlist_t *
 308 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
 309 {
 310         if (fmt == NULL) {
 311                 if (e == V8PLUSERR_NOERROR) {
 312                         *_v8plus_errmsg = '\0';
 313                 } else {
 314                         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
 315                             "%s", v8plus_strerror(e));
 316                 }
 317         } else {
 318                 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
 319         }
 320         _v8plus_errno = e;
 321 
 322         return (NULL);
 323 }
 324 
 325 nvlist_t *
 326 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
 327 {
 328         va_list ap;
 329 
 330         va_start(ap, fmt);
 331         (void) v8plus_verror(e, fmt, ap);
 332         va_end(ap);
 333 
 334         return (NULL);
 335 }
 336 
 337 static void __NORETURN
 338 v8plus_vpanic(const char *fmt, va_list ap)
 339 {
 340         (void) vfprintf(stderr, fmt, ap);
 341         (void) fflush(stderr);
 342         abort();
 343 }
 344 
 345 void
 346 v8plus_panic(const char *fmt, ...)
 347 {
 348         va_list ap;
 349 
 350         va_start(ap, fmt);
 351         v8plus_vpanic(fmt, ap);
 352         va_end(ap);
 353 }
 354 
 355 nvlist_t *
 356 v8plus_nverr(int nverr, const char *member)
 357 {
 358         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
 359             "nvlist manipulation error on member %s: %s",
 360             member == NULL ? "<none>" : member, strerror(nverr));
 361 
 362         switch (nverr) {
 363         case ENOMEM:
 364                 _v8plus_errno = V8PLUSERR_NOMEM;
 365                 break;
 366         case EINVAL:
 367                 _v8plus_errno = V8PLUSERR_YOUSUCK;
 368                 break;
 369         default:
 370                 _v8plus_errno = V8PLUSERR_UNKNOWN;
 371                 break;
 372         }
 373 
 374         return (NULL);
 375 }
 376 
 377 nvlist_t *
 378 v8plus_syserr(int syserr, const char *fmt, ...)
 379 {
 380         v8plus_errno_t e;
 381         va_list ap;
 382 
 383         switch (syserr) {
 384         case ENOMEM:
 385                 e = V8PLUSERR_NOMEM;
 386                 break;
 387         case EBADF:
 388                 e = V8PLUSERR_BADF;
 389                 break;
 390         default:
 391                 e = V8PLUSERR_UNKNOWN;
 392                 break;
 393         }
 394 
 395         va_start(ap, fmt);
 396         (void) v8plus_verror(e, fmt, ap);
 397         va_end(ap);
 398 
 399         return (NULL);
 400 }
 401 
 402 /*
 403  * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
 404  */
 405 nvlist_t *
 406 v8plus_void(void)
 407 {
 408         return (v8plus_error(V8PLUSERR_NOERROR, NULL));
 409 }
 410 
 411 v8plus_type_t
 412 v8plus_typeof(const nvpair_t *pp)
 413 {
 414         data_type_t t = nvpair_type((nvpair_t *)pp);
 415 
 416         switch (t) {
 417         case DATA_TYPE_DOUBLE:
 418                 return (V8PLUS_TYPE_NUMBER);
 419         case DATA_TYPE_STRING:
 420                 return (V8PLUS_TYPE_STRING);
 421         case DATA_TYPE_NVLIST:
 422                 return (V8PLUS_TYPE_OBJECT);
 423         case DATA_TYPE_BOOLEAN_VALUE:
 424                 return (V8PLUS_TYPE_BOOLEAN);
 425         case DATA_TYPE_BOOLEAN:
 426                 return (V8PLUS_TYPE_UNDEFINED);
 427         case DATA_TYPE_BYTE:
 428         {
 429                 uchar_t v;
 430                 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
 431                         return (V8PLUS_TYPE_INVALID);
 432                 return (V8PLUS_TYPE_NULL);
 433         }
 434         case DATA_TYPE_UINT64_ARRAY:
 435         {
 436                 uint64_t *vp;
 437                 uint_t nv;
 438                 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
 439                     nv != 1) {
 440                         return (V8PLUS_TYPE_INVALID);
 441                 }
 442                 return (V8PLUS_TYPE_JSFUNC);
 443         }
 444         default:
 445                 return (V8PLUS_TYPE_INVALID);
 446         }
 447 }
 448 
 449 static int
 450 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
 451 {
 452         data_type_t dt = nvpair_type((nvpair_t *)pp);
 453 
 454         switch (t) {
 455         case V8PLUS_TYPE_NONE:
 456                 return (-1);
 457         case V8PLUS_TYPE_STRING:
 458                 if (dt == DATA_TYPE_STRING) {
 459                         if (vp != NULL) {
 460                                 (void) nvpair_value_string((nvpair_t *)pp,
 461                                     (char **)vp);
 462                         }
 463                         return (0);
 464                 }
 465                 return (-1);
 466         case V8PLUS_TYPE_NUMBER:
 467                 if (dt == DATA_TYPE_DOUBLE) {
 468                         if (vp != NULL) {
 469                                 (void) nvpair_value_double((nvpair_t *)pp,
 470                                     (double *)vp);
 471                         }
 472                         return (0);
 473                 }
 474                 return (-1);
 475         case V8PLUS_TYPE_BOOLEAN:
 476                 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
 477                         if (vp != NULL) {
 478                                 (void) nvpair_value_boolean_value(
 479                                     (nvpair_t *)pp, (boolean_t *)vp);
 480                         }
 481                         return (0);
 482                 }
 483                 return (-1);
 484         case V8PLUS_TYPE_JSFUNC:
 485                 if (dt == DATA_TYPE_UINT64_ARRAY) {
 486                         uint_t nv;
 487                         uint64_t *vpp;
 488 
 489                         if (nvpair_value_uint64_array((nvpair_t *)pp,
 490                             &vpp, &nv) == 0 && nv == 1) {
 491                                 if (vp != NULL)
 492                                         *(v8plus_jsfunc_t *)vp = vpp[0];
 493                                 return (0);
 494                         }
 495                 }
 496                 return (-1);
 497         case V8PLUS_TYPE_OBJECT:
 498                 if (dt == DATA_TYPE_NVLIST) {
 499                         if (vp != NULL) {
 500                                 (void) nvpair_value_nvlist((nvpair_t *)pp,
 501                                     (nvlist_t **)vp);
 502                         }
 503                         return (0);
 504                 }
 505                 return (-1);
 506         case V8PLUS_TYPE_NULL:
 507                 if (dt == DATA_TYPE_BYTE) {
 508                         uchar_t v;
 509 
 510                         if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
 511                             v == 0)
 512                                 return (0);
 513                 }
 514                 return (-1);
 515         case V8PLUS_TYPE_UNDEFINED:
 516                 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
 517         case V8PLUS_TYPE_ANY:
 518                 if (vp != NULL)
 519                         *(const nvpair_t **)vp = pp;
 520                 return (0);
 521         case V8PLUS_TYPE_INVALID:
 522                 if (vp != NULL)
 523                         *(data_type_t *)vp = dt;
 524                 return (0);
 525         case V8PLUS_TYPE_STRNUMBER64:
 526                 if (dt == DATA_TYPE_STRING) {
 527                         char *s;
 528                         uint64_t v;
 529 
 530                         (void) nvpair_value_string((nvpair_t *)pp, &s);
 531                         errno = 0;
 532                         v = (uint64_t)strtoull(s, NULL, 0);
 533                         if (errno != 0)
 534                                 return (-1);
 535                         if (vp != NULL)
 536                                 *(uint64_t *)vp = v;
 537                         return (0);
 538                 }
 539                 return (-1);
 540         default:
 541                 return (-1);
 542         }
 543 }
 544 
 545 int
 546 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
 547 {
 548         v8plus_type_t nt;
 549         nvpair_t *pp;
 550         void *vp;
 551         va_list ap;
 552         uint_t i;
 553         char buf[32];
 554 
 555         va_start(ap, t);
 556 
 557         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 558                 switch (nt) {
 559                 case V8PLUS_TYPE_UNDEFINED:
 560                 case V8PLUS_TYPE_NULL:
 561                         break;
 562                 default:
 563                         (void) va_arg(ap, void *);
 564                 }
 565 
 566                 (void) snprintf(buf, sizeof (buf), "%u", i);
 567                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
 568                         (void) v8plus_error(V8PLUSERR_MISSINGARG,
 569                             "argument %u is required", i);
 570                         return (-1);
 571                 }
 572 
 573                 if (v8plus_arg_value(nt, pp, NULL) != 0) {
 574                         (void) v8plus_error(V8PLUSERR_BADARG,
 575                             "argument %u is of incorrect type", i);
 576                         return (-1);
 577                 }
 578 
 579                 nt = va_arg(ap, data_type_t);
 580         }
 581 
 582         va_end(ap);
 583 
 584         if (flags & V8PLUS_ARG_F_NOEXTRA) {
 585                 (void) snprintf(buf, sizeof (buf), "%u", i);
 586                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
 587                         (void) v8plus_error(V8PLUSERR_EXTRAARG,
 588                             "superfluous extra argument(s) detected");
 589                         return (-1);
 590                 }
 591         }
 592 
 593         va_start(ap, t);
 594 
 595         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 596                 switch (nt) {
 597                 case V8PLUS_TYPE_UNDEFINED:
 598                 case V8PLUS_TYPE_NULL:
 599                         vp = NULL;
 600                         break;
 601                 default:
 602                         vp = va_arg(ap, void *);
 603                 }
 604 
 605                 (void) snprintf(buf, sizeof (buf), "%u", i);
 606                 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
 607                 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
 608 
 609                 nt = va_arg(ap, data_type_t);
 610         }
 611 
 612         va_end(ap);
 613 
 614         return (0);
 615 }
 616 
 617 static int
 618 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
 619 {
 620         v8plus_type_t nt = t;
 621         char *name;
 622         int err;
 623 
 624         /*
 625          * Do not call va_start() or va_end() in this function!  We are limited
 626          * to a single traversal of the arguments so that we can recurse to
 627          * handle embedded object definitions.
 628          */
 629 
 630         while (nt != V8PLUS_TYPE_NONE) {
 631                 name = va_arg(*ap, char *);
 632 
 633                 switch (nt) {
 634                 case V8PLUS_TYPE_STRING:
 635                 {
 636                         char *s = va_arg(*ap, char *);
 637                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 638                                 (void) v8plus_nverr(err, name);
 639                                 return (-1);
 640                         }
 641                         break;
 642                 }
 643                 case V8PLUS_TYPE_NUMBER:
 644                 {
 645                         double d = va_arg(*ap, double);
 646                         if ((err = nvlist_add_double(lp, name, d)) != 0) {
 647                                 (void) v8plus_nverr(err, name);
 648                                 return (-1);
 649                         }
 650                         break;
 651                 }
 652                 case V8PLUS_TYPE_BOOLEAN:
 653                 {
 654                         boolean_t b = va_arg(*ap, boolean_t);
 655                         if ((err = nvlist_add_boolean_value(lp,
 656                             name, b)) != 0) {
 657                                 (void) v8plus_nverr(err, name);
 658                                 return (-1);
 659                         }
 660                         break;
 661                 }
 662                 case V8PLUS_TYPE_JSFUNC:
 663                 {
 664                         v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
 665                         if ((err = nvlist_add_uint64_array(lp,
 666                             name, &j, 1)) != 0) {
 667                                 (void) v8plus_nverr(err, name);
 668                                 return (-1);
 669                         }
 670                         if ((err = nvlist_add_string_array(lp,
 671                             V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
 672                                 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
 673                                 return (-1);
 674                         }
 675                         v8plus_jsfunc_hold(j);
 676                         break;
 677                 }
 678                 case V8PLUS_TYPE_OBJECT:
 679                 {
 680                         const nvlist_t *op = va_arg(*ap, const nvlist_t *);
 681                         if ((err = nvlist_add_nvlist(lp, name,
 682                             (nvlist_t *)op)) != 0) {
 683                                 (void) v8plus_nverr(err, name);
 684                                 return (-1);
 685                         }
 686                         break;
 687                 }
 688                 case V8PLUS_TYPE_NULL:
 689                         if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
 690                                 (void) v8plus_nverr(err, name);
 691                                 return (-1);
 692                         }
 693                         break;
 694                 case V8PLUS_TYPE_UNDEFINED:
 695                         if ((err = nvlist_add_boolean(lp, name)) != 0) {
 696                                 (void) v8plus_nverr(err, name);
 697                                 return (-1);
 698                         }
 699                         break;
 700                 case V8PLUS_TYPE_ANY:
 701                 {
 702                         nvpair_t *pp = va_arg(*ap, nvpair_t *);
 703                         if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
 704                                 (void) v8plus_nverr(err, name);
 705                                 return (-1);
 706                         }
 707                         break;
 708                 }
 709                 case V8PLUS_TYPE_STRNUMBER64:
 710                 {
 711                         uint64_t v = va_arg(*ap, uint64_t);
 712                         char s[32];
 713                         (void) snprintf(s, sizeof (s), "%" PRIu64, v);
 714                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 715                                 (void) v8plus_nverr(err, name);
 716                                 return (-1);
 717                         }
 718                         break;
 719                 }
 720                 case V8PLUS_TYPE_INL_OBJECT:
 721                 {
 722                         nvlist_t *slp;
 723 
 724                         nt = va_arg(*ap, v8plus_type_t);
 725                         err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
 726                         if (err != 0) {
 727                                 (void) v8plus_nverr(err, name);
 728                                 return (-1);
 729                         }
 730                         if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
 731                                 return (-1);
 732 
 733                         err = nvlist_add_nvlist(lp, name, slp);
 734                         nvlist_free(slp);
 735                         if (err != 0) {
 736                                 (void) v8plus_nverr(err, name);
 737                                 return (-1);
 738                         }
 739                         break;
 740                 }
 741                 case V8PLUS_TYPE_INVALID:
 742                 default:
 743                         (void) v8plus_error(V8PLUSERR_YOUSUCK,
 744                             "invalid property type %d", nt);
 745                         return (-1);
 746                 }
 747 
 748                 nt = va_arg(*ap, v8plus_type_t);
 749         }
 750 
 751         return (0);
 752 }
 753 
 754 nvlist_t *
 755 v8plus_obj(v8plus_type_t t, ...)
 756 {
 757         nvlist_t *rp;
 758         va_list ap;
 759         int err;
 760 
 761         if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
 762                 return (v8plus_nverr(err, NULL));
 763 
 764         va_start(ap, t);
 765         err = v8plus_obj_vsetprops(rp, t, &ap);
 766         va_end(ap);
 767 
 768         if (err != 0) {
 769                 nvlist_free(rp);
 770                 rp = NULL;
 771         }
 772 
 773         return (rp);
 774 }
 775 
 776 int
 777 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
 778 {
 779         va_list ap;
 780         int err;
 781 
 782         va_start(ap, t);
 783         err = v8plus_obj_vsetprops(lp, t, &ap);
 784         va_end(ap);
 785 
 786         return (err);
 787 }
 788 
 789 static void
 790 v8plus_uv_worker(uv_work_t *wp)
 791 {
 792         v8plus_uv_ctx_t *cp = wp->data;
 793 
 794         cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
 795 }
 796 
 797 static void
 798 v8plus_uv_completion(uv_work_t *wp)
 799 {
 800         v8plus_uv_ctx_t *cp = wp->data;
 801 
 802         cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
 803         v8plus_obj_rele(cp->vuc_obj);
 804         free(cp);
 805         free(wp);
 806 }
 807 
 808 void
 809 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
 810     v8plus_completion_f completion)
 811 {
 812         uv_work_t *wp = malloc(sizeof (uv_work_t));
 813         v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
 814 
 815         bzero(wp, sizeof (uv_work_t));
 816         bzero(cp, sizeof (v8plus_uv_ctx_t));
 817 
 818         v8plus_obj_hold(cop);
 819         cp->vuc_obj = cop;
 820         cp->vuc_ctx = ctxp;
 821         cp->vuc_worker = worker;
 822         cp->vuc_completion = completion;
 823         wp->data = cp;
 824 
 825         uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
 826             v8plus_uv_completion);
 827 }