Coverage Report

Created: 2024-02-25 06:23

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