Coverage Report

Created: 2025-07-18 06:55

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