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 }