Coverage Report

Created: 2024-05-13 06:32

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