Coverage Report

Created: 2026-07-16 07:18

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