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