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