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