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 }