quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

quickjs-libc.c (140472B)


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