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