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