Coverage Report

Created: 2026-03-10 06:55

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