1 /*
2 * QuickJS C library
3 *
4 * Copyright (c) 2017-2020 Fabrice Bellard
5 * Copyright (c) 2017-2020 Charlie Gordon
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <inttypes.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/time.h>
35 #include <time.h>
36 #include <signal.h>
37 #include <limits.h>
38 #include <sys/stat.h>
39 #include <dirent.h>
40 #if defined(_WIN32)
41 #include <windows.h>
42 #include <conio.h>
43 #include <utime.h>
44 #else
45 #include <dlfcn.h>
46 #include <termios.h>
47 #include <sys/ioctl.h>
48 #include <sys/wait.h>
49
50 #if defined(__APPLE__)
51 typedef sig_t sighandler_t;
52 #if !defined(environ)
53 #include <crt_externs.h>
54 #define environ (*_NSGetEnviron())
55 #endif
56 #endif /* __APPLE__ */
57
58 #endif
59
60 #if !defined(_WIN32)
61 /* enable the os.Worker API. IT relies on POSIX threads */
62 #define USE_WORKER
63 #endif
64
65 #ifdef USE_WORKER
66 #include <pthread.h>
67 #include <stdatomic.h>
68 #endif
69
70 #include "cutils.h"
71 #include "list.h"
72 #include "quickjs-libc.h"
73
74 /* TODO:
75 - add socket calls
76 */
77
78 typedef struct {
79 struct list_head link;
80 int fd;
81 JSValue rw_func[2];
82 } JSOSRWHandler;
83
84 typedef struct {
85 struct list_head link;
86 int sig_num;
87 JSValue func;
88 } JSOSSignalHandler;
89
90 typedef struct {
91 struct list_head link;
92 BOOL has_object;
93 int64_t timeout;
94 JSValue func;
95 } JSOSTimer;
96
97 typedef struct {
98 struct list_head link;
99 uint8_t *data;
100 size_t data_len;
101 /* list of SharedArrayBuffers, necessary to free the message */
102 uint8_t **sab_tab;
103 size_t sab_tab_len;
104 } JSWorkerMessage;
105
106 typedef struct {
107 int ref_count;
108 #ifdef USE_WORKER
109 pthread_mutex_t mutex;
110 #endif
111 struct list_head msg_queue; /* list of JSWorkerMessage.link */
112 int read_fd;
113 int write_fd;
114 } JSWorkerMessagePipe;
115
116 typedef struct {
117 struct list_head link;
118 JSWorkerMessagePipe *recv_pipe;
119 JSValue on_message_func;
120 } JSWorkerMessageHandler;
121
122 typedef struct JSThreadState {
123 struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */
124 struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */
125 struct list_head os_timers; /* list of JSOSTimer.link */
126 struct list_head port_list; /* list of JSWorkerMessageHandler.link */
127 int eval_script_recurse; /* only used in the main thread */
128 /* not used in the main thread */
129 JSWorkerMessagePipe *recv_pipe, *send_pipe;
130 } JSThreadState;
131
132 static uint64_t os_pending_signals;
133 static int (*os_poll_func)(JSContext *ctx);
134
js_std_dbuf_init(JSContext * ctx,DynBuf * s)135 static void js_std_dbuf_init(JSContext *ctx, DynBuf *s)
136 {
137 dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt);
138 }
139
my_isdigit(int c)140 static BOOL my_isdigit(int c)
141 {
142 return (c >= '0' && c <= '9');
143 }
144
js_printf_internal(JSContext * ctx,int argc,JSValueConst * argv,FILE * fp)145 static JSValue js_printf_internal(JSContext *ctx,
146 int argc, JSValueConst *argv, FILE *fp)
147 {
148 char fmtbuf[32];
149 uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];
150 JSValue res;
151 DynBuf dbuf;
152 const char *fmt_str;
153 const uint8_t *fmt, *fmt_end;
154 const uint8_t *p;
155 char *q;
156 int i, c, len, mod;
157 size_t fmt_len;
158 int32_t int32_arg;
159 int64_t int64_arg;
160 double double_arg;
161 const char *string_arg;
162 /* Use indirect call to dbuf_printf to prevent gcc warning */
163 int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf;
164
165 js_std_dbuf_init(ctx, &dbuf);
166
167 if (argc > 0) {
168 fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]);
169 if (!fmt_str)
170 goto fail;
171
172 i = 1;
173 fmt = (const uint8_t *)fmt_str;
174 fmt_end = fmt + fmt_len;
175 while (fmt < fmt_end) {
176 for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++)
177 continue;
178 dbuf_put(&dbuf, p, fmt - p);
179 if (fmt >= fmt_end)
180 break;
181 q = fmtbuf;
182 *q++ = *fmt++; /* copy '%' */
183
184 /* flags */
185 for(;;) {
186 c = *fmt;
187 if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' ||
188 c == '\'') {
189 if (q >= fmtbuf + sizeof(fmtbuf) - 1)
190 goto invalid;
191 *q++ = c;
192 fmt++;
193 } else {
194 break;
195 }
196 }
197 /* width */
198 if (*fmt == '*') {
199 if (i >= argc)
200 goto missing;
201 if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
202 goto fail;
203 q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
204 fmt++;
205 } else {
206 while (my_isdigit(*fmt)) {
207 if (q >= fmtbuf + sizeof(fmtbuf) - 1)
208 goto invalid;
209 *q++ = *fmt++;
210 }
211 }
212 if (*fmt == '.') {
213 if (q >= fmtbuf + sizeof(fmtbuf) - 1)
214 goto invalid;
215 *q++ = *fmt++;
216 if (*fmt == '*') {
217 if (i >= argc)
218 goto missing;
219 if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
220 goto fail;
221 q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
222 fmt++;
223 } else {
224 while (my_isdigit(*fmt)) {
225 if (q >= fmtbuf + sizeof(fmtbuf) - 1)
226 goto invalid;
227 *q++ = *fmt++;
228 }
229 }
230 }
231
232 /* we only support the "l" modifier for 64 bit numbers */
233 mod = ' ';
234 if (*fmt == 'l') {
235 mod = *fmt++;
236 }
237
238 /* type */
239 c = *fmt++;
240 if (q >= fmtbuf + sizeof(fmtbuf) - 1)
241 goto invalid;
242 *q++ = c;
243 *q = '\0';
244
245 switch (c) {
246 case 'c':
247 if (i >= argc)
248 goto missing;
249 if (JS_IsString(argv[i])) {
250 string_arg = JS_ToCString(ctx, argv[i++]);
251 if (!string_arg)
252 goto fail;
253 int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
254 JS_FreeCString(ctx, string_arg);
255 } else {
256 if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
257 goto fail;
258 }
259 /* handle utf-8 encoding explicitly */
260 if ((unsigned)int32_arg > 0x10FFFF)
261 int32_arg = 0xFFFD;
262 /* ignore conversion flags, width and precision */
263 len = unicode_to_utf8(cbuf, int32_arg);
264 dbuf_put(&dbuf, cbuf, len);
265 break;
266
267 case 'd':
268 case 'i':
269 case 'o':
270 case 'u':
271 case 'x':
272 case 'X':
273 if (i >= argc)
274 goto missing;
275 if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++]))
276 goto fail;
277 if (mod == 'l') {
278 /* 64 bit number */
279 #if defined(_WIN32)
280 if (q >= fmtbuf + sizeof(fmtbuf) - 3)
281 goto invalid;
282 q[2] = q[-1];
283 q[-1] = 'I';
284 q[0] = '6';
285 q[1] = '4';
286 q[3] = '\0';
287 dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg);
288 #else
289 if (q >= fmtbuf + sizeof(fmtbuf) - 2)
290 goto invalid;
291 q[1] = q[-1];
292 q[-1] = q[0] = 'l';
293 q[2] = '\0';
294 dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);
295 #endif
296 } else {
297 dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg);
298 }
299 break;
300
301 case 's':
302 if (i >= argc)
303 goto missing;
304 /* XXX: handle strings containing null characters */
305 string_arg = JS_ToCString(ctx, argv[i++]);
306 if (!string_arg)
307 goto fail;
308 dbuf_printf_fun(&dbuf, fmtbuf, string_arg);
309 JS_FreeCString(ctx, string_arg);
310 break;
311
312 case 'e':
313 case 'f':
314 case 'g':
315 case 'a':
316 case 'E':
317 case 'F':
318 case 'G':
319 case 'A':
320 if (i >= argc)
321 goto missing;
322 if (JS_ToFloat64(ctx, &double_arg, argv[i++]))
323 goto fail;
324 dbuf_printf_fun(&dbuf, fmtbuf, double_arg);
325 break;
326
327 case '%':
328 dbuf_putc(&dbuf, '%');
329 break;
330
331 default:
332 /* XXX: should support an extension mechanism */
333 invalid:
334 JS_ThrowTypeError(ctx, "invalid conversion specifier in format string");
335 goto fail;
336 missing:
337 JS_ThrowReferenceError(ctx, "missing argument for conversion specifier");
338 goto fail;
339 }
340 }
341 JS_FreeCString(ctx, fmt_str);
342 }
343 if (dbuf.error) {
344 res = JS_ThrowOutOfMemory(ctx);
345 } else {
346 if (fp) {
347 len = fwrite(dbuf.buf, 1, dbuf.size, fp);
348 res = JS_NewInt32(ctx, len);
349 } else {
350 res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size);
351 }
352 }
353 dbuf_free(&dbuf);
354 return res;
355
356 fail:
357 dbuf_free(&dbuf);
358 return JS_EXCEPTION;
359 }
360
js_load_file(JSContext * ctx,size_t * pbuf_len,const char * filename)361 uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename)
362 {
363 FILE *f;
364 uint8_t *buf;
365 size_t buf_len;
366 long lret;
367
368 f = fopen(filename, "rb");
369 if (!f)
370 return NULL;
371 if (fseek(f, 0, SEEK_END) < 0)
372 goto fail;
373 lret = ftell(f);
374 if (lret < 0)
375 goto fail;
376 /* XXX: on Linux, ftell() return LONG_MAX for directories */
377 if (lret == LONG_MAX) {
378 errno = EISDIR;
379 goto fail;
380 }
381 buf_len = lret;
382 if (fseek(f, 0, SEEK_SET) < 0)
383 goto fail;
384 if (ctx)
385 buf = js_malloc(ctx, buf_len + 1);
386 else
387 buf = malloc(buf_len + 1);
388 if (!buf)
389 goto fail;
390 if (fread(buf, 1, buf_len, f) != buf_len) {
391 errno = EIO;
392 if (ctx)
393 js_free(ctx, buf);
394 else
395 free(buf);
396 fail:
397 fclose(f);
398 return NULL;
399 }
400 buf[buf_len] = '\0';
401 fclose(f);
402 *pbuf_len = buf_len;
403 return buf;
404 }
405
406 /* load and evaluate a file */
js_loadScript(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)407 static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val,
408 int argc, JSValueConst *argv)
409 {
410 uint8_t *buf;
411 const char *filename;
412 JSValue ret;
413 size_t buf_len;
414
415 filename = JS_ToCString(ctx, argv[0]);
416 if (!filename)
417 return JS_EXCEPTION;
418 buf = js_load_file(ctx, &buf_len, filename);
419 if (!buf) {
420 JS_ThrowReferenceError(ctx, "could not load '%s'", filename);
421 JS_FreeCString(ctx, filename);
422 return JS_EXCEPTION;
423 }
424 ret = JS_Eval(ctx, (char *)buf, buf_len, filename,
425 JS_EVAL_TYPE_GLOBAL);
426 js_free(ctx, buf);
427 JS_FreeCString(ctx, filename);
428 return ret;
429 }
430
431 /* load a file as a UTF-8 encoded string */
js_std_loadFile(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)432 static JSValue js_std_loadFile(JSContext *ctx, JSValueConst this_val,
433 int argc, JSValueConst *argv)
434 {
435 uint8_t *buf;
436 const char *filename;
437 JSValue ret;
438 size_t buf_len;
439
440 filename = JS_ToCString(ctx, argv[0]);
441 if (!filename)
442 return JS_EXCEPTION;
443 buf = js_load_file(ctx, &buf_len, filename);
444 JS_FreeCString(ctx, filename);
445 if (!buf)
446 return JS_NULL;
447 ret = JS_NewStringLen(ctx, (char *)buf, buf_len);
448 js_free(ctx, buf);
449 return ret;
450 }
451
452 typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx,
453 const char *module_name);
454
455
456 #if defined(_WIN32)
js_module_loader_so(JSContext * ctx,const char * module_name)457 static JSModuleDef *js_module_loader_so(JSContext *ctx,
458 const char *module_name)
459 {
460 JS_ThrowReferenceError(ctx, "shared library modules are not supported yet");
461 return NULL;
462 }
463 #else
js_module_loader_so(JSContext * ctx,const char * module_name)464 static JSModuleDef *js_module_loader_so(JSContext *ctx,
465 const char *module_name)
466 {
467 JSModuleDef *m;
468 void *hd;
469 JSInitModuleFunc *init;
470 char *filename;
471
472 if (!strchr(module_name, '/')) {
473 /* must add a '/' so that the DLL is not searched in the
474 system library paths */
475 filename = js_malloc(ctx, strlen(module_name) + 2 + 1);
476 if (!filename)
477 return NULL;
478 strcpy(filename, "./");
479 strcpy(filename + 2, module_name);
480 } else {
481 filename = (char *)module_name;
482 }
483
484 /* C module */
485 hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
486 if (filename != module_name)
487 js_free(ctx, filename);
488 if (!hd) {
489 JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library",
490 module_name);
491 goto fail;
492 }
493
494 init = dlsym(hd, "js_init_module");
495 if (!init) {
496 JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found",
497 module_name);
498 goto fail;
499 }
500
501 m = init(ctx, module_name);
502 if (!m) {
503 JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error",
504 module_name);
505 fail:
506 if (hd)
507 dlclose(hd);
508 return NULL;
509 }
510 return m;
511 }
512 #endif /* !_WIN32 */
513
js_module_set_import_meta(JSContext * ctx,JSValueConst func_val,JS_BOOL use_realpath,JS_BOOL is_main)514 int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
515 JS_BOOL use_realpath, JS_BOOL is_main)
516 {
517 JSModuleDef *m;
518 char buf[PATH_MAX + 16];
519 JSValue meta_obj;
520 JSAtom module_name_atom;
521 const char *module_name;
522
523 assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE);
524 m = JS_VALUE_GET_PTR(func_val);
525
526 module_name_atom = JS_GetModuleName(ctx, m);
527 module_name = JS_AtomToCString(ctx, module_name_atom);
528 JS_FreeAtom(ctx, module_name_atom);
529 if (!module_name)
530 return -1;
531 if (!strchr(module_name, ':')) {
532 strcpy(buf, "file://");
533 #if !defined(_WIN32)
534 /* realpath() cannot be used with modules compiled with qjsc
535 because the corresponding module source code is not
536 necessarily present */
537 if (use_realpath) {
538 char *res = realpath(module_name, buf + strlen(buf));
539 if (!res) {
540 JS_ThrowTypeError(ctx, "realpath failure");
541 JS_FreeCString(ctx, module_name);
542 return -1;
543 }
544 } else
545 #endif
546 {
547 pstrcat(buf, sizeof(buf), module_name);
548 }
549 } else {
550 pstrcpy(buf, sizeof(buf), module_name);
551 }
552 JS_FreeCString(ctx, module_name);
553
554 meta_obj = JS_GetImportMeta(ctx, m);
555 if (JS_IsException(meta_obj))
556 return -1;
557 JS_DefinePropertyValueStr(ctx, meta_obj, "url",
558 JS_NewString(ctx, buf),
559 JS_PROP_C_W_E);
560 JS_DefinePropertyValueStr(ctx, meta_obj, "main",
561 JS_NewBool(ctx, is_main),
562 JS_PROP_C_W_E);
563 JS_FreeValue(ctx, meta_obj);
564 return 0;
565 }
566
js_module_loader(JSContext * ctx,const char * module_name,void * opaque)567 JSModuleDef *js_module_loader(JSContext *ctx,
568 const char *module_name, void *opaque)
569 {
570 JSModuleDef *m;
571
572 if (has_suffix(module_name, ".so")) {
573 m = js_module_loader_so(ctx, module_name);
574 } else {
575 size_t buf_len;
576 uint8_t *buf;
577 JSValue func_val;
578
579 buf = js_load_file(ctx, &buf_len, module_name);
580 if (!buf) {
581 JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
582 module_name);
583 return NULL;
584 }
585
586 /* compile the module */
587 func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
588 JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
589 js_free(ctx, buf);
590 if (JS_IsException(func_val))
591 return NULL;
592 /* XXX: could propagate the exception */
593 js_module_set_import_meta(ctx, func_val, TRUE, FALSE);
594 /* the module is already referenced, so we must free it */
595 m = JS_VALUE_GET_PTR(func_val);
596 JS_FreeValue(ctx, func_val);
597 }
598 return m;
599 }
600
js_std_exit(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)601 static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val,
602 int argc, JSValueConst *argv)
603 {
604 int status;
605 if (JS_ToInt32(ctx, &status, argv[0]))
606 status = -1;
607 exit(status);
608 return JS_UNDEFINED;
609 }
610
js_std_getenv(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)611 static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val,
612 int argc, JSValueConst *argv)
613 {
614 const char *name, *str;
615 name = JS_ToCString(ctx, argv[0]);
616 if (!name)
617 return JS_EXCEPTION;
618 str = getenv(name);
619 JS_FreeCString(ctx, name);
620 if (!str)
621 return JS_UNDEFINED;
622 else
623 return JS_NewString(ctx, str);
624 }
625
626 #if defined(_WIN32)
setenv(const char * name,const char * value,int overwrite)627 static void setenv(const char *name, const char *value, int overwrite)
628 {
629 char *str;
630 size_t name_len, value_len;
631 name_len = strlen(name);
632 value_len = strlen(value);
633 str = malloc(name_len + 1 + value_len + 1);
634 memcpy(str, name, name_len);
635 str[name_len] = '=';
636 memcpy(str + name_len + 1, value, value_len);
637 str[name_len + 1 + value_len] = '\0';
638 _putenv(str);
639 free(str);
640 }
641
unsetenv(const char * name)642 static void unsetenv(const char *name)
643 {
644 setenv(name, "", TRUE);
645 }
646 #endif /* _WIN32 */
647
js_std_setenv(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)648 static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val,
649 int argc, JSValueConst *argv)
650 {
651 const char *name, *value;
652 name = JS_ToCString(ctx, argv[0]);
653 if (!name)
654 return JS_EXCEPTION;
655 value = JS_ToCString(ctx, argv[1]);
656 if (!value) {
657 JS_FreeCString(ctx, name);
658 return JS_EXCEPTION;
659 }
660 setenv(name, value, TRUE);
661 JS_FreeCString(ctx, name);
662 JS_FreeCString(ctx, value);
663 return JS_UNDEFINED;
664 }
665
js_std_unsetenv(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)666 static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val,
667 int argc, JSValueConst *argv)
668 {
669 const char *name;
670 name = JS_ToCString(ctx, argv[0]);
671 if (!name)
672 return JS_EXCEPTION;
673 unsetenv(name);
674 JS_FreeCString(ctx, name);
675 return JS_UNDEFINED;
676 }
677
678 /* return an object containing the list of the available environment
679 variables. */
js_std_getenviron(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)680 static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val,
681 int argc, JSValueConst *argv)
682 {
683 char **envp;
684 const char *name, *p, *value;
685 JSValue obj;
686 uint32_t idx;
687 size_t name_len;
688 JSAtom atom;
689 int ret;
690
691 obj = JS_NewObject(ctx);
692 if (JS_IsException(obj))
693 return JS_EXCEPTION;
694 envp = environ;
695 for(idx = 0; envp[idx] != NULL; idx++) {
696 name = envp[idx];
697 p = strchr(name, '=');
698 name_len = p - name;
699 if (!p)
700 continue;
701 value = p + 1;
702 atom = JS_NewAtomLen(ctx, name, name_len);
703 if (atom == JS_ATOM_NULL)
704 goto fail;
705 ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value),
706 JS_PROP_C_W_E);
707 JS_FreeAtom(ctx, atom);
708 if (ret < 0)
709 goto fail;
710 }
711 return obj;
712 fail:
713 JS_FreeValue(ctx, obj);
714 return JS_EXCEPTION;
715 }
716
js_std_gc(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)717 static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val,
718 int argc, JSValueConst *argv)
719 {
720 JS_RunGC(JS_GetRuntime(ctx));
721 return JS_UNDEFINED;
722 }
723
interrupt_handler(JSRuntime * rt,void * opaque)724 static int interrupt_handler(JSRuntime *rt, void *opaque)
725 {
726 return (os_pending_signals >> SIGINT) & 1;
727 }
728
get_bool_option(JSContext * ctx,BOOL * pbool,JSValueConst obj,const char * option)729 static int get_bool_option(JSContext *ctx, BOOL *pbool,
730 JSValueConst obj,
731 const char *option)
732 {
733 JSValue val;
734 val = JS_GetPropertyStr(ctx, obj, option);
735 if (JS_IsException(val))
736 return -1;
737 if (!JS_IsUndefined(val)) {
738 *pbool = JS_ToBool(ctx, val);
739 }
740 JS_FreeValue(ctx, val);
741 return 0;
742 }
743
js_evalScript(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)744 static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
745 int argc, JSValueConst *argv)
746 {
747 JSRuntime *rt = JS_GetRuntime(ctx);
748 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
749 const char *str;
750 size_t len;
751 JSValue ret;
752 JSValueConst options_obj;
753 BOOL backtrace_barrier = FALSE;
754 int flags;
755
756 if (argc >= 2) {
757 options_obj = argv[1];
758 if (get_bool_option(ctx, &backtrace_barrier, options_obj,
759 "backtrace_barrier"))
760 return JS_EXCEPTION;
761 }
762
763 str = JS_ToCStringLen(ctx, &len, argv[0]);
764 if (!str)
765 return JS_EXCEPTION;
766 if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {
767 /* install the interrupt handler */
768 JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
769 }
770 flags = JS_EVAL_TYPE_GLOBAL;
771 if (backtrace_barrier)
772 flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
773 ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
774 JS_FreeCString(ctx, str);
775 if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
776 /* remove the interrupt handler */
777 JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL);
778 os_pending_signals &= ~((uint64_t)1 << SIGINT);
779 /* convert the uncatchable "interrupted" error into a normal error
780 so that it can be caught by the REPL */
781 if (JS_IsException(ret))
782 JS_ResetUncatchableError(ctx);
783 }
784 return ret;
785 }
786
787 static JSClassID js_std_file_class_id;
788
789 typedef struct {
790 FILE *f;
791 BOOL close_in_finalizer;
792 BOOL is_popen;
793 } JSSTDFile;
794
js_std_file_finalizer(JSRuntime * rt,JSValue val)795 static void js_std_file_finalizer(JSRuntime *rt, JSValue val)
796 {
797 JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id);
798 if (s) {
799 if (s->f && s->close_in_finalizer) {
800 if (s->is_popen)
801 pclose(s->f);
802 else
803 fclose(s->f);
804 }
805 js_free_rt(rt, s);
806 }
807 }
808
js_get_errno(ssize_t ret)809 static ssize_t js_get_errno(ssize_t ret)
810 {
811 if (ret == -1)
812 ret = -errno;
813 return ret;
814 }
815
js_std_strerror(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)816 static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,
817 int argc, JSValueConst *argv)
818 {
819 int err;
820 if (JS_ToInt32(ctx, &err, argv[0]))
821 return JS_EXCEPTION;
822 return JS_NewString(ctx, strerror(err));
823 }
824
js_std_parseExtJSON(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)825 static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val,
826 int argc, JSValueConst *argv)
827 {
828 JSValue obj;
829 const char *str;
830 size_t len;
831
832 str = JS_ToCStringLen(ctx, &len, argv[0]);
833 if (!str)
834 return JS_EXCEPTION;
835 obj = JS_ParseJSON2(ctx, str, len, "<input>", JS_PARSE_JSON_EXT);
836 JS_FreeCString(ctx, str);
837 return obj;
838 }
839
js_new_std_file(JSContext * ctx,FILE * f,BOOL close_in_finalizer,BOOL is_popen)840 static JSValue js_new_std_file(JSContext *ctx, FILE *f,
841 BOOL close_in_finalizer,
842 BOOL is_popen)
843 {
844 JSSTDFile *s;
845 JSValue obj;
846 obj = JS_NewObjectClass(ctx, js_std_file_class_id);
847 if (JS_IsException(obj))
848 return obj;
849 s = js_mallocz(ctx, sizeof(*s));
850 if (!s) {
851 JS_FreeValue(ctx, obj);
852 return JS_EXCEPTION;
853 }
854 s->close_in_finalizer = close_in_finalizer;
855 s->is_popen = is_popen;
856 s->f = f;
857 JS_SetOpaque(obj, s);
858 return obj;
859 }
860
js_set_error_object(JSContext * ctx,JSValue obj,int err)861 static void js_set_error_object(JSContext *ctx, JSValue obj, int err)
862 {
863 if (!JS_IsUndefined(obj)) {
864 JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err));
865 }
866 }
867
js_std_open(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)868 static JSValue js_std_open(JSContext *ctx, JSValueConst this_val,
869 int argc, JSValueConst *argv)
870 {
871 const char *filename, *mode = NULL;
872 FILE *f;
873 int err;
874
875 filename = JS_ToCString(ctx, argv[0]);
876 if (!filename)
877 goto fail;
878 mode = JS_ToCString(ctx, argv[1]);
879 if (!mode)
880 goto fail;
881 if (mode[strspn(mode, "rwa+b")] != '\0') {
882 JS_ThrowTypeError(ctx, "invalid file mode");
883 goto fail;
884 }
885
886 f = fopen(filename, mode);
887 if (!f)
888 err = errno;
889 else
890 err = 0;
891 if (argc >= 3)
892 js_set_error_object(ctx, argv[2], err);
893 JS_FreeCString(ctx, filename);
894 JS_FreeCString(ctx, mode);
895 if (!f)
896 return JS_NULL;
897 return js_new_std_file(ctx, f, TRUE, FALSE);
898 fail:
899 JS_FreeCString(ctx, filename);
900 JS_FreeCString(ctx, mode);
901 return JS_EXCEPTION;
902 }
903
js_std_popen(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)904 static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val,
905 int argc, JSValueConst *argv)
906 {
907 const char *filename, *mode = NULL;
908 FILE *f;
909 int err;
910
911 filename = JS_ToCString(ctx, argv[0]);
912 if (!filename)
913 goto fail;
914 mode = JS_ToCString(ctx, argv[1]);
915 if (!mode)
916 goto fail;
917 if (mode[strspn(mode, "rw")] != '\0') {
918 JS_ThrowTypeError(ctx, "invalid file mode");
919 goto fail;
920 }
921
922 f = popen(filename, mode);
923 if (!f)
924 err = errno;
925 else
926 err = 0;
927 if (argc >= 3)
928 js_set_error_object(ctx, argv[2], err);
929 JS_FreeCString(ctx, filename);
930 JS_FreeCString(ctx, mode);
931 if (!f)
932 return JS_NULL;
933 return js_new_std_file(ctx, f, TRUE, TRUE);
934 fail:
935 JS_FreeCString(ctx, filename);
936 JS_FreeCString(ctx, mode);
937 return JS_EXCEPTION;
938 }
939
js_std_fdopen(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)940 static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val,
941 int argc, JSValueConst *argv)
942 {
943 const char *mode;
944 FILE *f;
945 int fd, err;
946
947 if (JS_ToInt32(ctx, &fd, argv[0]))
948 return JS_EXCEPTION;
949 mode = JS_ToCString(ctx, argv[1]);
950 if (!mode)
951 goto fail;
952 if (mode[strspn(mode, "rwa+")] != '\0') {
953 JS_ThrowTypeError(ctx, "invalid file mode");
954 goto fail;
955 }
956
957 f = fdopen(fd, mode);
958 if (!f)
959 err = errno;
960 else
961 err = 0;
962 if (argc >= 3)
963 js_set_error_object(ctx, argv[2], err);
964 JS_FreeCString(ctx, mode);
965 if (!f)
966 return JS_NULL;
967 return js_new_std_file(ctx, f, TRUE, FALSE);
968 fail:
969 JS_FreeCString(ctx, mode);
970 return JS_EXCEPTION;
971 }
972
js_std_tmpfile(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)973 static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val,
974 int argc, JSValueConst *argv)
975 {
976 FILE *f;
977 f = tmpfile();
978 if (argc >= 1)
979 js_set_error_object(ctx, argv[0], f ? 0 : errno);
980 if (!f)
981 return JS_NULL;
982 return js_new_std_file(ctx, f, TRUE, FALSE);
983 }
984
js_std_sprintf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)985 static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val,
986 int argc, JSValueConst *argv)
987 {
988 return js_printf_internal(ctx, argc, argv, NULL);
989 }
990
js_std_printf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)991 static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val,
992 int argc, JSValueConst *argv)
993 {
994 return js_printf_internal(ctx, argc, argv, stdout);
995 }
996
js_std_file_get(JSContext * ctx,JSValueConst obj)997 static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj)
998 {
999 JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id);
1000 if (!s)
1001 return NULL;
1002 if (!s->f) {
1003 JS_ThrowTypeError(ctx, "invalid file handle");
1004 return NULL;
1005 }
1006 return s->f;
1007 }
1008
js_std_file_puts(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)1009 static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val,
1010 int argc, JSValueConst *argv, int magic)
1011 {
1012 FILE *f;
1013 int i;
1014 const char *str;
1015 size_t len;
1016
1017 if (magic == 0) {
1018 f = stdout;
1019 } else {
1020 f = js_std_file_get(ctx, this_val);
1021 if (!f)
1022 return JS_EXCEPTION;
1023 }
1024
1025 for(i = 0; i < argc; i++) {
1026 str = JS_ToCStringLen(ctx, &len, argv[i]);
1027 if (!str)
1028 return JS_EXCEPTION;
1029 fwrite(str, 1, len, f);
1030 JS_FreeCString(ctx, str);
1031 }
1032 return JS_UNDEFINED;
1033 }
1034
js_std_file_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1035 static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val,
1036 int argc, JSValueConst *argv)
1037 {
1038 JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id);
1039 int err;
1040 if (!s)
1041 return JS_EXCEPTION;
1042 if (!s->f)
1043 return JS_ThrowTypeError(ctx, "invalid file handle");
1044 if (s->is_popen)
1045 err = js_get_errno(pclose(s->f));
1046 else
1047 err = js_get_errno(fclose(s->f));
1048 s->f = NULL;
1049 return JS_NewInt32(ctx, err);
1050 }
1051
js_std_file_printf(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1052 static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val,
1053 int argc, JSValueConst *argv)
1054 {
1055 FILE *f = js_std_file_get(ctx, this_val);
1056 if (!f)
1057 return JS_EXCEPTION;
1058 return js_printf_internal(ctx, argc, argv, f);
1059 }
1060
js_std_file_flush(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1061 static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val,
1062 int argc, JSValueConst *argv)
1063 {
1064 FILE *f = js_std_file_get(ctx, this_val);
1065 if (!f)
1066 return JS_EXCEPTION;
1067 fflush(f);
1068 return JS_UNDEFINED;
1069 }
1070
js_std_file_tell(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_bigint)1071 static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,
1072 int argc, JSValueConst *argv, int is_bigint)
1073 {
1074 FILE *f = js_std_file_get(ctx, this_val);
1075 int64_t pos;
1076 if (!f)
1077 return JS_EXCEPTION;
1078 #if defined(__linux__)
1079 pos = ftello(f);
1080 #else
1081 pos = ftell(f);
1082 #endif
1083 if (is_bigint)
1084 return JS_NewBigInt64(ctx, pos);
1085 else
1086 return JS_NewInt64(ctx, pos);
1087 }
1088
js_std_file_seek(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1089 static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,
1090 int argc, JSValueConst *argv)
1091 {
1092 FILE *f = js_std_file_get(ctx, this_val);
1093 int64_t pos;
1094 int whence, ret;
1095 if (!f)
1096 return JS_EXCEPTION;
1097 if (JS_ToInt64Ext(ctx, &pos, argv[0]))
1098 return JS_EXCEPTION;
1099 if (JS_ToInt32(ctx, &whence, argv[1]))
1100 return JS_EXCEPTION;
1101 #if defined(__linux__)
1102 ret = fseeko(f, pos, whence);
1103 #else
1104 ret = fseek(f, pos, whence);
1105 #endif
1106 if (ret < 0)
1107 ret = -errno;
1108 return JS_NewInt32(ctx, ret);
1109 }
1110
js_std_file_eof(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1111 static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val,
1112 int argc, JSValueConst *argv)
1113 {
1114 FILE *f = js_std_file_get(ctx, this_val);
1115 if (!f)
1116 return JS_EXCEPTION;
1117 return JS_NewBool(ctx, feof(f));
1118 }
1119
js_std_file_error(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1120 static JSValue js_std_file_error(JSContext *ctx, JSValueConst this_val,
1121 int argc, JSValueConst *argv)
1122 {
1123 FILE *f = js_std_file_get(ctx, this_val);
1124 if (!f)
1125 return JS_EXCEPTION;
1126 return JS_NewBool(ctx, ferror(f));
1127 }
1128
js_std_file_clearerr(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1129 static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val,
1130 int argc, JSValueConst *argv)
1131 {
1132 FILE *f = js_std_file_get(ctx, this_val);
1133 if (!f)
1134 return JS_EXCEPTION;
1135 clearerr(f);
1136 return JS_UNDEFINED;
1137 }
1138
js_std_file_fileno(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1139 static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val,
1140 int argc, JSValueConst *argv)
1141 {
1142 FILE *f = js_std_file_get(ctx, this_val);
1143 if (!f)
1144 return JS_EXCEPTION;
1145 return JS_NewInt32(ctx, fileno(f));
1146 }
1147
js_std_file_read_write(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)1148 static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val,
1149 int argc, JSValueConst *argv, int magic)
1150 {
1151 FILE *f = js_std_file_get(ctx, this_val);
1152 uint64_t pos, len;
1153 size_t size, ret;
1154 uint8_t *buf;
1155
1156 if (!f)
1157 return JS_EXCEPTION;
1158 if (JS_ToIndex(ctx, &pos, argv[1]))
1159 return JS_EXCEPTION;
1160 if (JS_ToIndex(ctx, &len, argv[2]))
1161 return JS_EXCEPTION;
1162 buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
1163 if (!buf)
1164 return JS_EXCEPTION;
1165 if (pos + len > size)
1166 return JS_ThrowRangeError(ctx, "read/write array buffer overflow");
1167 if (magic)
1168 ret = fwrite(buf + pos, 1, len, f);
1169 else
1170 ret = fread(buf + pos, 1, len, f);
1171 return JS_NewInt64(ctx, ret);
1172 }
1173
1174 /* XXX: could use less memory and go faster */
js_std_file_getline(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1175 static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val,
1176 int argc, JSValueConst *argv)
1177 {
1178 FILE *f = js_std_file_get(ctx, this_val);
1179 int c;
1180 DynBuf dbuf;
1181 JSValue obj;
1182
1183 if (!f)
1184 return JS_EXCEPTION;
1185
1186 js_std_dbuf_init(ctx, &dbuf);
1187 for(;;) {
1188 c = fgetc(f);
1189 if (c == EOF) {
1190 if (dbuf.size == 0) {
1191 /* EOF */
1192 dbuf_free(&dbuf);
1193 return JS_NULL;
1194 } else {
1195 break;
1196 }
1197 }
1198 if (c == '\n')
1199 break;
1200 if (dbuf_putc(&dbuf, c)) {
1201 dbuf_free(&dbuf);
1202 return JS_ThrowOutOfMemory(ctx);
1203 }
1204 }
1205 obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);
1206 dbuf_free(&dbuf);
1207 return obj;
1208 }
1209
1210 /* XXX: could use less memory and go faster */
js_std_file_readAsString(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1211 static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val,
1212 int argc, JSValueConst *argv)
1213 {
1214 FILE *f = js_std_file_get(ctx, this_val);
1215 int c;
1216 DynBuf dbuf;
1217 JSValue obj;
1218 uint64_t max_size64;
1219 size_t max_size;
1220 JSValueConst max_size_val;
1221
1222 if (!f)
1223 return JS_EXCEPTION;
1224
1225 if (argc >= 1)
1226 max_size_val = argv[0];
1227 else
1228 max_size_val = JS_UNDEFINED;
1229 max_size = (size_t)-1;
1230 if (!JS_IsUndefined(max_size_val)) {
1231 if (JS_ToIndex(ctx, &max_size64, max_size_val))
1232 return JS_EXCEPTION;
1233 if (max_size64 < max_size)
1234 max_size = max_size64;
1235 }
1236
1237 js_std_dbuf_init(ctx, &dbuf);
1238 while (max_size != 0) {
1239 c = fgetc(f);
1240 if (c == EOF)
1241 break;
1242 if (dbuf_putc(&dbuf, c)) {
1243 dbuf_free(&dbuf);
1244 return JS_EXCEPTION;
1245 }
1246 max_size--;
1247 }
1248 obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size);
1249 dbuf_free(&dbuf);
1250 return obj;
1251 }
1252
js_std_file_getByte(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1253 static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val,
1254 int argc, JSValueConst *argv)
1255 {
1256 FILE *f = js_std_file_get(ctx, this_val);
1257 if (!f)
1258 return JS_EXCEPTION;
1259 return JS_NewInt32(ctx, fgetc(f));
1260 }
1261
js_std_file_putByte(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1262 static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,
1263 int argc, JSValueConst *argv)
1264 {
1265 FILE *f = js_std_file_get(ctx, this_val);
1266 int c;
1267 if (!f)
1268 return JS_EXCEPTION;
1269 if (JS_ToInt32(ctx, &c, argv[0]))
1270 return JS_EXCEPTION;
1271 c = fputc(c, f);
1272 return JS_NewInt32(ctx, c);
1273 }
1274
1275 /* urlGet */
1276
1277 #define URL_GET_PROGRAM "curl -s -i"
1278 #define URL_GET_BUF_SIZE 4096
1279
http_get_header_line(FILE * f,char * buf,size_t buf_size,DynBuf * dbuf)1280 static int http_get_header_line(FILE *f, char *buf, size_t buf_size,
1281 DynBuf *dbuf)
1282 {
1283 int c;
1284 char *p;
1285
1286 p = buf;
1287 for(;;) {
1288 c = fgetc(f);
1289 if (c < 0)
1290 return -1;
1291 if ((p - buf) < buf_size - 1)
1292 *p++ = c;
1293 if (dbuf)
1294 dbuf_putc(dbuf, c);
1295 if (c == '\n')
1296 break;
1297 }
1298 *p = '\0';
1299 return 0;
1300 }
1301
http_get_status(const char * buf)1302 static int http_get_status(const char *buf)
1303 {
1304 const char *p = buf;
1305 while (*p != ' ' && *p != '\0')
1306 p++;
1307 if (*p != ' ')
1308 return 0;
1309 while (*p == ' ')
1310 p++;
1311 return atoi(p);
1312 }
1313
js_std_urlGet(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1314 static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
1315 int argc, JSValueConst *argv)
1316 {
1317 const char *url;
1318 DynBuf cmd_buf;
1319 DynBuf data_buf_s, *data_buf = &data_buf_s;
1320 DynBuf header_buf_s, *header_buf = &header_buf_s;
1321 char *buf;
1322 size_t i, len;
1323 int c, status;
1324 JSValue response = JS_UNDEFINED, ret_obj;
1325 JSValueConst options_obj;
1326 FILE *f;
1327 BOOL binary_flag, full_flag;
1328
1329 url = JS_ToCString(ctx, argv[0]);
1330 if (!url)
1331 return JS_EXCEPTION;
1332
1333 binary_flag = FALSE;
1334 full_flag = FALSE;
1335
1336 if (argc >= 2) {
1337 options_obj = argv[1];
1338
1339 if (get_bool_option(ctx, &binary_flag, options_obj, "binary"))
1340 goto fail_obj;
1341
1342 if (get_bool_option(ctx, &full_flag, options_obj, "full")) {
1343 fail_obj:
1344 JS_FreeCString(ctx, url);
1345 return JS_EXCEPTION;
1346 }
1347 }
1348
1349 js_std_dbuf_init(ctx, &cmd_buf);
1350 dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM);
1351 len = strlen(url);
1352 for(i = 0; i < len; i++) {
1353 c = url[i];
1354 if (c == '\'' || c == '\\')
1355 dbuf_putc(&cmd_buf, '\\');
1356 dbuf_putc(&cmd_buf, c);
1357 }
1358 JS_FreeCString(ctx, url);
1359 dbuf_putstr(&cmd_buf, "''");
1360 dbuf_putc(&cmd_buf, '\0');
1361 if (dbuf_error(&cmd_buf)) {
1362 dbuf_free(&cmd_buf);
1363 return JS_EXCEPTION;
1364 }
1365 // printf("%s\n", (char *)cmd_buf.buf);
1366 f = popen((char *)cmd_buf.buf, "r");
1367 dbuf_free(&cmd_buf);
1368 if (!f) {
1369 return JS_ThrowTypeError(ctx, "could not start curl");
1370 }
1371
1372 js_std_dbuf_init(ctx, data_buf);
1373 js_std_dbuf_init(ctx, header_buf);
1374
1375 buf = js_malloc(ctx, URL_GET_BUF_SIZE);
1376 if (!buf)
1377 goto fail;
1378
1379 /* get the HTTP status */
1380 if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) {
1381 status = 0;
1382 goto bad_header;
1383 }
1384 status = http_get_status(buf);
1385 if (!full_flag && !(status >= 200 && status <= 299)) {
1386 goto bad_header;
1387 }
1388
1389 /* wait until there is an empty line */
1390 for(;;) {
1391 if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) {
1392 bad_header:
1393 response = JS_NULL;
1394 goto done;
1395 }
1396 if (!strcmp(buf, "\r\n"))
1397 break;
1398 }
1399 if (dbuf_error(header_buf))
1400 goto fail;
1401 header_buf->size -= 2; /* remove the trailing CRLF */
1402
1403 /* download the data */
1404 for(;;) {
1405 len = fread(buf, 1, URL_GET_BUF_SIZE, f);
1406 if (len == 0)
1407 break;
1408 dbuf_put(data_buf, (uint8_t *)buf, len);
1409 }
1410 if (dbuf_error(data_buf))
1411 goto fail;
1412 if (binary_flag) {
1413 response = JS_NewArrayBufferCopy(ctx,
1414 data_buf->buf, data_buf->size);
1415 } else {
1416 response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size);
1417 }
1418 if (JS_IsException(response))
1419 goto fail;
1420 done:
1421 js_free(ctx, buf);
1422 buf = NULL;
1423 pclose(f);
1424 f = NULL;
1425 dbuf_free(data_buf);
1426 data_buf = NULL;
1427
1428 if (full_flag) {
1429 ret_obj = JS_NewObject(ctx);
1430 if (JS_IsException(ret_obj))
1431 goto fail;
1432 JS_DefinePropertyValueStr(ctx, ret_obj, "response",
1433 response,
1434 JS_PROP_C_W_E);
1435 if (!JS_IsNull(response)) {
1436 JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders",
1437 JS_NewStringLen(ctx, (char *)header_buf->buf,
1438 header_buf->size),
1439 JS_PROP_C_W_E);
1440 JS_DefinePropertyValueStr(ctx, ret_obj, "status",
1441 JS_NewInt32(ctx, status),
1442 JS_PROP_C_W_E);
1443 }
1444 } else {
1445 ret_obj = response;
1446 }
1447 dbuf_free(header_buf);
1448 return ret_obj;
1449 fail:
1450 if (f)
1451 pclose(f);
1452 js_free(ctx, buf);
1453 if (data_buf)
1454 dbuf_free(data_buf);
1455 if (header_buf)
1456 dbuf_free(header_buf);
1457 JS_FreeValue(ctx, response);
1458 return JS_EXCEPTION;
1459 }
1460
1461 static JSClassDef js_std_file_class = {
1462 "FILE",
1463 .finalizer = js_std_file_finalizer,
1464 };
1465
1466 static const JSCFunctionListEntry js_std_error_props[] = {
1467 /* various errno values */
1468 #define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )
1469 DEF(EINVAL),
1470 DEF(EIO),
1471 DEF(EACCES),
1472 DEF(EEXIST),
1473 DEF(ENOSPC),
1474 DEF(ENOSYS),
1475 DEF(EBUSY),
1476 DEF(ENOENT),
1477 DEF(EPERM),
1478 DEF(EPIPE),
1479 DEF(EBADF),
1480 #undef DEF
1481 };
1482
1483 static const JSCFunctionListEntry js_std_funcs[] = {
1484 JS_CFUNC_DEF("exit", 1, js_std_exit ),
1485 JS_CFUNC_DEF("gc", 0, js_std_gc ),
1486 JS_CFUNC_DEF("evalScript", 1, js_evalScript ),
1487 JS_CFUNC_DEF("loadScript", 1, js_loadScript ),
1488 JS_CFUNC_DEF("getenv", 1, js_std_getenv ),
1489 JS_CFUNC_DEF("setenv", 1, js_std_setenv ),
1490 JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ),
1491 JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ),
1492 JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),
1493 JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ),
1494 JS_CFUNC_DEF("strerror", 1, js_std_strerror ),
1495 JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ),
1496
1497 /* FILE I/O */
1498 JS_CFUNC_DEF("open", 2, js_std_open ),
1499 JS_CFUNC_DEF("popen", 2, js_std_popen ),
1500 JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ),
1501 JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ),
1502 JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ),
1503 JS_CFUNC_DEF("printf", 1, js_std_printf ),
1504 JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ),
1505 JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ),
1506 JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ),
1507 JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ),
1508 JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE),
1509 };
1510
1511 static const JSCFunctionListEntry js_std_file_proto_funcs[] = {
1512 JS_CFUNC_DEF("close", 0, js_std_file_close ),
1513 JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ),
1514 JS_CFUNC_DEF("printf", 1, js_std_file_printf ),
1515 JS_CFUNC_DEF("flush", 0, js_std_file_flush ),
1516 JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ),
1517 JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ),
1518 JS_CFUNC_DEF("seek", 2, js_std_file_seek ),
1519 JS_CFUNC_DEF("eof", 0, js_std_file_eof ),
1520 JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ),
1521 JS_CFUNC_DEF("error", 0, js_std_file_error ),
1522 JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ),
1523 JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ),
1524 JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ),
1525 JS_CFUNC_DEF("getline", 0, js_std_file_getline ),
1526 JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ),
1527 JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ),
1528 JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ),
1529 /* setvbuf, ... */
1530 };
1531
js_std_init(JSContext * ctx,JSModuleDef * m)1532 static int js_std_init(JSContext *ctx, JSModuleDef *m)
1533 {
1534 JSValue proto;
1535
1536 /* FILE class */
1537 /* the class ID is created once */
1538 JS_NewClassID(&js_std_file_class_id);
1539 /* the class is created once per runtime */
1540 JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class);
1541 proto = JS_NewObject(ctx);
1542 JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs,
1543 countof(js_std_file_proto_funcs));
1544 JS_SetClassProto(ctx, js_std_file_class_id, proto);
1545
1546 JS_SetModuleExportList(ctx, m, js_std_funcs,
1547 countof(js_std_funcs));
1548 JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));
1549 JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE));
1550 JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE));
1551 return 0;
1552 }
1553
js_init_module_std(JSContext * ctx,const char * module_name)1554 JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name)
1555 {
1556 JSModuleDef *m;
1557 m = JS_NewCModule(ctx, module_name, js_std_init);
1558 if (!m)
1559 return NULL;
1560 JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs));
1561 JS_AddModuleExport(ctx, m, "in");
1562 JS_AddModuleExport(ctx, m, "out");
1563 JS_AddModuleExport(ctx, m, "err");
1564 return m;
1565 }
1566
1567 /**********************************************************/
1568 /* 'os' object */
1569
js_os_open(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1570 static JSValue js_os_open(JSContext *ctx, JSValueConst this_val,
1571 int argc, JSValueConst *argv)
1572 {
1573 const char *filename;
1574 int flags, mode, ret;
1575
1576 filename = JS_ToCString(ctx, argv[0]);
1577 if (!filename)
1578 return JS_EXCEPTION;
1579 if (JS_ToInt32(ctx, &flags, argv[1]))
1580 goto fail;
1581 if (argc >= 3 && !JS_IsUndefined(argv[2])) {
1582 if (JS_ToInt32(ctx, &mode, argv[2])) {
1583 fail:
1584 JS_FreeCString(ctx, filename);
1585 return JS_EXCEPTION;
1586 }
1587 } else {
1588 mode = 0666;
1589 }
1590 #if defined(_WIN32)
1591 /* force binary mode by default */
1592 if (!(flags & O_TEXT))
1593 flags |= O_BINARY;
1594 #endif
1595 ret = js_get_errno(open(filename, flags, mode));
1596 JS_FreeCString(ctx, filename);
1597 return JS_NewInt32(ctx, ret);
1598 }
1599
js_os_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1600 static JSValue js_os_close(JSContext *ctx, JSValueConst this_val,
1601 int argc, JSValueConst *argv)
1602 {
1603 int fd, ret;
1604 if (JS_ToInt32(ctx, &fd, argv[0]))
1605 return JS_EXCEPTION;
1606 ret = js_get_errno(close(fd));
1607 return JS_NewInt32(ctx, ret);
1608 }
1609
js_os_seek(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1610 static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val,
1611 int argc, JSValueConst *argv)
1612 {
1613 int fd, whence;
1614 int64_t pos, ret;
1615 BOOL is_bigint;
1616
1617 if (JS_ToInt32(ctx, &fd, argv[0]))
1618 return JS_EXCEPTION;
1619 is_bigint = JS_IsBigInt(ctx, argv[1]);
1620 if (JS_ToInt64Ext(ctx, &pos, argv[1]))
1621 return JS_EXCEPTION;
1622 if (JS_ToInt32(ctx, &whence, argv[2]))
1623 return JS_EXCEPTION;
1624 ret = lseek(fd, pos, whence);
1625 if (ret == -1)
1626 ret = -errno;
1627 if (is_bigint)
1628 return JS_NewBigInt64(ctx, ret);
1629 else
1630 return JS_NewInt64(ctx, ret);
1631 }
1632
js_os_read_write(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)1633 static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val,
1634 int argc, JSValueConst *argv, int magic)
1635 {
1636 int fd;
1637 uint64_t pos, len;
1638 size_t size;
1639 ssize_t ret;
1640 uint8_t *buf;
1641
1642 if (JS_ToInt32(ctx, &fd, argv[0]))
1643 return JS_EXCEPTION;
1644 if (JS_ToIndex(ctx, &pos, argv[2]))
1645 return JS_EXCEPTION;
1646 if (JS_ToIndex(ctx, &len, argv[3]))
1647 return JS_EXCEPTION;
1648 buf = JS_GetArrayBuffer(ctx, &size, argv[1]);
1649 if (!buf)
1650 return JS_EXCEPTION;
1651 if (pos + len > size)
1652 return JS_ThrowRangeError(ctx, "read/write array buffer overflow");
1653 if (magic)
1654 ret = js_get_errno(write(fd, buf + pos, len));
1655 else
1656 ret = js_get_errno(read(fd, buf + pos, len));
1657 return JS_NewInt64(ctx, ret);
1658 }
1659
js_os_isatty(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1660 static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,
1661 int argc, JSValueConst *argv)
1662 {
1663 int fd;
1664 if (JS_ToInt32(ctx, &fd, argv[0]))
1665 return JS_EXCEPTION;
1666 return JS_NewBool(ctx, isatty(fd) == 1);
1667 }
1668
1669 #if defined(_WIN32)
js_os_ttyGetWinSize(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1670 static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,
1671 int argc, JSValueConst *argv)
1672 {
1673 int fd;
1674 HANDLE handle;
1675 CONSOLE_SCREEN_BUFFER_INFO info;
1676 JSValue obj;
1677
1678 if (JS_ToInt32(ctx, &fd, argv[0]))
1679 return JS_EXCEPTION;
1680 handle = (HANDLE)_get_osfhandle(fd);
1681
1682 if (!GetConsoleScreenBufferInfo(handle, &info))
1683 return JS_NULL;
1684 obj = JS_NewArray(ctx);
1685 if (JS_IsException(obj))
1686 return obj;
1687 JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E);
1688 JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E);
1689 return obj;
1690 }
1691
js_os_ttySetRaw(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1692 static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
1693 int argc, JSValueConst *argv)
1694 {
1695 int fd;
1696 HANDLE handle;
1697
1698 if (JS_ToInt32(ctx, &fd, argv[0]))
1699 return JS_EXCEPTION;
1700 handle = (HANDLE)_get_osfhandle(fd);
1701
1702 SetConsoleMode(handle, ENABLE_WINDOW_INPUT);
1703 return JS_UNDEFINED;
1704 }
1705 #else
js_os_ttyGetWinSize(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1706 static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val,
1707 int argc, JSValueConst *argv)
1708 {
1709 int fd;
1710 struct winsize ws;
1711 JSValue obj;
1712
1713 if (JS_ToInt32(ctx, &fd, argv[0]))
1714 return JS_EXCEPTION;
1715 if (ioctl(fd, TIOCGWINSZ, &ws) == 0 &&
1716 ws.ws_col >= 4 && ws.ws_row >= 4) {
1717 obj = JS_NewArray(ctx);
1718 if (JS_IsException(obj))
1719 return obj;
1720 JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E);
1721 JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E);
1722 return obj;
1723 } else {
1724 return JS_NULL;
1725 }
1726 }
1727
1728 static struct termios oldtty;
1729
term_exit(void)1730 static void term_exit(void)
1731 {
1732 tcsetattr(0, TCSANOW, &oldtty);
1733 }
1734
1735 /* XXX: should add a way to go back to normal mode */
js_os_ttySetRaw(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1736 static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
1737 int argc, JSValueConst *argv)
1738 {
1739 struct termios tty;
1740 int fd;
1741
1742 if (JS_ToInt32(ctx, &fd, argv[0]))
1743 return JS_EXCEPTION;
1744
1745 memset(&tty, 0, sizeof(tty));
1746 tcgetattr(fd, &tty);
1747 oldtty = tty;
1748
1749 tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
1750 |INLCR|IGNCR|ICRNL|IXON);
1751 tty.c_oflag |= OPOST;
1752 tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
1753 tty.c_cflag &= ~(CSIZE|PARENB);
1754 tty.c_cflag |= CS8;
1755 tty.c_cc[VMIN] = 1;
1756 tty.c_cc[VTIME] = 0;
1757
1758 tcsetattr(fd, TCSANOW, &tty);
1759
1760 atexit(term_exit);
1761 return JS_UNDEFINED;
1762 }
1763
1764 #endif /* !_WIN32 */
1765
js_os_remove(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1766 static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val,
1767 int argc, JSValueConst *argv)
1768 {
1769 const char *filename;
1770 int ret;
1771
1772 filename = JS_ToCString(ctx, argv[0]);
1773 if (!filename)
1774 return JS_EXCEPTION;
1775 ret = js_get_errno(remove(filename));
1776 JS_FreeCString(ctx, filename);
1777 return JS_NewInt32(ctx, ret);
1778 }
1779
js_os_rename(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1780 static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val,
1781 int argc, JSValueConst *argv)
1782 {
1783 const char *oldpath, *newpath;
1784 int ret;
1785
1786 oldpath = JS_ToCString(ctx, argv[0]);
1787 if (!oldpath)
1788 return JS_EXCEPTION;
1789 newpath = JS_ToCString(ctx, argv[1]);
1790 if (!newpath) {
1791 JS_FreeCString(ctx, oldpath);
1792 return JS_EXCEPTION;
1793 }
1794 ret = js_get_errno(rename(oldpath, newpath));
1795 JS_FreeCString(ctx, oldpath);
1796 JS_FreeCString(ctx, newpath);
1797 return JS_NewInt32(ctx, ret);
1798 }
1799
is_main_thread(JSRuntime * rt)1800 static BOOL is_main_thread(JSRuntime *rt)
1801 {
1802 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
1803 return !ts->recv_pipe;
1804 }
1805
find_rh(JSThreadState * ts,int fd)1806 static JSOSRWHandler *find_rh(JSThreadState *ts, int fd)
1807 {
1808 JSOSRWHandler *rh;
1809 struct list_head *el;
1810
1811 list_for_each(el, &ts->os_rw_handlers) {
1812 rh = list_entry(el, JSOSRWHandler, link);
1813 if (rh->fd == fd)
1814 return rh;
1815 }
1816 return NULL;
1817 }
1818
free_rw_handler(JSRuntime * rt,JSOSRWHandler * rh)1819 static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh)
1820 {
1821 int i;
1822 list_del(&rh->link);
1823 for(i = 0; i < 2; i++) {
1824 JS_FreeValueRT(rt, rh->rw_func[i]);
1825 }
1826 js_free_rt(rt, rh);
1827 }
1828
js_os_setReadHandler(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int magic)1829 static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val,
1830 int argc, JSValueConst *argv, int magic)
1831 {
1832 JSRuntime *rt = JS_GetRuntime(ctx);
1833 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
1834 JSOSRWHandler *rh;
1835 int fd;
1836 JSValueConst func;
1837
1838 if (JS_ToInt32(ctx, &fd, argv[0]))
1839 return JS_EXCEPTION;
1840 func = argv[1];
1841 if (JS_IsNull(func)) {
1842 rh = find_rh(ts, fd);
1843 if (rh) {
1844 JS_FreeValue(ctx, rh->rw_func[magic]);
1845 rh->rw_func[magic] = JS_NULL;
1846 if (JS_IsNull(rh->rw_func[0]) &&
1847 JS_IsNull(rh->rw_func[1])) {
1848 /* remove the entry */
1849 free_rw_handler(JS_GetRuntime(ctx), rh);
1850 }
1851 }
1852 } else {
1853 if (!JS_IsFunction(ctx, func))
1854 return JS_ThrowTypeError(ctx, "not a function");
1855 rh = find_rh(ts, fd);
1856 if (!rh) {
1857 rh = js_mallocz(ctx, sizeof(*rh));
1858 if (!rh)
1859 return JS_EXCEPTION;
1860 rh->fd = fd;
1861 rh->rw_func[0] = JS_NULL;
1862 rh->rw_func[1] = JS_NULL;
1863 list_add_tail(&rh->link, &ts->os_rw_handlers);
1864 }
1865 JS_FreeValue(ctx, rh->rw_func[magic]);
1866 rh->rw_func[magic] = JS_DupValue(ctx, func);
1867 }
1868 return JS_UNDEFINED;
1869 }
1870
find_sh(JSThreadState * ts,int sig_num)1871 static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num)
1872 {
1873 JSOSSignalHandler *sh;
1874 struct list_head *el;
1875 list_for_each(el, &ts->os_signal_handlers) {
1876 sh = list_entry(el, JSOSSignalHandler, link);
1877 if (sh->sig_num == sig_num)
1878 return sh;
1879 }
1880 return NULL;
1881 }
1882
free_sh(JSRuntime * rt,JSOSSignalHandler * sh)1883 static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh)
1884 {
1885 list_del(&sh->link);
1886 JS_FreeValueRT(rt, sh->func);
1887 js_free_rt(rt, sh);
1888 }
1889
os_signal_handler(int sig_num)1890 static void os_signal_handler(int sig_num)
1891 {
1892 os_pending_signals |= ((uint64_t)1 << sig_num);
1893 }
1894
1895 #if defined(_WIN32)
1896 typedef void (*sighandler_t)(int sig_num);
1897 #endif
1898
js_os_signal(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1899 static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val,
1900 int argc, JSValueConst *argv)
1901 {
1902 JSRuntime *rt = JS_GetRuntime(ctx);
1903 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
1904 JSOSSignalHandler *sh;
1905 uint32_t sig_num;
1906 JSValueConst func;
1907 sighandler_t handler;
1908
1909 if (!is_main_thread(rt))
1910 return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread");
1911
1912 if (JS_ToUint32(ctx, &sig_num, argv[0]))
1913 return JS_EXCEPTION;
1914 if (sig_num >= 64)
1915 return JS_ThrowRangeError(ctx, "invalid signal number");
1916 func = argv[1];
1917 /* func = null: SIG_DFL, func = undefined, SIG_IGN */
1918 if (JS_IsNull(func) || JS_IsUndefined(func)) {
1919 sh = find_sh(ts, sig_num);
1920 if (sh) {
1921 free_sh(JS_GetRuntime(ctx), sh);
1922 }
1923 if (JS_IsNull(func))
1924 handler = SIG_DFL;
1925 else
1926 handler = SIG_IGN;
1927 signal(sig_num, handler);
1928 } else {
1929 if (!JS_IsFunction(ctx, func))
1930 return JS_ThrowTypeError(ctx, "not a function");
1931 sh = find_sh(ts, sig_num);
1932 if (!sh) {
1933 sh = js_mallocz(ctx, sizeof(*sh));
1934 if (!sh)
1935 return JS_EXCEPTION;
1936 sh->sig_num = sig_num;
1937 list_add_tail(&sh->link, &ts->os_signal_handlers);
1938 }
1939 JS_FreeValue(ctx, sh->func);
1940 sh->func = JS_DupValue(ctx, func);
1941 signal(sig_num, os_signal_handler);
1942 }
1943 return JS_UNDEFINED;
1944 }
1945
1946 #if defined(__linux__) || defined(__APPLE__)
get_time_ms(void)1947 static int64_t get_time_ms(void)
1948 {
1949 struct timespec ts;
1950 clock_gettime(CLOCK_MONOTONIC, &ts);
1951 return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
1952 }
1953 #else
1954 /* more portable, but does not work if the date is updated */
get_time_ms(void)1955 static int64_t get_time_ms(void)
1956 {
1957 struct timeval tv;
1958 gettimeofday(&tv, NULL);
1959 return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
1960 }
1961 #endif
1962
unlink_timer(JSRuntime * rt,JSOSTimer * th)1963 static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
1964 {
1965 if (th->link.prev) {
1966 list_del(&th->link);
1967 th->link.prev = th->link.next = NULL;
1968 }
1969 }
1970
free_timer(JSRuntime * rt,JSOSTimer * th)1971 static void free_timer(JSRuntime *rt, JSOSTimer *th)
1972 {
1973 JS_FreeValueRT(rt, th->func);
1974 js_free_rt(rt, th);
1975 }
1976
1977 static JSClassID js_os_timer_class_id;
1978
js_os_timer_finalizer(JSRuntime * rt,JSValue val)1979 static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
1980 {
1981 JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
1982 if (th) {
1983 th->has_object = FALSE;
1984 if (!th->link.prev)
1985 free_timer(rt, th);
1986 }
1987 }
1988
js_os_timer_mark(JSRuntime * rt,JSValueConst val,JS_MarkFunc * mark_func)1989 static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
1990 JS_MarkFunc *mark_func)
1991 {
1992 JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
1993 if (th) {
1994 JS_MarkValue(rt, th->func, mark_func);
1995 }
1996 }
1997
js_os_setTimeout(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)1998 static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
1999 int argc, JSValueConst *argv)
2000 {
2001 JSRuntime *rt = JS_GetRuntime(ctx);
2002 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
2003 int64_t delay;
2004 JSValueConst func;
2005 JSOSTimer *th;
2006 JSValue obj;
2007
2008 func = argv[0];
2009 if (!JS_IsFunction(ctx, func))
2010 return JS_ThrowTypeError(ctx, "not a function");
2011 if (JS_ToInt64(ctx, &delay, argv[1]))
2012 return JS_EXCEPTION;
2013 obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
2014 if (JS_IsException(obj))
2015 return obj;
2016 th = js_mallocz(ctx, sizeof(*th));
2017 if (!th) {
2018 JS_FreeValue(ctx, obj);
2019 return JS_EXCEPTION;
2020 }
2021 th->has_object = TRUE;
2022 th->timeout = get_time_ms() + delay;
2023 th->func = JS_DupValue(ctx, func);
2024 list_add_tail(&th->link, &ts->os_timers);
2025 JS_SetOpaque(obj, th);
2026 return obj;
2027 }
2028
js_os_clearTimeout(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2029 static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
2030 int argc, JSValueConst *argv)
2031 {
2032 JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);
2033 if (!th)
2034 return JS_EXCEPTION;
2035 unlink_timer(JS_GetRuntime(ctx), th);
2036 return JS_UNDEFINED;
2037 }
2038
2039 static JSClassDef js_os_timer_class = {
2040 "OSTimer",
2041 .finalizer = js_os_timer_finalizer,
2042 .gc_mark = js_os_timer_mark,
2043 };
2044
call_handler(JSContext * ctx,JSValueConst func)2045 static void call_handler(JSContext *ctx, JSValueConst func)
2046 {
2047 JSValue ret, func1;
2048 /* 'func' might be destroyed when calling itself (if it frees the
2049 handler), so must take extra care */
2050 func1 = JS_DupValue(ctx, func);
2051 ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);
2052 JS_FreeValue(ctx, func1);
2053 if (JS_IsException(ret))
2054 js_std_dump_error(ctx);
2055 JS_FreeValue(ctx, ret);
2056 }
2057
2058 #if defined(_WIN32)
2059
js_os_poll(JSContext * ctx)2060 static int js_os_poll(JSContext *ctx)
2061 {
2062 JSRuntime *rt = JS_GetRuntime(ctx);
2063 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
2064 int min_delay, console_fd;
2065 int64_t cur_time, delay;
2066 JSOSRWHandler *rh;
2067 struct list_head *el;
2068
2069 /* XXX: handle signals if useful */
2070
2071 if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers))
2072 return -1; /* no more events */
2073
2074 /* XXX: only timers and basic console input are supported */
2075 if (!list_empty(&ts->os_timers)) {
2076 cur_time = get_time_ms();
2077 min_delay = 10000;
2078 list_for_each(el, &ts->os_timers) {
2079 JSOSTimer *th = list_entry(el, JSOSTimer, link);
2080 delay = th->timeout - cur_time;
2081 if (delay <= 0) {
2082 JSValue func;
2083 /* the timer expired */
2084 func = th->func;
2085 th->func = JS_UNDEFINED;
2086 unlink_timer(rt, th);
2087 if (!th->has_object)
2088 free_timer(rt, th);
2089 call_handler(ctx, func);
2090 JS_FreeValue(ctx, func);
2091 return 0;
2092 } else if (delay < min_delay) {
2093 min_delay = delay;
2094 }
2095 }
2096 } else {
2097 min_delay = -1;
2098 }
2099
2100 console_fd = -1;
2101 list_for_each(el, &ts->os_rw_handlers) {
2102 rh = list_entry(el, JSOSRWHandler, link);
2103 if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
2104 console_fd = rh->fd;
2105 break;
2106 }
2107 }
2108
2109 if (console_fd >= 0) {
2110 DWORD ti, ret;
2111 HANDLE handle;
2112 if (min_delay == -1)
2113 ti = INFINITE;
2114 else
2115 ti = min_delay;
2116 handle = (HANDLE)_get_osfhandle(console_fd);
2117 ret = WaitForSingleObject(handle, ti);
2118 if (ret == WAIT_OBJECT_0) {
2119 list_for_each(el, &ts->os_rw_handlers) {
2120 rh = list_entry(el, JSOSRWHandler, link);
2121 if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
2122 call_handler(ctx, rh->rw_func[0]);
2123 /* must stop because the list may have been modified */
2124 break;
2125 }
2126 }
2127 }
2128 } else {
2129 Sleep(min_delay);
2130 }
2131 return 0;
2132 }
2133 #else
2134
2135 #ifdef USE_WORKER
2136
2137 static void js_free_message(JSWorkerMessage *msg);
2138
2139 /* return 1 if a message was handled, 0 if no message */
handle_posted_message(JSRuntime * rt,JSContext * ctx,JSWorkerMessageHandler * port)2140 static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
2141 JSWorkerMessageHandler *port)
2142 {
2143 JSWorkerMessagePipe *ps = port->recv_pipe;
2144 int ret;
2145 struct list_head *el;
2146 JSWorkerMessage *msg;
2147 JSValue obj, data_obj, func, retval;
2148
2149 pthread_mutex_lock(&ps->mutex);
2150 if (!list_empty(&ps->msg_queue)) {
2151 el = ps->msg_queue.next;
2152 msg = list_entry(el, JSWorkerMessage, link);
2153
2154 /* remove the message from the queue */
2155 list_del(&msg->link);
2156
2157 if (list_empty(&ps->msg_queue)) {
2158 uint8_t buf[16];
2159 int ret;
2160 for(;;) {
2161 ret = read(ps->read_fd, buf, sizeof(buf));
2162 if (ret >= 0)
2163 break;
2164 if (errno != EAGAIN && errno != EINTR)
2165 break;
2166 }
2167 }
2168
2169 pthread_mutex_unlock(&ps->mutex);
2170
2171 data_obj = JS_ReadObject(ctx, msg->data, msg->data_len,
2172 JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE);
2173
2174 js_free_message(msg);
2175
2176 if (JS_IsException(data_obj))
2177 goto fail;
2178 obj = JS_NewObject(ctx);
2179 if (JS_IsException(obj)) {
2180 JS_FreeValue(ctx, data_obj);
2181 goto fail;
2182 }
2183 JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E);
2184
2185 /* 'func' might be destroyed when calling itself (if it frees the
2186 handler), so must take extra care */
2187 func = JS_DupValue(ctx, port->on_message_func);
2188 retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj);
2189 JS_FreeValue(ctx, obj);
2190 JS_FreeValue(ctx, func);
2191 if (JS_IsException(retval)) {
2192 fail:
2193 js_std_dump_error(ctx);
2194 } else {
2195 JS_FreeValue(ctx, retval);
2196 }
2197 ret = 1;
2198 } else {
2199 pthread_mutex_unlock(&ps->mutex);
2200 ret = 0;
2201 }
2202 return ret;
2203 }
2204 #else
handle_posted_message(JSRuntime * rt,JSContext * ctx,JSWorkerMessageHandler * port)2205 static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
2206 JSWorkerMessageHandler *port)
2207 {
2208 return 0;
2209 }
2210 #endif
2211
js_os_poll(JSContext * ctx)2212 static int js_os_poll(JSContext *ctx)
2213 {
2214 JSRuntime *rt = JS_GetRuntime(ctx);
2215 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
2216 int ret, fd_max, min_delay;
2217 int64_t cur_time, delay;
2218 fd_set rfds, wfds;
2219 JSOSRWHandler *rh;
2220 struct list_head *el;
2221 struct timeval tv, *tvp;
2222
2223 /* only check signals in the main thread */
2224 if (!ts->recv_pipe &&
2225 unlikely(os_pending_signals != 0)) {
2226 JSOSSignalHandler *sh;
2227 uint64_t mask;
2228
2229 list_for_each(el, &ts->os_signal_handlers) {
2230 sh = list_entry(el, JSOSSignalHandler, link);
2231 mask = (uint64_t)1 << sh->sig_num;
2232 if (os_pending_signals & mask) {
2233 os_pending_signals &= ~mask;
2234 call_handler(ctx, sh->func);
2235 return 0;
2236 }
2237 }
2238 }
2239
2240 if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&
2241 list_empty(&ts->port_list))
2242 return -1; /* no more events */
2243
2244 if (!list_empty(&ts->os_timers)) {
2245 cur_time = get_time_ms();
2246 min_delay = 10000;
2247 list_for_each(el, &ts->os_timers) {
2248 JSOSTimer *th = list_entry(el, JSOSTimer, link);
2249 delay = th->timeout - cur_time;
2250 if (delay <= 0) {
2251 JSValue func;
2252 /* the timer expired */
2253 func = th->func;
2254 th->func = JS_UNDEFINED;
2255 unlink_timer(rt, th);
2256 if (!th->has_object)
2257 free_timer(rt, th);
2258 call_handler(ctx, func);
2259 JS_FreeValue(ctx, func);
2260 return 0;
2261 } else if (delay < min_delay) {
2262 min_delay = delay;
2263 }
2264 }
2265 tv.tv_sec = min_delay / 1000;
2266 tv.tv_usec = (min_delay % 1000) * 1000;
2267 tvp = &tv;
2268 } else {
2269 tvp = NULL;
2270 }
2271
2272 FD_ZERO(&rfds);
2273 FD_ZERO(&wfds);
2274 fd_max = -1;
2275 list_for_each(el, &ts->os_rw_handlers) {
2276 rh = list_entry(el, JSOSRWHandler, link);
2277 fd_max = max_int(fd_max, rh->fd);
2278 if (!JS_IsNull(rh->rw_func[0]))
2279 FD_SET(rh->fd, &rfds);
2280 if (!JS_IsNull(rh->rw_func[1]))
2281 FD_SET(rh->fd, &wfds);
2282 }
2283
2284 list_for_each(el, &ts->port_list) {
2285 JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
2286 if (!JS_IsNull(port->on_message_func)) {
2287 JSWorkerMessagePipe *ps = port->recv_pipe;
2288 fd_max = max_int(fd_max, ps->read_fd);
2289 FD_SET(ps->read_fd, &rfds);
2290 }
2291 }
2292
2293 ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);
2294 if (ret > 0) {
2295 list_for_each(el, &ts->os_rw_handlers) {
2296 rh = list_entry(el, JSOSRWHandler, link);
2297 if (!JS_IsNull(rh->rw_func[0]) &&
2298 FD_ISSET(rh->fd, &rfds)) {
2299 call_handler(ctx, rh->rw_func[0]);
2300 /* must stop because the list may have been modified */
2301 goto done;
2302 }
2303 if (!JS_IsNull(rh->rw_func[1]) &&
2304 FD_ISSET(rh->fd, &wfds)) {
2305 call_handler(ctx, rh->rw_func[1]);
2306 /* must stop because the list may have been modified */
2307 goto done;
2308 }
2309 }
2310
2311 list_for_each(el, &ts->port_list) {
2312 JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
2313 if (!JS_IsNull(port->on_message_func)) {
2314 JSWorkerMessagePipe *ps = port->recv_pipe;
2315 if (FD_ISSET(ps->read_fd, &rfds)) {
2316 if (handle_posted_message(rt, ctx, port))
2317 goto done;
2318 }
2319 }
2320 }
2321 }
2322 done:
2323 return 0;
2324 }
2325 #endif /* !_WIN32 */
2326
make_obj_error(JSContext * ctx,JSValue obj,int err)2327 static JSValue make_obj_error(JSContext *ctx,
2328 JSValue obj,
2329 int err)
2330 {
2331 JSValue arr;
2332 if (JS_IsException(obj))
2333 return obj;
2334 arr = JS_NewArray(ctx);
2335 if (JS_IsException(arr))
2336 return JS_EXCEPTION;
2337 JS_DefinePropertyValueUint32(ctx, arr, 0, obj,
2338 JS_PROP_C_W_E);
2339 JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err),
2340 JS_PROP_C_W_E);
2341 return arr;
2342 }
2343
make_string_error(JSContext * ctx,const char * buf,int err)2344 static JSValue make_string_error(JSContext *ctx,
2345 const char *buf,
2346 int err)
2347 {
2348 return make_obj_error(ctx, JS_NewString(ctx, buf), err);
2349 }
2350
2351 /* return [cwd, errorcode] */
js_os_getcwd(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2352 static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val,
2353 int argc, JSValueConst *argv)
2354 {
2355 char buf[PATH_MAX];
2356 int err;
2357
2358 if (!getcwd(buf, sizeof(buf))) {
2359 buf[0] = '\0';
2360 err = errno;
2361 } else {
2362 err = 0;
2363 }
2364 return make_string_error(ctx, buf, err);
2365 }
2366
js_os_chdir(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2367 static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val,
2368 int argc, JSValueConst *argv)
2369 {
2370 const char *target;
2371 int err;
2372
2373 target = JS_ToCString(ctx, argv[0]);
2374 if (!target)
2375 return JS_EXCEPTION;
2376 err = js_get_errno(chdir(target));
2377 JS_FreeCString(ctx, target);
2378 return JS_NewInt32(ctx, err);
2379 }
2380
js_os_mkdir(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2381 static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val,
2382 int argc, JSValueConst *argv)
2383 {
2384 int mode, ret;
2385 const char *path;
2386
2387 if (argc >= 2) {
2388 if (JS_ToInt32(ctx, &mode, argv[1]))
2389 return JS_EXCEPTION;
2390 } else {
2391 mode = 0777;
2392 }
2393 path = JS_ToCString(ctx, argv[0]);
2394 if (!path)
2395 return JS_EXCEPTION;
2396 #if defined(_WIN32)
2397 (void)mode;
2398 ret = js_get_errno(mkdir(path));
2399 #else
2400 ret = js_get_errno(mkdir(path, mode));
2401 #endif
2402 JS_FreeCString(ctx, path);
2403 return JS_NewInt32(ctx, ret);
2404 }
2405
2406 /* return [array, errorcode] */
js_os_readdir(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2407 static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val,
2408 int argc, JSValueConst *argv)
2409 {
2410 const char *path;
2411 DIR *f;
2412 struct dirent *d;
2413 JSValue obj;
2414 int err;
2415 uint32_t len;
2416
2417 path = JS_ToCString(ctx, argv[0]);
2418 if (!path)
2419 return JS_EXCEPTION;
2420 obj = JS_NewArray(ctx);
2421 if (JS_IsException(obj)) {
2422 JS_FreeCString(ctx, path);
2423 return JS_EXCEPTION;
2424 }
2425 f = opendir(path);
2426 if (!f)
2427 err = errno;
2428 else
2429 err = 0;
2430 JS_FreeCString(ctx, path);
2431 if (!f)
2432 goto done;
2433 len = 0;
2434 for(;;) {
2435 errno = 0;
2436 d = readdir(f);
2437 if (!d) {
2438 err = errno;
2439 break;
2440 }
2441 JS_DefinePropertyValueUint32(ctx, obj, len++,
2442 JS_NewString(ctx, d->d_name),
2443 JS_PROP_C_W_E);
2444 }
2445 closedir(f);
2446 done:
2447 return make_obj_error(ctx, obj, err);
2448 }
2449
2450 #if !defined(_WIN32)
timespec_to_ms(const struct timespec * tv)2451 static int64_t timespec_to_ms(const struct timespec *tv)
2452 {
2453 return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000);
2454 }
2455 #endif
2456
2457 /* return [obj, errcode] */
js_os_stat(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv,int is_lstat)2458 static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
2459 int argc, JSValueConst *argv, int is_lstat)
2460 {
2461 const char *path;
2462 int err, res;
2463 struct stat st;
2464 JSValue obj;
2465
2466 path = JS_ToCString(ctx, argv[0]);
2467 if (!path)
2468 return JS_EXCEPTION;
2469 #if defined(_WIN32)
2470 res = stat(path, &st);
2471 #else
2472 if (is_lstat)
2473 res = lstat(path, &st);
2474 else
2475 res = stat(path, &st);
2476 #endif
2477 JS_FreeCString(ctx, path);
2478 if (res < 0) {
2479 err = errno;
2480 obj = JS_NULL;
2481 } else {
2482 err = 0;
2483 obj = JS_NewObject(ctx);
2484 if (JS_IsException(obj))
2485 return JS_EXCEPTION;
2486 JS_DefinePropertyValueStr(ctx, obj, "dev",
2487 JS_NewInt64(ctx, st.st_dev),
2488 JS_PROP_C_W_E);
2489 JS_DefinePropertyValueStr(ctx, obj, "ino",
2490 JS_NewInt64(ctx, st.st_ino),
2491 JS_PROP_C_W_E);
2492 JS_DefinePropertyValueStr(ctx, obj, "mode",
2493 JS_NewInt32(ctx, st.st_mode),
2494 JS_PROP_C_W_E);
2495 JS_DefinePropertyValueStr(ctx, obj, "nlink",
2496 JS_NewInt64(ctx, st.st_nlink),
2497 JS_PROP_C_W_E);
2498 JS_DefinePropertyValueStr(ctx, obj, "uid",
2499 JS_NewInt64(ctx, st.st_uid),
2500 JS_PROP_C_W_E);
2501 JS_DefinePropertyValueStr(ctx, obj, "gid",
2502 JS_NewInt64(ctx, st.st_gid),
2503 JS_PROP_C_W_E);
2504 JS_DefinePropertyValueStr(ctx, obj, "rdev",
2505 JS_NewInt64(ctx, st.st_rdev),
2506 JS_PROP_C_W_E);
2507 JS_DefinePropertyValueStr(ctx, obj, "size",
2508 JS_NewInt64(ctx, st.st_size),
2509 JS_PROP_C_W_E);
2510 #if !defined(_WIN32)
2511 JS_DefinePropertyValueStr(ctx, obj, "blocks",
2512 JS_NewInt64(ctx, st.st_blocks),
2513 JS_PROP_C_W_E);
2514 #endif
2515 #if defined(_WIN32)
2516 JS_DefinePropertyValueStr(ctx, obj, "atime",
2517 JS_NewInt64(ctx, (int64_t)st.st_atime * 1000),
2518 JS_PROP_C_W_E);
2519 JS_DefinePropertyValueStr(ctx, obj, "mtime",
2520 JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000),
2521 JS_PROP_C_W_E);
2522 JS_DefinePropertyValueStr(ctx, obj, "ctime",
2523 JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000),
2524 JS_PROP_C_W_E);
2525 #elif defined(__APPLE__)
2526 JS_DefinePropertyValueStr(ctx, obj, "atime",
2527 JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)),
2528 JS_PROP_C_W_E);
2529 JS_DefinePropertyValueStr(ctx, obj, "mtime",
2530 JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)),
2531 JS_PROP_C_W_E);
2532 JS_DefinePropertyValueStr(ctx, obj, "ctime",
2533 JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)),
2534 JS_PROP_C_W_E);
2535 #else
2536 JS_DefinePropertyValueStr(ctx, obj, "atime",
2537 JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)),
2538 JS_PROP_C_W_E);
2539 JS_DefinePropertyValueStr(ctx, obj, "mtime",
2540 JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)),
2541 JS_PROP_C_W_E);
2542 JS_DefinePropertyValueStr(ctx, obj, "ctime",
2543 JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)),
2544 JS_PROP_C_W_E);
2545 #endif
2546 }
2547 return make_obj_error(ctx, obj, err);
2548 }
2549
2550 #if !defined(_WIN32)
ms_to_timeval(struct timeval * tv,uint64_t v)2551 static void ms_to_timeval(struct timeval *tv, uint64_t v)
2552 {
2553 tv->tv_sec = v / 1000;
2554 tv->tv_usec = (v % 1000) * 1000;
2555 }
2556 #endif
2557
js_os_utimes(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2558 static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
2559 int argc, JSValueConst *argv)
2560 {
2561 const char *path;
2562 int64_t atime, mtime;
2563 int ret;
2564
2565 if (JS_ToInt64(ctx, &atime, argv[1]))
2566 return JS_EXCEPTION;
2567 if (JS_ToInt64(ctx, &mtime, argv[2]))
2568 return JS_EXCEPTION;
2569 path = JS_ToCString(ctx, argv[0]);
2570 if (!path)
2571 return JS_EXCEPTION;
2572 #if defined(_WIN32)
2573 {
2574 struct _utimbuf times;
2575 times.actime = atime / 1000;
2576 times.modtime = mtime / 1000;
2577 ret = js_get_errno(_utime(path, ×));
2578 }
2579 #else
2580 {
2581 struct timeval times[2];
2582 ms_to_timeval(×[0], atime);
2583 ms_to_timeval(×[1], mtime);
2584 ret = js_get_errno(utimes(path, times));
2585 }
2586 #endif
2587 JS_FreeCString(ctx, path);
2588 return JS_NewInt32(ctx, ret);
2589 }
2590
2591 #if !defined(_WIN32)
2592
2593 /* return [path, errorcode] */
js_os_realpath(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2594 static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
2595 int argc, JSValueConst *argv)
2596 {
2597 const char *path;
2598 char buf[PATH_MAX], *res;
2599 int err;
2600
2601 path = JS_ToCString(ctx, argv[0]);
2602 if (!path)
2603 return JS_EXCEPTION;
2604 res = realpath(path, buf);
2605 JS_FreeCString(ctx, path);
2606 if (!res) {
2607 buf[0] = '\0';
2608 err = errno;
2609 } else {
2610 err = 0;
2611 }
2612 return make_string_error(ctx, buf, err);
2613 }
2614
js_os_symlink(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2615 static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
2616 int argc, JSValueConst *argv)
2617 {
2618 const char *target, *linkpath;
2619 int err;
2620
2621 target = JS_ToCString(ctx, argv[0]);
2622 if (!target)
2623 return JS_EXCEPTION;
2624 linkpath = JS_ToCString(ctx, argv[1]);
2625 if (!linkpath) {
2626 JS_FreeCString(ctx, target);
2627 return JS_EXCEPTION;
2628 }
2629 err = js_get_errno(symlink(target, linkpath));
2630 JS_FreeCString(ctx, target);
2631 JS_FreeCString(ctx, linkpath);
2632 return JS_NewInt32(ctx, err);
2633 }
2634
2635 /* return [path, errorcode] */
js_os_readlink(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2636 static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val,
2637 int argc, JSValueConst *argv)
2638 {
2639 const char *path;
2640 char buf[PATH_MAX];
2641 int err;
2642 ssize_t res;
2643
2644 path = JS_ToCString(ctx, argv[0]);
2645 if (!path)
2646 return JS_EXCEPTION;
2647 res = readlink(path, buf, sizeof(buf) - 1);
2648 if (res < 0) {
2649 buf[0] = '\0';
2650 err = errno;
2651 } else {
2652 buf[res] = '\0';
2653 err = 0;
2654 }
2655 JS_FreeCString(ctx, path);
2656 return make_string_error(ctx, buf, err);
2657 }
2658
build_envp(JSContext * ctx,JSValueConst obj)2659 static char **build_envp(JSContext *ctx, JSValueConst obj)
2660 {
2661 uint32_t len, i;
2662 JSPropertyEnum *tab;
2663 char **envp, *pair;
2664 const char *key, *str;
2665 JSValue val;
2666 size_t key_len, str_len;
2667
2668 if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj,
2669 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0)
2670 return NULL;
2671 envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1));
2672 if (!envp)
2673 goto fail;
2674 for(i = 0; i < len; i++) {
2675 val = JS_GetProperty(ctx, obj, tab[i].atom);
2676 if (JS_IsException(val))
2677 goto fail;
2678 str = JS_ToCString(ctx, val);
2679 JS_FreeValue(ctx, val);
2680 if (!str)
2681 goto fail;
2682 key = JS_AtomToCString(ctx, tab[i].atom);
2683 if (!key) {
2684 JS_FreeCString(ctx, str);
2685 goto fail;
2686 }
2687 key_len = strlen(key);
2688 str_len = strlen(str);
2689 pair = js_malloc(ctx, key_len + str_len + 2);
2690 if (!pair) {
2691 JS_FreeCString(ctx, key);
2692 JS_FreeCString(ctx, str);
2693 goto fail;
2694 }
2695 memcpy(pair, key, key_len);
2696 pair[key_len] = '=';
2697 memcpy(pair + key_len + 1, str, str_len);
2698 pair[key_len + 1 + str_len] = '\0';
2699 envp[i] = pair;
2700 JS_FreeCString(ctx, key);
2701 JS_FreeCString(ctx, str);
2702 }
2703 done:
2704 for(i = 0; i < len; i++)
2705 JS_FreeAtom(ctx, tab[i].atom);
2706 js_free(ctx, tab);
2707 return envp;
2708 fail:
2709 if (envp) {
2710 for(i = 0; i < len; i++)
2711 js_free(ctx, envp[i]);
2712 js_free(ctx, envp);
2713 envp = NULL;
2714 }
2715 goto done;
2716 }
2717
2718 /* execvpe is not available on non GNU systems */
my_execvpe(const char * filename,char ** argv,char ** envp)2719 static int my_execvpe(const char *filename, char **argv, char **envp)
2720 {
2721 char *path, *p, *p_next, *p1;
2722 char buf[PATH_MAX];
2723 size_t filename_len, path_len;
2724 BOOL eacces_error;
2725
2726 filename_len = strlen(filename);
2727 if (filename_len == 0) {
2728 errno = ENOENT;
2729 return -1;
2730 }
2731 if (strchr(filename, '/'))
2732 return execve(filename, argv, envp);
2733
2734 path = getenv("PATH");
2735 if (!path)
2736 path = (char *)"/bin:/usr/bin";
2737 eacces_error = FALSE;
2738 p = path;
2739 for(p = path; p != NULL; p = p_next) {
2740 p1 = strchr(p, ':');
2741 if (!p1) {
2742 p_next = NULL;
2743 path_len = strlen(p);
2744 } else {
2745 p_next = p1 + 1;
2746 path_len = p1 - p;
2747 }
2748 /* path too long */
2749 if ((path_len + 1 + filename_len + 1) > PATH_MAX)
2750 continue;
2751 memcpy(buf, p, path_len);
2752 buf[path_len] = '/';
2753 memcpy(buf + path_len + 1, filename, filename_len);
2754 buf[path_len + 1 + filename_len] = '\0';
2755
2756 execve(buf, argv, envp);
2757
2758 switch(errno) {
2759 case EACCES:
2760 eacces_error = TRUE;
2761 break;
2762 case ENOENT:
2763 case ENOTDIR:
2764 break;
2765 default:
2766 return -1;
2767 }
2768 }
2769 if (eacces_error)
2770 errno = EACCES;
2771 return -1;
2772 }
2773
2774 /* exec(args[, options]) -> exitcode */
js_os_exec(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2775 static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
2776 int argc, JSValueConst *argv)
2777 {
2778 JSValueConst options, args = argv[0];
2779 JSValue val, ret_val;
2780 const char **exec_argv, *file = NULL, *str, *cwd = NULL;
2781 char **envp = environ;
2782 uint32_t exec_argc, i;
2783 int ret, pid, status;
2784 BOOL block_flag = TRUE, use_path = TRUE;
2785 static const char *std_name[3] = { "stdin", "stdout", "stderr" };
2786 int std_fds[3];
2787 uint32_t uid = -1, gid = -1;
2788
2789 val = JS_GetPropertyStr(ctx, args, "length");
2790 if (JS_IsException(val))
2791 return JS_EXCEPTION;
2792 ret = JS_ToUint32(ctx, &exec_argc, val);
2793 JS_FreeValue(ctx, val);
2794 if (ret)
2795 return JS_EXCEPTION;
2796 /* arbitrary limit to avoid overflow */
2797 if (exec_argc < 1 || exec_argc > 65535) {
2798 return JS_ThrowTypeError(ctx, "invalid number of arguments");
2799 }
2800 exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1));
2801 if (!exec_argv)
2802 return JS_EXCEPTION;
2803 for(i = 0; i < exec_argc; i++) {
2804 val = JS_GetPropertyUint32(ctx, args, i);
2805 if (JS_IsException(val))
2806 goto exception;
2807 str = JS_ToCString(ctx, val);
2808 JS_FreeValue(ctx, val);
2809 if (!str)
2810 goto exception;
2811 exec_argv[i] = str;
2812 }
2813 exec_argv[exec_argc] = NULL;
2814
2815 for(i = 0; i < 3; i++)
2816 std_fds[i] = i;
2817
2818 /* get the options, if any */
2819 if (argc >= 2) {
2820 options = argv[1];
2821
2822 if (get_bool_option(ctx, &block_flag, options, "block"))
2823 goto exception;
2824 if (get_bool_option(ctx, &use_path, options, "usePath"))
2825 goto exception;
2826
2827 val = JS_GetPropertyStr(ctx, options, "file");
2828 if (JS_IsException(val))
2829 goto exception;
2830 if (!JS_IsUndefined(val)) {
2831 file = JS_ToCString(ctx, val);
2832 JS_FreeValue(ctx, val);
2833 if (!file)
2834 goto exception;
2835 }
2836
2837 val = JS_GetPropertyStr(ctx, options, "cwd");
2838 if (JS_IsException(val))
2839 goto exception;
2840 if (!JS_IsUndefined(val)) {
2841 cwd = JS_ToCString(ctx, val);
2842 JS_FreeValue(ctx, val);
2843 if (!cwd)
2844 goto exception;
2845 }
2846
2847 /* stdin/stdout/stderr handles */
2848 for(i = 0; i < 3; i++) {
2849 val = JS_GetPropertyStr(ctx, options, std_name[i]);
2850 if (JS_IsException(val))
2851 goto exception;
2852 if (!JS_IsUndefined(val)) {
2853 int fd;
2854 ret = JS_ToInt32(ctx, &fd, val);
2855 JS_FreeValue(ctx, val);
2856 if (ret)
2857 goto exception;
2858 std_fds[i] = fd;
2859 }
2860 }
2861
2862 val = JS_GetPropertyStr(ctx, options, "env");
2863 if (JS_IsException(val))
2864 goto exception;
2865 if (!JS_IsUndefined(val)) {
2866 envp = build_envp(ctx, val);
2867 JS_FreeValue(ctx, val);
2868 if (!envp)
2869 goto exception;
2870 }
2871
2872 val = JS_GetPropertyStr(ctx, options, "uid");
2873 if (JS_IsException(val))
2874 goto exception;
2875 if (!JS_IsUndefined(val)) {
2876 ret = JS_ToUint32(ctx, &uid, val);
2877 JS_FreeValue(ctx, val);
2878 if (ret)
2879 goto exception;
2880 }
2881
2882 val = JS_GetPropertyStr(ctx, options, "gid");
2883 if (JS_IsException(val))
2884 goto exception;
2885 if (!JS_IsUndefined(val)) {
2886 ret = JS_ToUint32(ctx, &gid, val);
2887 JS_FreeValue(ctx, val);
2888 if (ret)
2889 goto exception;
2890 }
2891 }
2892
2893 pid = fork();
2894 if (pid < 0) {
2895 JS_ThrowTypeError(ctx, "fork error");
2896 goto exception;
2897 }
2898 if (pid == 0) {
2899 /* child */
2900 int fd_max = sysconf(_SC_OPEN_MAX);
2901
2902 /* remap the stdin/stdout/stderr handles if necessary */
2903 for(i = 0; i < 3; i++) {
2904 if (std_fds[i] != i) {
2905 if (dup2(std_fds[i], i) < 0)
2906 _exit(127);
2907 }
2908 }
2909
2910 for(i = 3; i < fd_max; i++)
2911 close(i);
2912 if (cwd) {
2913 if (chdir(cwd) < 0)
2914 _exit(127);
2915 }
2916 if (uid != -1) {
2917 if (setuid(uid) < 0)
2918 _exit(127);
2919 }
2920 if (gid != -1) {
2921 if (setgid(gid) < 0)
2922 _exit(127);
2923 }
2924
2925 if (!file)
2926 file = exec_argv[0];
2927 if (use_path)
2928 ret = my_execvpe(file, (char **)exec_argv, envp);
2929 else
2930 ret = execve(file, (char **)exec_argv, envp);
2931 _exit(127);
2932 }
2933 /* parent */
2934 if (block_flag) {
2935 for(;;) {
2936 ret = waitpid(pid, &status, 0);
2937 if (ret == pid) {
2938 if (WIFEXITED(status)) {
2939 ret = WEXITSTATUS(status);
2940 break;
2941 } else if (WIFSIGNALED(status)) {
2942 ret = -WTERMSIG(status);
2943 break;
2944 }
2945 }
2946 }
2947 } else {
2948 ret = pid;
2949 }
2950 ret_val = JS_NewInt32(ctx, ret);
2951 done:
2952 JS_FreeCString(ctx, file);
2953 JS_FreeCString(ctx, cwd);
2954 for(i = 0; i < exec_argc; i++)
2955 JS_FreeCString(ctx, exec_argv[i]);
2956 js_free(ctx, exec_argv);
2957 if (envp != environ) {
2958 char **p;
2959 p = envp;
2960 while (*p != NULL) {
2961 js_free(ctx, *p);
2962 p++;
2963 }
2964 js_free(ctx, envp);
2965 }
2966 return ret_val;
2967 exception:
2968 ret_val = JS_EXCEPTION;
2969 goto done;
2970 }
2971
2972 /* waitpid(pid, block) -> [pid, status] */
js_os_waitpid(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)2973 static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val,
2974 int argc, JSValueConst *argv)
2975 {
2976 int pid, status, options, ret;
2977 JSValue obj;
2978
2979 if (JS_ToInt32(ctx, &pid, argv[0]))
2980 return JS_EXCEPTION;
2981 if (JS_ToInt32(ctx, &options, argv[1]))
2982 return JS_EXCEPTION;
2983
2984 ret = waitpid(pid, &status, options);
2985 if (ret < 0) {
2986 ret = -errno;
2987 status = 0;
2988 }
2989
2990 obj = JS_NewArray(ctx);
2991 if (JS_IsException(obj))
2992 return obj;
2993 JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret),
2994 JS_PROP_C_W_E);
2995 JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status),
2996 JS_PROP_C_W_E);
2997 return obj;
2998 }
2999
3000 /* pipe() -> [read_fd, write_fd] or null if error */
js_os_pipe(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3001 static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val,
3002 int argc, JSValueConst *argv)
3003 {
3004 int pipe_fds[2], ret;
3005 JSValue obj;
3006
3007 ret = pipe(pipe_fds);
3008 if (ret < 0)
3009 return JS_NULL;
3010 obj = JS_NewArray(ctx);
3011 if (JS_IsException(obj))
3012 return obj;
3013 JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]),
3014 JS_PROP_C_W_E);
3015 JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]),
3016 JS_PROP_C_W_E);
3017 return obj;
3018 }
3019
3020 /* kill(pid, sig) */
js_os_kill(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3021 static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val,
3022 int argc, JSValueConst *argv)
3023 {
3024 int pid, sig, ret;
3025
3026 if (JS_ToInt32(ctx, &pid, argv[0]))
3027 return JS_EXCEPTION;
3028 if (JS_ToInt32(ctx, &sig, argv[1]))
3029 return JS_EXCEPTION;
3030 ret = js_get_errno(kill(pid, sig));
3031 return JS_NewInt32(ctx, ret);
3032 }
3033
3034 /* sleep(delay_ms) */
js_os_sleep(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3035 static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val,
3036 int argc, JSValueConst *argv)
3037 {
3038 int64_t delay;
3039 struct timespec ts;
3040 int ret;
3041
3042 if (JS_ToInt64(ctx, &delay, argv[0]))
3043 return JS_EXCEPTION;
3044 ts.tv_sec = delay / 1000;
3045 ts.tv_nsec = (delay % 1000) * 1000000;
3046 ret = js_get_errno(nanosleep(&ts, NULL));
3047 return JS_NewInt32(ctx, ret);
3048 }
3049
3050 /* dup(fd) */
js_os_dup(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3051 static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val,
3052 int argc, JSValueConst *argv)
3053 {
3054 int fd, ret;
3055
3056 if (JS_ToInt32(ctx, &fd, argv[0]))
3057 return JS_EXCEPTION;
3058 ret = js_get_errno(dup(fd));
3059 return JS_NewInt32(ctx, ret);
3060 }
3061
3062 /* dup2(fd) */
js_os_dup2(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3063 static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val,
3064 int argc, JSValueConst *argv)
3065 {
3066 int fd, fd2, ret;
3067
3068 if (JS_ToInt32(ctx, &fd, argv[0]))
3069 return JS_EXCEPTION;
3070 if (JS_ToInt32(ctx, &fd2, argv[1]))
3071 return JS_EXCEPTION;
3072 ret = js_get_errno(dup2(fd, fd2));
3073 return JS_NewInt32(ctx, ret);
3074 }
3075
3076 #endif /* !_WIN32 */
3077
3078 #ifdef USE_WORKER
3079
3080 /* Worker */
3081
3082 typedef struct {
3083 JSWorkerMessagePipe *recv_pipe;
3084 JSWorkerMessagePipe *send_pipe;
3085 JSWorkerMessageHandler *msg_handler;
3086 } JSWorkerData;
3087
3088 typedef struct {
3089 char *filename; /* module filename */
3090 char *basename; /* module base name */
3091 JSWorkerMessagePipe *recv_pipe, *send_pipe;
3092 } WorkerFuncArgs;
3093
3094 typedef struct {
3095 int ref_count;
3096 uint64_t buf[0];
3097 } JSSABHeader;
3098
3099 static JSClassID js_worker_class_id;
3100 static JSContext *(*js_worker_new_context_func)(JSRuntime *rt);
3101
atomic_add_int(int * ptr,int v)3102 static int atomic_add_int(int *ptr, int v)
3103 {
3104 return atomic_fetch_add((_Atomic(uint32_t) *)ptr, v) + v;
3105 }
3106
3107 /* shared array buffer allocator */
js_sab_alloc(void * opaque,size_t size)3108 static void *js_sab_alloc(void *opaque, size_t size)
3109 {
3110 JSSABHeader *sab;
3111 sab = malloc(sizeof(JSSABHeader) + size);
3112 if (!sab)
3113 return NULL;
3114 sab->ref_count = 1;
3115 return sab->buf;
3116 }
3117
js_sab_free(void * opaque,void * ptr)3118 static void js_sab_free(void *opaque, void *ptr)
3119 {
3120 JSSABHeader *sab;
3121 int ref_count;
3122 sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));
3123 ref_count = atomic_add_int(&sab->ref_count, -1);
3124 assert(ref_count >= 0);
3125 if (ref_count == 0) {
3126 free(sab);
3127 }
3128 }
3129
js_sab_dup(void * opaque,void * ptr)3130 static void js_sab_dup(void *opaque, void *ptr)
3131 {
3132 JSSABHeader *sab;
3133 sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader));
3134 atomic_add_int(&sab->ref_count, 1);
3135 }
3136
js_new_message_pipe(void)3137 static JSWorkerMessagePipe *js_new_message_pipe(void)
3138 {
3139 JSWorkerMessagePipe *ps;
3140 int pipe_fds[2];
3141
3142 if (pipe(pipe_fds) < 0)
3143 return NULL;
3144
3145 ps = malloc(sizeof(*ps));
3146 if (!ps) {
3147 close(pipe_fds[0]);
3148 close(pipe_fds[1]);
3149 return NULL;
3150 }
3151 ps->ref_count = 1;
3152 init_list_head(&ps->msg_queue);
3153 pthread_mutex_init(&ps->mutex, NULL);
3154 ps->read_fd = pipe_fds[0];
3155 ps->write_fd = pipe_fds[1];
3156 return ps;
3157 }
3158
js_dup_message_pipe(JSWorkerMessagePipe * ps)3159 static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps)
3160 {
3161 atomic_add_int(&ps->ref_count, 1);
3162 return ps;
3163 }
3164
js_free_message(JSWorkerMessage * msg)3165 static void js_free_message(JSWorkerMessage *msg)
3166 {
3167 size_t i;
3168 /* free the SAB */
3169 for(i = 0; i < msg->sab_tab_len; i++) {
3170 js_sab_free(NULL, msg->sab_tab[i]);
3171 }
3172 free(msg->sab_tab);
3173 free(msg->data);
3174 free(msg);
3175 }
3176
js_free_message_pipe(JSWorkerMessagePipe * ps)3177 static void js_free_message_pipe(JSWorkerMessagePipe *ps)
3178 {
3179 struct list_head *el, *el1;
3180 JSWorkerMessage *msg;
3181 int ref_count;
3182
3183 if (!ps)
3184 return;
3185
3186 ref_count = atomic_add_int(&ps->ref_count, -1);
3187 assert(ref_count >= 0);
3188 if (ref_count == 0) {
3189 list_for_each_safe(el, el1, &ps->msg_queue) {
3190 msg = list_entry(el, JSWorkerMessage, link);
3191 js_free_message(msg);
3192 }
3193 pthread_mutex_destroy(&ps->mutex);
3194 close(ps->read_fd);
3195 close(ps->write_fd);
3196 free(ps);
3197 }
3198 }
3199
js_free_port(JSRuntime * rt,JSWorkerMessageHandler * port)3200 static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port)
3201 {
3202 if (port) {
3203 js_free_message_pipe(port->recv_pipe);
3204 JS_FreeValueRT(rt, port->on_message_func);
3205 list_del(&port->link);
3206 js_free_rt(rt, port);
3207 }
3208 }
3209
js_worker_finalizer(JSRuntime * rt,JSValue val)3210 static void js_worker_finalizer(JSRuntime *rt, JSValue val)
3211 {
3212 JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id);
3213 if (worker) {
3214 js_free_message_pipe(worker->recv_pipe);
3215 js_free_message_pipe(worker->send_pipe);
3216 js_free_port(rt, worker->msg_handler);
3217 js_free_rt(rt, worker);
3218 }
3219 }
3220
3221 static JSClassDef js_worker_class = {
3222 "Worker",
3223 .finalizer = js_worker_finalizer,
3224 };
3225
worker_func(void * opaque)3226 static void *worker_func(void *opaque)
3227 {
3228 WorkerFuncArgs *args = opaque;
3229 JSRuntime *rt;
3230 JSThreadState *ts;
3231 JSContext *ctx;
3232
3233 rt = JS_NewRuntime();
3234 if (rt == NULL) {
3235 fprintf(stderr, "JS_NewRuntime failure");
3236 exit(1);
3237 }
3238 js_std_init_handlers(rt);
3239
3240 JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
3241
3242 /* set the pipe to communicate with the parent */
3243 ts = JS_GetRuntimeOpaque(rt);
3244 ts->recv_pipe = args->recv_pipe;
3245 ts->send_pipe = args->send_pipe;
3246
3247 /* function pointer to avoid linking the whole JS_NewContext() if
3248 not needed */
3249 ctx = js_worker_new_context_func(rt);
3250 if (ctx == NULL) {
3251 fprintf(stderr, "JS_NewContext failure");
3252 }
3253
3254 JS_SetCanBlock(rt, TRUE);
3255
3256 js_std_add_helpers(ctx, -1, NULL);
3257
3258 if (!JS_RunModule(ctx, args->basename, args->filename))
3259 js_std_dump_error(ctx);
3260 free(args->filename);
3261 free(args->basename);
3262 free(args);
3263
3264 js_std_loop(ctx);
3265
3266 JS_FreeContext(ctx);
3267 js_std_free_handlers(rt);
3268 JS_FreeRuntime(rt);
3269 return NULL;
3270 }
3271
js_worker_ctor_internal(JSContext * ctx,JSValueConst new_target,JSWorkerMessagePipe * recv_pipe,JSWorkerMessagePipe * send_pipe)3272 static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target,
3273 JSWorkerMessagePipe *recv_pipe,
3274 JSWorkerMessagePipe *send_pipe)
3275 {
3276 JSValue obj = JS_UNDEFINED, proto;
3277 JSWorkerData *s;
3278
3279 /* create the object */
3280 if (JS_IsUndefined(new_target)) {
3281 proto = JS_GetClassProto(ctx, js_worker_class_id);
3282 } else {
3283 proto = JS_GetPropertyStr(ctx, new_target, "prototype");
3284 if (JS_IsException(proto))
3285 goto fail;
3286 }
3287 obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id);
3288 JS_FreeValue(ctx, proto);
3289 if (JS_IsException(obj))
3290 goto fail;
3291 s = js_mallocz(ctx, sizeof(*s));
3292 if (!s)
3293 goto fail;
3294 s->recv_pipe = js_dup_message_pipe(recv_pipe);
3295 s->send_pipe = js_dup_message_pipe(send_pipe);
3296
3297 JS_SetOpaque(obj, s);
3298 return obj;
3299 fail:
3300 JS_FreeValue(ctx, obj);
3301 return JS_EXCEPTION;
3302 }
3303
js_worker_ctor(JSContext * ctx,JSValueConst new_target,int argc,JSValueConst * argv)3304 static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
3305 int argc, JSValueConst *argv)
3306 {
3307 JSRuntime *rt = JS_GetRuntime(ctx);
3308 WorkerFuncArgs *args = NULL;
3309 pthread_t tid;
3310 pthread_attr_t attr;
3311 JSValue obj = JS_UNDEFINED;
3312 int ret;
3313 const char *filename = NULL, *basename;
3314 JSAtom basename_atom;
3315
3316 /* XXX: in order to avoid problems with resource liberation, we
3317 don't support creating workers inside workers */
3318 if (!is_main_thread(rt))
3319 return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker");
3320
3321 /* base name, assuming the calling function is a normal JS
3322 function */
3323 basename_atom = JS_GetScriptOrModuleName(ctx, 1);
3324 if (basename_atom == JS_ATOM_NULL) {
3325 return JS_ThrowTypeError(ctx, "could not determine calling script or module name");
3326 }
3327 basename = JS_AtomToCString(ctx, basename_atom);
3328 JS_FreeAtom(ctx, basename_atom);
3329 if (!basename)
3330 goto fail;
3331
3332 /* module name */
3333 filename = JS_ToCString(ctx, argv[0]);
3334 if (!filename)
3335 goto fail;
3336
3337 args = malloc(sizeof(*args));
3338 if (!args)
3339 goto oom_fail;
3340 memset(args, 0, sizeof(*args));
3341 args->filename = strdup(filename);
3342 args->basename = strdup(basename);
3343
3344 /* ports */
3345 args->recv_pipe = js_new_message_pipe();
3346 if (!args->recv_pipe)
3347 goto oom_fail;
3348 args->send_pipe = js_new_message_pipe();
3349 if (!args->send_pipe)
3350 goto oom_fail;
3351
3352 obj = js_worker_ctor_internal(ctx, new_target,
3353 args->send_pipe, args->recv_pipe);
3354 if (JS_IsException(obj))
3355 goto fail;
3356
3357 pthread_attr_init(&attr);
3358 /* no join at the end */
3359 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3360 ret = pthread_create(&tid, &attr, worker_func, args);
3361 pthread_attr_destroy(&attr);
3362 if (ret != 0) {
3363 JS_ThrowTypeError(ctx, "could not create worker");
3364 goto fail;
3365 }
3366 JS_FreeCString(ctx, basename);
3367 JS_FreeCString(ctx, filename);
3368 return obj;
3369 oom_fail:
3370 JS_ThrowOutOfMemory(ctx);
3371 fail:
3372 JS_FreeCString(ctx, basename);
3373 JS_FreeCString(ctx, filename);
3374 if (args) {
3375 free(args->filename);
3376 free(args->basename);
3377 js_free_message_pipe(args->recv_pipe);
3378 js_free_message_pipe(args->send_pipe);
3379 free(args);
3380 }
3381 JS_FreeValue(ctx, obj);
3382 return JS_EXCEPTION;
3383 }
3384
js_worker_postMessage(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3385 static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
3386 int argc, JSValueConst *argv)
3387 {
3388 JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
3389 JSWorkerMessagePipe *ps;
3390 size_t data_len, sab_tab_len, i;
3391 uint8_t *data;
3392 JSWorkerMessage *msg;
3393 uint8_t **sab_tab;
3394
3395 if (!worker)
3396 return JS_EXCEPTION;
3397
3398 data = JS_WriteObject2(ctx, &data_len, argv[0],
3399 JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE,
3400 &sab_tab, &sab_tab_len);
3401 if (!data)
3402 return JS_EXCEPTION;
3403
3404 msg = malloc(sizeof(*msg));
3405 if (!msg)
3406 goto fail;
3407 msg->data = NULL;
3408 msg->sab_tab = NULL;
3409
3410 /* must reallocate because the allocator may be different */
3411 msg->data = malloc(data_len);
3412 if (!msg->data)
3413 goto fail;
3414 memcpy(msg->data, data, data_len);
3415 msg->data_len = data_len;
3416
3417 msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
3418 if (!msg->sab_tab)
3419 goto fail;
3420 memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
3421 msg->sab_tab_len = sab_tab_len;
3422
3423 js_free(ctx, data);
3424 js_free(ctx, sab_tab);
3425
3426 /* increment the SAB reference counts */
3427 for(i = 0; i < msg->sab_tab_len; i++) {
3428 js_sab_dup(NULL, msg->sab_tab[i]);
3429 }
3430
3431 ps = worker->send_pipe;
3432 pthread_mutex_lock(&ps->mutex);
3433 /* indicate that data is present */
3434 if (list_empty(&ps->msg_queue)) {
3435 uint8_t ch = '\0';
3436 int ret;
3437 for(;;) {
3438 ret = write(ps->write_fd, &ch, 1);
3439 if (ret == 1)
3440 break;
3441 if (ret < 0 && (errno != EAGAIN || errno != EINTR))
3442 break;
3443 }
3444 }
3445 list_add_tail(&msg->link, &ps->msg_queue);
3446 pthread_mutex_unlock(&ps->mutex);
3447 return JS_UNDEFINED;
3448 fail:
3449 if (msg) {
3450 free(msg->data);
3451 free(msg->sab_tab);
3452 free(msg);
3453 }
3454 js_free(ctx, data);
3455 js_free(ctx, sab_tab);
3456 return JS_EXCEPTION;
3457
3458 }
3459
js_worker_set_onmessage(JSContext * ctx,JSValueConst this_val,JSValueConst func)3460 static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val,
3461 JSValueConst func)
3462 {
3463 JSRuntime *rt = JS_GetRuntime(ctx);
3464 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
3465 JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
3466 JSWorkerMessageHandler *port;
3467
3468 if (!worker)
3469 return JS_EXCEPTION;
3470
3471 port = worker->msg_handler;
3472 if (JS_IsNull(func)) {
3473 if (port) {
3474 js_free_port(rt, port);
3475 worker->msg_handler = NULL;
3476 }
3477 } else {
3478 if (!JS_IsFunction(ctx, func))
3479 return JS_ThrowTypeError(ctx, "not a function");
3480 if (!port) {
3481 port = js_mallocz(ctx, sizeof(*port));
3482 if (!port)
3483 return JS_EXCEPTION;
3484 port->recv_pipe = js_dup_message_pipe(worker->recv_pipe);
3485 port->on_message_func = JS_NULL;
3486 list_add_tail(&port->link, &ts->port_list);
3487 worker->msg_handler = port;
3488 }
3489 JS_FreeValue(ctx, port->on_message_func);
3490 port->on_message_func = JS_DupValue(ctx, func);
3491 }
3492 return JS_UNDEFINED;
3493 }
3494
js_worker_get_onmessage(JSContext * ctx,JSValueConst this_val)3495 static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val)
3496 {
3497 JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id);
3498 JSWorkerMessageHandler *port;
3499 if (!worker)
3500 return JS_EXCEPTION;
3501 port = worker->msg_handler;
3502 if (port) {
3503 return JS_DupValue(ctx, port->on_message_func);
3504 } else {
3505 return JS_NULL;
3506 }
3507 }
3508
3509 static const JSCFunctionListEntry js_worker_proto_funcs[] = {
3510 JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ),
3511 JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ),
3512 };
3513
3514 #endif /* USE_WORKER */
3515
js_std_set_worker_new_context_func(JSContext * (* func)(JSRuntime * rt))3516 void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt))
3517 {
3518 #ifdef USE_WORKER
3519 js_worker_new_context_func = func;
3520 #endif
3521 }
3522
3523 #if defined(_WIN32)
3524 #define OS_PLATFORM "win32"
3525 #elif defined(__APPLE__)
3526 #define OS_PLATFORM "darwin"
3527 #elif defined(EMSCRIPTEN)
3528 #define OS_PLATFORM "js"
3529 #else
3530 #define OS_PLATFORM "linux"
3531 #endif
3532
3533 #define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE )
3534
3535 static const JSCFunctionListEntry js_os_funcs[] = {
3536 JS_CFUNC_DEF("open", 2, js_os_open ),
3537 OS_FLAG(O_RDONLY),
3538 OS_FLAG(O_WRONLY),
3539 OS_FLAG(O_RDWR),
3540 OS_FLAG(O_APPEND),
3541 OS_FLAG(O_CREAT),
3542 OS_FLAG(O_EXCL),
3543 OS_FLAG(O_TRUNC),
3544 #if defined(_WIN32)
3545 OS_FLAG(O_BINARY),
3546 OS_FLAG(O_TEXT),
3547 #endif
3548 JS_CFUNC_DEF("close", 1, js_os_close ),
3549 JS_CFUNC_DEF("seek", 3, js_os_seek ),
3550 JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ),
3551 JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ),
3552 JS_CFUNC_DEF("isatty", 1, js_os_isatty ),
3553 JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ),
3554 JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ),
3555 JS_CFUNC_DEF("remove", 1, js_os_remove ),
3556 JS_CFUNC_DEF("rename", 2, js_os_rename ),
3557 JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ),
3558 JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ),
3559 JS_CFUNC_DEF("signal", 2, js_os_signal ),
3560 OS_FLAG(SIGINT),
3561 OS_FLAG(SIGABRT),
3562 OS_FLAG(SIGFPE),
3563 OS_FLAG(SIGILL),
3564 OS_FLAG(SIGSEGV),
3565 OS_FLAG(SIGTERM),
3566 #if !defined(_WIN32)
3567 OS_FLAG(SIGQUIT),
3568 OS_FLAG(SIGPIPE),
3569 OS_FLAG(SIGALRM),
3570 OS_FLAG(SIGUSR1),
3571 OS_FLAG(SIGUSR2),
3572 OS_FLAG(SIGCHLD),
3573 OS_FLAG(SIGCONT),
3574 OS_FLAG(SIGSTOP),
3575 OS_FLAG(SIGTSTP),
3576 OS_FLAG(SIGTTIN),
3577 OS_FLAG(SIGTTOU),
3578 #endif
3579 JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
3580 JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
3581 JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
3582 JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
3583 JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
3584 JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ),
3585 JS_CFUNC_DEF("readdir", 1, js_os_readdir ),
3586 /* st_mode constants */
3587 OS_FLAG(S_IFMT),
3588 OS_FLAG(S_IFIFO),
3589 OS_FLAG(S_IFCHR),
3590 OS_FLAG(S_IFDIR),
3591 OS_FLAG(S_IFBLK),
3592 OS_FLAG(S_IFREG),
3593 #if !defined(_WIN32)
3594 OS_FLAG(S_IFSOCK),
3595 OS_FLAG(S_IFLNK),
3596 OS_FLAG(S_ISGID),
3597 OS_FLAG(S_ISUID),
3598 #endif
3599 JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ),
3600 JS_CFUNC_DEF("utimes", 3, js_os_utimes ),
3601 #if !defined(_WIN32)
3602 JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ),
3603 JS_CFUNC_DEF("realpath", 1, js_os_realpath ),
3604 JS_CFUNC_DEF("symlink", 2, js_os_symlink ),
3605 JS_CFUNC_DEF("readlink", 1, js_os_readlink ),
3606 JS_CFUNC_DEF("exec", 1, js_os_exec ),
3607 JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ),
3608 OS_FLAG(WNOHANG),
3609 JS_CFUNC_DEF("pipe", 0, js_os_pipe ),
3610 JS_CFUNC_DEF("kill", 2, js_os_kill ),
3611 JS_CFUNC_DEF("sleep", 1, js_os_sleep ),
3612 JS_CFUNC_DEF("dup", 1, js_os_dup ),
3613 JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),
3614 #endif
3615 };
3616
js_os_init(JSContext * ctx,JSModuleDef * m)3617 static int js_os_init(JSContext *ctx, JSModuleDef *m)
3618 {
3619 os_poll_func = js_os_poll;
3620
3621 /* OSTimer class */
3622 JS_NewClassID(&js_os_timer_class_id);
3623 JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
3624
3625 #ifdef USE_WORKER
3626 {
3627 JSRuntime *rt = JS_GetRuntime(ctx);
3628 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
3629 JSValue proto, obj;
3630 /* Worker class */
3631 JS_NewClassID(&js_worker_class_id);
3632 JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class);
3633 proto = JS_NewObject(ctx);
3634 JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs));
3635
3636 obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1,
3637 JS_CFUNC_constructor, 0);
3638 JS_SetConstructor(ctx, obj, proto);
3639
3640 JS_SetClassProto(ctx, js_worker_class_id, proto);
3641
3642 /* set 'Worker.parent' if necessary */
3643 if (ts->recv_pipe && ts->send_pipe) {
3644 JS_DefinePropertyValueStr(ctx, obj, "parent",
3645 js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe),
3646 JS_PROP_C_W_E);
3647 }
3648
3649 JS_SetModuleExport(ctx, m, "Worker", obj);
3650 }
3651 #endif /* USE_WORKER */
3652
3653 return JS_SetModuleExportList(ctx, m, js_os_funcs,
3654 countof(js_os_funcs));
3655 }
3656
js_init_module_os(JSContext * ctx,const char * module_name)3657 JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)
3658 {
3659 JSModuleDef *m;
3660 m = JS_NewCModule(ctx, module_name, js_os_init);
3661 if (!m)
3662 return NULL;
3663 JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs));
3664 #ifdef USE_WORKER
3665 JS_AddModuleExport(ctx, m, "Worker");
3666 #endif
3667 return m;
3668 }
3669
3670 /**********************************************************/
3671
js_print(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)3672 static JSValue js_print(JSContext *ctx, JSValueConst this_val,
3673 int argc, JSValueConst *argv)
3674 {
3675 int i;
3676 const char *str;
3677 size_t len;
3678
3679 for(i = 0; i < argc; i++) {
3680 if (i != 0)
3681 putchar(' ');
3682 str = JS_ToCStringLen(ctx, &len, argv[i]);
3683 if (!str)
3684 return JS_EXCEPTION;
3685 fwrite(str, 1, len, stdout);
3686 JS_FreeCString(ctx, str);
3687 }
3688 putchar('\n');
3689 return JS_UNDEFINED;
3690 }
3691
js_std_add_helpers(JSContext * ctx,int argc,char ** argv)3692 void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
3693 {
3694 JSValue global_obj, args;
3695 int i;
3696
3697 /* XXX: should these global definitions be enumerable? */
3698 global_obj = JS_GetGlobalObject(ctx);
3699
3700 /* close default log implementation
3701 console = JS_NewObject(ctx);
3702 JS_SetPropertyStr(ctx, console, "log",
3703 JS_NewCFunction(ctx, js_print, "log", 1));
3704 JS_SetPropertyStr(ctx, global_obj, "console", console);
3705 */
3706
3707 /* same methods as the mozilla JS shell */
3708 if (argc >= 0) {
3709 args = JS_NewArray(ctx);
3710 for(i = 0; i < argc; i++) {
3711 JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i]));
3712 }
3713 JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args);
3714 }
3715
3716 JS_SetPropertyStr(ctx, global_obj, "print",
3717 JS_NewCFunction(ctx, js_print, "print", 1));
3718 JS_SetPropertyStr(ctx, global_obj, "__loadScript",
3719 JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1));
3720
3721 JS_FreeValue(ctx, global_obj);
3722 }
3723
js_std_init_handlers(JSRuntime * rt)3724 void js_std_init_handlers(JSRuntime *rt)
3725 {
3726 JSThreadState *ts;
3727
3728 ts = malloc(sizeof(*ts));
3729 if (!ts) {
3730 fprintf(stderr, "Could not allocate memory for the worker");
3731 exit(1);
3732 }
3733 memset(ts, 0, sizeof(*ts));
3734 init_list_head(&ts->os_rw_handlers);
3735 init_list_head(&ts->os_signal_handlers);
3736 init_list_head(&ts->os_timers);
3737 init_list_head(&ts->port_list);
3738
3739 JS_SetRuntimeOpaque(rt, ts);
3740
3741 #ifdef USE_WORKER
3742 /* set the SharedArrayBuffer memory handlers */
3743 {
3744 JSSharedArrayBufferFunctions sf;
3745 memset(&sf, 0, sizeof(sf));
3746 sf.sab_alloc = js_sab_alloc;
3747 sf.sab_free = js_sab_free;
3748 sf.sab_dup = js_sab_dup;
3749 JS_SetSharedArrayBufferFunctions(rt, &sf);
3750 }
3751 #endif
3752 }
3753
js_std_free_handlers(JSRuntime * rt)3754 void js_std_free_handlers(JSRuntime *rt)
3755 {
3756 JSThreadState *ts = JS_GetRuntimeOpaque(rt);
3757 struct list_head *el, *el1;
3758
3759 list_for_each_safe(el, el1, &ts->os_rw_handlers) {
3760 JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link);
3761 free_rw_handler(rt, rh);
3762 }
3763
3764 list_for_each_safe(el, el1, &ts->os_signal_handlers) {
3765 JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link);
3766 free_sh(rt, sh);
3767 }
3768
3769 list_for_each_safe(el, el1, &ts->os_timers) {
3770 JSOSTimer *th = list_entry(el, JSOSTimer, link);
3771 unlink_timer(rt, th);
3772 if (!th->has_object)
3773 free_timer(rt, th);
3774 }
3775
3776 #ifdef USE_WORKER
3777 /* XXX: free port_list ? */
3778 js_free_message_pipe(ts->recv_pipe);
3779 js_free_message_pipe(ts->send_pipe);
3780 #endif
3781
3782 free(ts);
3783 JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
3784 }
3785
js_dump_obj(JSContext * ctx,FILE * f,JSValueConst val)3786 static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
3787 {
3788 const char *str;
3789
3790 str = JS_ToCString(ctx, val);
3791 if (str) {
3792 fprintf(f, "%s\n", str);
3793 JS_FreeCString(ctx, str);
3794 } else {
3795 fprintf(f, "[exception]\n");
3796 }
3797 }
3798
js_std_dump_error1(JSContext * ctx,JSValueConst exception_val)3799 static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)
3800 {
3801 JSValue val;
3802 BOOL is_error;
3803
3804 is_error = JS_IsError(ctx, exception_val);
3805 js_dump_obj(ctx, stderr, exception_val);
3806 if (is_error) {
3807 val = JS_GetPropertyStr(ctx, exception_val, "stack");
3808 if (!JS_IsUndefined(val)) {
3809 js_dump_obj(ctx, stderr, val);
3810 }
3811 JS_FreeValue(ctx, val);
3812 }
3813 }
3814
js_std_dump_error(JSContext * ctx)3815 void js_std_dump_error(JSContext *ctx)
3816 {
3817 JSValue exception_val;
3818
3819 exception_val = JS_GetException(ctx);
3820 js_std_dump_error1(ctx, exception_val);
3821 JS_FreeValue(ctx, exception_val);
3822 }
3823
js_std_promise_rejection_tracker(JSContext * ctx,JSValueConst promise,JSValueConst reason,BOOL is_handled,void * opaque)3824 void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
3825 JSValueConst reason,
3826 BOOL is_handled, void *opaque)
3827 {
3828 if (!is_handled) {
3829 fprintf(stderr, "Possibly unhandled promise rejection: ");
3830 js_std_dump_error1(ctx, reason);
3831 }
3832 }
3833
3834 /* main loop which calls the user JS callbacks */
js_std_loop(JSContext * ctx)3835 void js_std_loop(JSContext *ctx)
3836 {
3837 JSContext *ctx1;
3838 int err;
3839
3840 for(;;) {
3841 /* execute the pending jobs */
3842 for(;;) {
3843 err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
3844 if (err <= 0) {
3845 if (err < 0) {
3846 js_std_dump_error(ctx1);
3847 }
3848 break;
3849 }
3850 }
3851
3852 if (!os_poll_func || os_poll_func(ctx))
3853 break;
3854 }
3855 }
3856
js_std_eval_binary(JSContext * ctx,const uint8_t * buf,size_t buf_len,int load_only)3857 void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
3858 int load_only)
3859 {
3860 JSValue obj, val;
3861 obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE);
3862 if (JS_IsException(obj))
3863 goto exception;
3864 if (load_only) {
3865 if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
3866 js_module_set_import_meta(ctx, obj, FALSE, FALSE);
3867 }
3868 } else {
3869 if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
3870 if (JS_ResolveModule(ctx, obj) < 0) {
3871 JS_FreeValue(ctx, obj);
3872 goto exception;
3873 }
3874 js_module_set_import_meta(ctx, obj, FALSE, TRUE);
3875 }
3876 val = JS_EvalFunction(ctx, obj);
3877 if (JS_IsException(val)) {
3878 exception:
3879 js_std_dump_error(ctx);
3880 exit(1);
3881 }
3882 JS_FreeValue(ctx, val);
3883 }
3884 }
3885