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