quickjs-tart

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

quickjs-libc.c (154981B)


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