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