1 /*
   2  * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
   3  */
   4 
   5 #include <sys/ccompile.h>
   6 #include <sys/debug.h>
   7 #include <stdarg.h>
   8 #include <string.h>
   9 #include <strings.h>
  10 #include <errno.h>
  11 #include <uv.h>
  12 #include "v8plus_glue.h"
  13 
  14 __thread v8plus_errno_t _v8plus_errno;
  15 __thread char _v8plus_errmsg[V8PLUS_ERRMSG_LEN];
  16 
  17 typedef struct v8plus_uv_ctx {
  18         void *vuc_obj;
  19         void *vuc_ctx;
  20         void *vuc_result;
  21         v8plus_worker_f vuc_worker;
  22         v8plus_completion_f vuc_completion;
  23 } v8plus_uv_ctx_t;
  24 
  25 nvlist_t *
  26 v8plus_verror(v8plus_errno_t e, const char *fmt, va_list ap)
  27 {
  28         if (fmt == NULL) {
  29                 if (e == V8PLUSERR_NOERROR) {
  30                         *_v8plus_errmsg = '\0';
  31                 } else {
  32                         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
  33                             "%s", v8plus_strerror(e));
  34                 }
  35         } else {
  36                 (void) vsnprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN, fmt, ap);
  37         }
  38         _v8plus_errno = e;
  39 
  40         return (NULL);
  41 }
  42 
  43 nvlist_t *
  44 v8plus_error(v8plus_errno_t e, const char *fmt, ...)
  45 {
  46         va_list ap;
  47 
  48         va_start(ap, fmt);
  49         (void) v8plus_verror(e, fmt, ap);
  50         va_end(ap);
  51 
  52         return (NULL);
  53 }
  54 
  55 static void __NORETURN
  56 v8plus_vpanic(const char *fmt, va_list ap)
  57 {
  58         (void) vfprintf(stderr, fmt, ap);
  59         (void) fflush(stderr);
  60         abort();
  61 }
  62 
  63 void
  64 v8plus_panic(const char *fmt, ...)
  65 {
  66         va_list ap;
  67 
  68         va_start(ap, fmt);
  69         v8plus_vpanic(fmt, ap);
  70         va_end(ap);
  71 }
  72 
  73 nvlist_t *
  74 v8plus_nverr(int nverr, const char *member)
  75 {
  76         (void) snprintf(_v8plus_errmsg, V8PLUS_ERRMSG_LEN,
  77             "nvlist manipulation error on member %s: %s",
  78             member == NULL ? "<none>" : member, strerror(nverr));
  79 
  80         switch (nverr) {
  81         case ENOMEM:
  82                 _v8plus_errno = V8PLUSERR_NOMEM;
  83                 break;
  84         case EINVAL:
  85                 _v8plus_errno = V8PLUSERR_YOUSUCK;
  86                 break;
  87         default:
  88                 _v8plus_errno = V8PLUSERR_UNKNOWN;
  89                 break;
  90         }
  91 
  92         return (NULL);
  93 }
  94 
  95 nvlist_t *
  96 v8plus_syserr(int syserr, const char *fmt, ...)
  97 {
  98         v8plus_errno_t e;
  99         va_list ap;
 100 
 101         switch (syserr) {
 102         case ENOMEM:
 103                 e = V8PLUSERR_NOMEM;
 104                 break;
 105         case EBADF:
 106                 e = V8PLUSERR_BADF;
 107                 break;
 108         default:
 109                 e = V8PLUSERR_UNKNOWN;
 110                 break;
 111         }
 112 
 113         va_start(ap, fmt);
 114         (void) v8plus_verror(e, fmt, ap);
 115         va_end(ap);
 116 
 117         return (NULL);
 118 }
 119 
 120 /*
 121  * The NULL nvlist with V8PLUSERR_NOERROR means we are returning void.
 122  */
 123 nvlist_t *
 124 v8plus_void(void)
 125 {
 126         return (v8plus_error(V8PLUSERR_NOERROR, NULL));
 127 }
 128 
 129 v8plus_type_t
 130 v8plus_typeof(const nvpair_t *pp)
 131 {
 132         data_type_t t = nvpair_type((nvpair_t *)pp);
 133 
 134         switch (t) {
 135         case DATA_TYPE_DOUBLE:
 136                 return (V8PLUS_TYPE_NUMBER);
 137         case DATA_TYPE_STRING:
 138                 return (V8PLUS_TYPE_STRING);
 139         case DATA_TYPE_NVLIST:
 140                 return (V8PLUS_TYPE_OBJECT);
 141         case DATA_TYPE_BOOLEAN_VALUE:
 142                 return (V8PLUS_TYPE_BOOLEAN);
 143         case DATA_TYPE_BOOLEAN:
 144                 return (V8PLUS_TYPE_UNDEFINED);
 145         case DATA_TYPE_BYTE:
 146         {
 147                 uchar_t v;
 148                 if (nvpair_value_byte((nvpair_t *)pp, &v) != 0 || v != 0)
 149                         return (V8PLUS_TYPE_INVALID);
 150                 return (V8PLUS_TYPE_NULL);
 151         }
 152         case DATA_TYPE_UINT64_ARRAY:
 153         {
 154                 uint64_t *vp;
 155                 uint_t nv;
 156                 if (nvpair_value_uint64_array((nvpair_t *)pp, &vp, &nv) != 0 ||
 157                     nv != 1) {
 158                         return (V8PLUS_TYPE_INVALID);
 159                 }
 160                 return (V8PLUS_TYPE_JSFUNC);
 161         }
 162         default:
 163                 return (V8PLUS_TYPE_INVALID);
 164         }
 165 }
 166 
 167 static int
 168 v8plus_arg_value(v8plus_type_t t, const nvpair_t *pp, void *vp)
 169 {
 170         data_type_t dt = nvpair_type((nvpair_t *)pp);
 171 
 172         switch (t) {
 173         case V8PLUS_TYPE_NONE:
 174                 return (-1);
 175         case V8PLUS_TYPE_STRING:
 176                 if (dt == DATA_TYPE_STRING) {
 177                         if (vp != NULL) {
 178                                 (void) nvpair_value_string((nvpair_t *)pp,
 179                                     (char **)vp);
 180                         }
 181                         return (0);
 182                 }
 183                 return (-1);
 184         case V8PLUS_TYPE_NUMBER:
 185                 if (dt == DATA_TYPE_DOUBLE) {
 186                         if (vp != NULL) {
 187                                 (void) nvpair_value_double((nvpair_t *)pp,
 188                                     (double *)vp);
 189                         }
 190                         return (0);
 191                 }
 192                 return (-1);
 193         case V8PLUS_TYPE_BOOLEAN:
 194                 if (dt == DATA_TYPE_BOOLEAN_VALUE) {
 195                         if (vp != NULL) {
 196                                 (void) nvpair_value_boolean_value(
 197                                     (nvpair_t *)pp, (boolean_t *)vp);
 198                         }
 199                         return (0);
 200                 }
 201                 return (-1);
 202         case V8PLUS_TYPE_JSFUNC:
 203                 if (dt == DATA_TYPE_UINT64_ARRAY) {
 204                         uint_t nv;
 205                         uint64_t *vpp;
 206 
 207                         if (nvpair_value_uint64_array((nvpair_t *)pp,
 208                             &vpp, &nv) == 0 && nv == 1) {
 209                                 if (vp != NULL)
 210                                         *(v8plus_jsfunc_t *)vp = vpp[0];
 211                                 return (0);
 212                         }
 213                 }
 214                 return (-1);
 215         case V8PLUS_TYPE_OBJECT:
 216                 if (dt == DATA_TYPE_NVLIST) {
 217                         if (vp != NULL) {
 218                                 (void) nvpair_value_nvlist((nvpair_t *)pp,
 219                                     (nvlist_t **)vp);
 220                         }
 221                         return (0);
 222                 }
 223                 return (-1);
 224         case V8PLUS_TYPE_NULL:
 225                 if (dt == DATA_TYPE_BYTE) {
 226                         uchar_t v;
 227 
 228                         if (nvpair_value_byte((nvpair_t *)pp, &v) == 0 &&
 229                             v == 0)
 230                                 return (0);
 231                 }
 232                 return (-1);
 233         case V8PLUS_TYPE_UNDEFINED:
 234                 return (dt == DATA_TYPE_BOOLEAN ? 0 : -1);
 235         case V8PLUS_TYPE_ANY:
 236                 if (vp != NULL)
 237                         *(const nvpair_t **)vp = pp;
 238                 return (0);
 239         case V8PLUS_TYPE_INVALID:
 240                 if (vp != NULL)
 241                         *(data_type_t *)vp = dt;
 242                 return (0);
 243         case V8PLUS_TYPE_STRNUMBER64:
 244                 if (dt == DATA_TYPE_STRING) {
 245                         char *s;
 246                         uint64_t v;
 247 
 248                         (void) nvpair_value_string((nvpair_t *)pp, &s);
 249                         errno = 0;
 250                         v = (uint64_t)strtoull(s, NULL, 0);
 251                         if (errno != 0)
 252                                 return (-1);
 253                         if (vp != NULL)
 254                                 *(uint64_t *)vp = v;
 255                         return (0);
 256                 }
 257                 return (-1);
 258         default:
 259                 return (-1);
 260         }
 261 }
 262 
 263 int
 264 v8plus_args(const nvlist_t *lp, uint_t flags, v8plus_type_t t, ...)
 265 {
 266         v8plus_type_t nt;
 267         nvpair_t *pp;
 268         void *vp;
 269         va_list ap;
 270         uint_t i;
 271         char buf[32];
 272 
 273         va_start(ap, t);
 274 
 275         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 276                 switch (nt) {
 277                 case V8PLUS_TYPE_UNDEFINED:
 278                 case V8PLUS_TYPE_NULL:
 279                         break;
 280                 default:
 281                         (void) va_arg(ap, void *);
 282                 }
 283 
 284                 (void) snprintf(buf, sizeof (buf), "%u", i);
 285                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) != 0) {
 286                         (void) v8plus_error(V8PLUSERR_MISSINGARG,
 287                             "argument %u is required", i);
 288                         return (-1);
 289                 }
 290 
 291                 if (v8plus_arg_value(nt, pp, NULL) != 0) {
 292                         (void) v8plus_error(V8PLUSERR_BADARG,
 293                             "argument %u is of incorrect type", i);
 294                         return (-1);
 295                 }
 296 
 297                 nt = va_arg(ap, data_type_t);
 298         }
 299 
 300         va_end(ap);
 301 
 302         if (flags & V8PLUS_ARG_F_NOEXTRA) {
 303                 (void) snprintf(buf, sizeof (buf), "%u", i);
 304                 if (nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0) {
 305                         (void) v8plus_error(V8PLUSERR_EXTRAARG,
 306                             "superfluous extra argument(s) detected");
 307                         return (-1);
 308                 }
 309         }
 310 
 311         va_start(ap, t);
 312 
 313         for (i = 0, nt = t; nt != V8PLUS_TYPE_NONE; i++) {
 314                 switch (nt) {
 315                 case V8PLUS_TYPE_UNDEFINED:
 316                 case V8PLUS_TYPE_NULL:
 317                         vp = NULL;
 318                         break;
 319                 default:
 320                         vp = va_arg(ap, void *);
 321                 }
 322 
 323                 (void) snprintf(buf, sizeof (buf), "%u", i);
 324                 VERIFY(nvlist_lookup_nvpair((nvlist_t *)lp, buf, &pp) == 0);
 325                 VERIFY(v8plus_arg_value(nt, pp, vp) == 0);
 326 
 327                 nt = va_arg(ap, data_type_t);
 328         }
 329 
 330         va_end(ap);
 331 
 332         return (0);
 333 }
 334 
 335 static int
 336 v8plus_obj_vsetprops(nvlist_t *lp, v8plus_type_t t, va_list *ap)
 337 {
 338         v8plus_type_t nt = t;
 339         char *name;
 340         int err;
 341 
 342         /*
 343          * Do not call va_start() or va_end() in this function!  We are limited
 344          * to a single traversal of the arguments so that we can recurse to
 345          * handle embedded object definitions.
 346          */
 347 
 348         while (nt != V8PLUS_TYPE_NONE) {
 349                 name = va_arg(*ap, char *);
 350 
 351                 switch (nt) {
 352                 case V8PLUS_TYPE_STRING:
 353                 {
 354                         char *s = va_arg(*ap, char *);
 355                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 356                                 (void) v8plus_nverr(err, name);
 357                                 return (-1);
 358                         }
 359                         break;
 360                 }
 361                 case V8PLUS_TYPE_NUMBER:
 362                 {
 363                         double d = va_arg(*ap, double);
 364                         if ((err = nvlist_add_double(lp, name, d)) != 0) {
 365                                 (void) v8plus_nverr(err, name);
 366                                 return (-1);
 367                         }
 368                         break;
 369                 }
 370                 case V8PLUS_TYPE_BOOLEAN:
 371                 {
 372                         boolean_t b = va_arg(*ap, boolean_t);
 373                         if ((err = nvlist_add_boolean_value(lp,
 374                             name, b)) != 0) {
 375                                 (void) v8plus_nverr(err, name);
 376                                 return (-1);
 377                         }
 378                         break;
 379                 }
 380                 case V8PLUS_TYPE_JSFUNC:
 381                 {
 382                         v8plus_jsfunc_t j = va_arg(*ap, v8plus_jsfunc_t);
 383                         if ((err = nvlist_add_uint64_array(lp,
 384                             name, &j, 1)) != 0) {
 385                                 (void) v8plus_nverr(err, name);
 386                                 return (-1);
 387                         }
 388                         if ((err = nvlist_add_string_array(lp,
 389                             V8PLUS_JSF_COOKIE, NULL, 0)) != 0) {
 390                                 (void) v8plus_nverr(err, V8PLUS_JSF_COOKIE);
 391                                 return (-1);
 392                         }
 393                         v8plus_jsfunc_hold(j);
 394                         break;
 395                 }
 396                 case V8PLUS_TYPE_OBJECT:
 397                 {
 398                         const nvlist_t *op = va_arg(*ap, const nvlist_t *);
 399                         if ((err = nvlist_add_nvlist(lp, name,
 400                             (nvlist_t *)op)) != 0) {
 401                                 (void) v8plus_nverr(err, name);
 402                                 return (-1);
 403                         }
 404                         break;
 405                 }
 406                 case V8PLUS_TYPE_NULL:
 407                         if ((err = nvlist_add_byte(lp, name, 0)) != 0) {
 408                                 (void) v8plus_nverr(err, name);
 409                                 return (-1);
 410                         }
 411                         break;
 412                 case V8PLUS_TYPE_UNDEFINED:
 413                         if ((err = nvlist_add_boolean(lp, name)) != 0) {
 414                                 (void) v8plus_nverr(err, name);
 415                                 return (-1);
 416                         }
 417                         break;
 418                 case V8PLUS_TYPE_ANY:
 419                 {
 420                         nvpair_t *pp = va_arg(*ap, nvpair_t *);
 421                         if ((err = nvlist_add_nvpair(lp, pp)) != 0) {
 422                                 (void) v8plus_nverr(err, name);
 423                                 return (-1);
 424                         }
 425                         break;
 426                 }
 427                 case V8PLUS_TYPE_STRNUMBER64:
 428                 {
 429                         uint64_t v = va_arg(*ap, uint64_t);
 430                         char s[32];
 431                         (void) snprintf(s, sizeof (s), "%" PRIu64, v);
 432                         if ((err = nvlist_add_string(lp, name, s)) != 0) {
 433                                 (void) v8plus_nverr(err, name);
 434                                 return (-1);
 435                         }
 436                         break;
 437                 }
 438                 case V8PLUS_TYPE_INL_OBJECT:
 439                 {
 440                         nvlist_t *slp;
 441 
 442                         nt = va_arg(*ap, v8plus_type_t);
 443                         err = nvlist_alloc(&slp, NV_UNIQUE_NAME, 0);
 444                         if (err != 0) {
 445                                 (void) v8plus_nverr(err, name);
 446                                 return (-1);
 447                         }
 448                         if (v8plus_obj_vsetprops(slp, nt, ap) != 0)
 449                                 return (-1);
 450 
 451                         err = nvlist_add_nvlist(lp, name, slp);
 452                         nvlist_free(slp);
 453                         if (err != 0) {
 454                                 (void) v8plus_nverr(err, name);
 455                                 return (-1);
 456                         }
 457                         break;
 458                 }
 459                 case V8PLUS_TYPE_INVALID:
 460                 default:
 461                         (void) v8plus_error(V8PLUSERR_YOUSUCK,
 462                             "invalid property type %d", nt);
 463                         return (-1);
 464                 }
 465 
 466                 nt = va_arg(*ap, v8plus_type_t);
 467         }
 468 
 469         return (0);
 470 }
 471 
 472 nvlist_t *
 473 v8plus_obj(v8plus_type_t t, ...)
 474 {
 475         nvlist_t *rp;
 476         va_list ap;
 477         int err;
 478 
 479         if ((err = nvlist_alloc(&rp, NV_UNIQUE_NAME, 0)) != 0)
 480                 return (v8plus_nverr(err, NULL));
 481 
 482         va_start(ap, t);
 483         err = v8plus_obj_vsetprops(rp, t, &ap);
 484         va_end(ap);
 485 
 486         if (err != 0) {
 487                 nvlist_free(rp);
 488                 rp = NULL;
 489         }
 490 
 491         return (rp);
 492 }
 493 
 494 int
 495 v8plus_obj_setprops(nvlist_t *lp, v8plus_type_t t, ...)
 496 {
 497         va_list ap;
 498         int err;
 499 
 500         va_start(ap, t);
 501         err = v8plus_obj_vsetprops(lp, t, &ap);
 502         va_end(ap);
 503 
 504         return (err);
 505 }
 506 
 507 static void
 508 v8plus_uv_worker(uv_work_t *wp)
 509 {
 510         v8plus_uv_ctx_t *cp = wp->data;
 511 
 512         cp->vuc_result = cp->vuc_worker(cp->vuc_obj, cp->vuc_ctx);
 513 }
 514 
 515 static void
 516 v8plus_uv_completion(uv_work_t *wp)
 517 {
 518         v8plus_uv_ctx_t *cp = wp->data;
 519 
 520         cp->vuc_completion(cp->vuc_obj, cp->vuc_ctx, cp->vuc_result);
 521         v8plus_obj_rele(cp->vuc_obj);
 522         free(cp);
 523         free(wp);
 524 }
 525 
 526 void
 527 v8plus_defer(void *cop, void *ctxp, v8plus_worker_f worker,
 528     v8plus_completion_f completion)
 529 {
 530         uv_work_t *wp = malloc(sizeof (uv_work_t));
 531         v8plus_uv_ctx_t *cp = malloc(sizeof (v8plus_uv_ctx_t));
 532 
 533         bzero(wp, sizeof (uv_work_t));
 534         bzero(cp, sizeof (v8plus_uv_ctx_t));
 535 
 536         v8plus_obj_hold(cop);
 537         cp->vuc_obj = cop;
 538         cp->vuc_ctx = ctxp;
 539         cp->vuc_worker = worker;
 540         cp->vuc_completion = completion;
 541         wp->data = cp;
 542 
 543         uv_queue_work(uv_default_loop(), wp, v8plus_uv_worker,
 544             v8plus_uv_completion);
 545 }