#include // IWYU pragma: keep #include #include #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #else #include #include #include #include #include #include #include #include #endif #include "ant.h" #include "cfunc.h" #include "errors.h" #include "output.h" #include "utils.h" #include "tty_ctrl.h" #include "internal.h" #include "interrupt.h" #include "descriptors.h" #include "silver/call.h" #include "silver/engine.h" #include "gc/roots.h" #include "gc/modules.h" #include "esm/loader.h" #include "esm/commonjs.h" #include "modules/events.h" #include "modules/process.h" #include "sandbox/sandbox.h" #include "modules/tty.h" #include "modules/buffer.h" #include "modules/napi.h" #include "modules/worker_threads.h" #include "sync.h" #include "modules/timer.h" #include "modules/fs.h" #include "modules/stream.h" #ifndef _WIN32 extern char **environ; #else #define environ _environ #endif static constexpr size_t STDIN_FILE_CHUNK = 65536; typedef struct { uv_signal_t handle; int signum; UT_hash_handle hh; } SignalHandle; typedef struct { uv_fs_t req; ant_t *js; bool pending; bool detached; char buf[STDIN_FILE_CHUNK]; } stdin_file_read_t; typedef struct { union { uv_handle_t handle; uv_stream_t stream; uv_tty_t tty; uv_pipe_t pipe; uv_tcp_t tcp; } h; stdin_file_read_t *file; uv_handle_type kind; bool opened; bool closed; bool reading; bool eof; bool started; bool read_requested; bool paused; bool refed; bool keypress_enabled; int escape_state; int escape_len; char escape_buf[16]; } stdin_state_t; typedef struct env_entry { char *key; char *value; UT_hash_handle hh; } env_entry_t; struct ant_process_state { env_entry_t *private_env; bool has_private_env; ant_value_t process_obj; ant_value_t cached_cwd; uint64_t cwd_epoch; ant_value_t stdin_obj; ant_value_t stdout_obj; ant_value_t stderr_obj; SignalHandle *signal_handles; stdin_state_t stdin_state; uint64_t process_start_time; int32_t exit_code; bool has_exit_code; bool exiting; stdin_byte_consumer_fn stdin_byte_consumer; stdin_eof_fn stdin_byte_eof; uint32_t sandbox_terminal_capabilities; uint16_t sandbox_tty_rows; uint16_t sandbox_tty_cols; bool sandbox_terminal_enabled; bool sandbox_color_saved; char *sandbox_no_color; char *sandbox_force_color; #ifndef _WIN32 uv_signal_t sigwinch_handle; bool sigwinch_initialized; bool sigwinch_closing; bool sigwinch_requested; #endif }; static void private_env_free(env_entry_t **table); static atomic_uint_fast64_t process_cwd_epoch; uint64_t process_cwd_epoch_get(void) { return atomic_load_explicit(&process_cwd_epoch, memory_order_acquire); } static ant_process_state_t *process_state(ant_t *js) { if (js->process_state) return js->process_state; ant_process_state_t *ps = calloc(1, sizeof(*ps)); if (!ps) return NULL; ps->sandbox_tty_rows = 24; ps->sandbox_tty_cols = 80; ps->cached_cwd = js_mkundef(); ps->stdin_state.refed = true; js->process_state = ps; return ps; } typedef struct { const char *name; int signum; UT_hash_handle hh_name; UT_hash_handle hh_num; } SignalEntry; static SignalEntry *signals_by_name = NULL; static SignalEntry *signals_by_num = NULL; static uv_once_t signal_map_once = UV_ONCE_INIT; static void build_signal_map(void) { #define S(sig) { #sig, sig, {0}, {0} } static SignalEntry entries[] = { S(SIGINT), S(SIGILL), S(SIGABRT), S(SIGFPE), S(SIGSEGV), S(SIGTERM), #ifdef SIGHUP S(SIGHUP), #endif #ifdef SIGQUIT S(SIGQUIT), #endif #ifdef SIGTRAP S(SIGTRAP), #endif #ifdef SIGKILL S(SIGKILL), #endif #ifdef SIGBUS S(SIGBUS), #endif #ifdef SIGSYS S(SIGSYS), #endif #ifdef SIGPIPE S(SIGPIPE), #endif #ifdef SIGALRM S(SIGALRM), #endif #ifdef SIGURG S(SIGURG), #endif #ifdef SIGSTOP S(SIGSTOP), #endif #ifdef SIGTSTP S(SIGTSTP), #endif #ifdef SIGCONT S(SIGCONT), #endif #ifdef SIGCHLD S(SIGCHLD), #endif #ifdef SIGTTIN S(SIGTTIN), #endif #ifdef SIGTTOU S(SIGTTOU), #endif #ifdef SIGXCPU S(SIGXCPU), #endif #ifdef SIGXFSZ S(SIGXFSZ), #endif #ifdef SIGVTALRM S(SIGVTALRM), #endif #ifdef SIGPROF S(SIGPROF), #endif #ifdef SIGUSR1 S(SIGUSR1), #endif #ifdef SIGUSR2 S(SIGUSR2), #endif #ifdef SIGEMT S(SIGEMT), #endif #ifdef SIGIO S(SIGIO), #endif #ifdef SIGWINCH S(SIGWINCH), #endif #ifdef SIGINFO S(SIGINFO), #endif #ifdef SIGPWR S(SIGPWR), #endif #ifdef SIGSTKFLT S(SIGSTKFLT), #endif #ifdef SIGPOLL S(SIGPOLL), #endif }; #undef S size_t count = sizeof(entries) / sizeof(entries[0]); for (size_t i = 0; i < count; i++) { SignalEntry *existing = NULL; HASH_ADD_KEYPTR(hh_name, signals_by_name, entries[i].name, strlen(entries[i].name), &entries[i]); HASH_FIND(hh_num, signals_by_num, &entries[i].signum, sizeof(int), existing); if (!existing) HASH_ADD(hh_num, signals_by_num, signum, sizeof(int), &entries[i]); } } static void init_signal_map(void) { uv_once(&signal_map_once, build_signal_map); } int process_signal_number(const char *name) { if (!name || name[0] != 'S') return -1; init_signal_map(); SignalEntry *entry = NULL; HASH_FIND(hh_name, signals_by_name, name, strlen(name), entry); return entry ? entry->signum : -1; } const char *process_signal_name(int signum) { init_signal_map(); SignalEntry *entry = NULL; HASH_FIND(hh_num, signals_by_num, &signum, sizeof(int), entry); return entry ? entry->name : NULL; } static void uv_signal_handler(uv_signal_t *handle, int signum) { ant_t *js = handle->data; const char *name = process_signal_name(signum); if (name) { ant_value_t sig_arg = js_mkstr(js, name, strlen(name)); emit_process_event(js, name, &sig_arg, 1); } } static void start_signal_watch(ant_t *js, int signum) { if (js->worker) return; ant_process_state_t *ps = process_state(js); SignalHandle *sh = NULL; if (!ps) return; HASH_FIND_INT(ps->signal_handles, &signum, sh); if (sh) return; sh = malloc(sizeof(SignalHandle)); sh->signum = signum; uv_signal_init(js->loop, &sh->handle); sh->handle.data = js; uv_signal_start(&sh->handle, uv_signal_handler, signum); uv_unref((uv_handle_t *)&sh->handle); HASH_ADD_INT(ps->signal_handles, signum, sh); } static void on_signal_handle_close(uv_handle_t *handle) { SignalHandle *sh = (SignalHandle *)handle; free(sh); } static void stop_signal_watch(ant_t *js, int signum) { ant_process_state_t *ps = process_state(js); SignalHandle *sh = NULL; if (!ps) return; HASH_FIND_INT(ps->signal_handles, &signum, sh); if (!sh) return; HASH_DEL(ps->signal_handles, sh); uv_signal_stop(&sh->handle); uv_close((uv_handle_t *)&sh->handle, on_signal_handle_close); } void emit_process_event(ant_t *js, const char *event_type, ant_value_t *args, int nargs) { ant_process_state_t *ps = js->process_state; if (!ps || !is_object_type(ps->process_obj)) return; eventemitter_emit_args(js, ps->process_obj, event_type, args, nargs); } bool process_has_event_listeners(ant_t *js, const char *event_type) { ant_process_state_t *ps = js->process_state; if (!ps || !event_type || !is_object_type(ps->process_obj)) return false; return eventemitter_listener_count(js, ps->process_obj, event_type) > 0; } bool process_report_uncaught_exception(ant_t *js) { if (!js || !Ant_Exception_Pending(js)) return false; if (js_is_termination(Ant_Exception_Peek(js))) { js_take_thrown(js, js_mkundef()); return false; } GC_ROOT_SAVE(root_mark, js); ant_value_t stack = Ant_Exception_Stack(js, Ant_Exception_Peek(js)); ant_value_t reason = js_take_thrown(js, js_mkundef()); GC_ROOT_PIN(js, reason); GC_ROOT_PIN(js, stack); if (process_has_event_listeners(js, "uncaughtException")) { ant_value_t origin = js_mkstr(js, "uncaughtException", 17); GC_ROOT_PIN(js, origin); ant_value_t event_args[2] = { reason, origin }; emit_process_event(js, "uncaughtException", event_args, 2); if (Ant_Exception_Pending(js)) { ant_value_t thrown = js_take_thrown(js, js_mkundef()); if (js->worker) ant_worker_report_uncaught(js, thrown); else if (!js->uncaught_nonfatal) exit(7); } GC_ROOT_RESTORE(js, root_mark); return true; } if (js->worker) { ant_worker_report_uncaught(js, reason); GC_ROOT_RESTORE(js, root_mark); return true; } print_error_value(js, reason, stack, NULL); if (!js->uncaught_nonfatal) process_exit_with(js, EXIT_FAILURE); GC_ROOT_RESTORE(js, root_mark); return true; } static const char *stdin_escape_name(const char *seq, int len) { if (len < 2) return NULL; if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { int num = 0; int idx = 1; while (idx < len && seq[idx] >= '0' && seq[idx] <= '9') { num = num * 10 + (seq[idx] - '0'); idx++; } if (idx < len && seq[idx] == '~') { typedef struct { int code; const char *name; } esc_num_map_t; static const esc_num_map_t esc_num_map[] = { { 1, "home" }, { 2, "insert" }, { 3, "delete" }, { 4, "end" }, { 5, "pageup" }, { 6, "pagedown" }, { 7, "home" }, { 8, "end" }, { 15, "f5" }, { 17, "f6" }, { 18, "f7" }, { 19, "f8" }, { 20, "f9" }, { 21, "f10" }, { 23, "f11" }, { 24, "f12" }, }; for (size_t i = 0; i < sizeof(esc_num_map) / sizeof(esc_num_map[0]); i++) { if (esc_num_map[i].code == num) return esc_num_map[i].name; } } return NULL; } typedef struct { char code; bool needs_tilde; const char *name; } esc_map_t; static const esc_map_t esc_map[] = { { 'A', false, "up" }, { 'B', false, "down" }, { 'C', false, "right" }, { 'D', false, "left" }, { 'H', false, "home" }, { 'F', false, "end" }, { 'Z', false, "tab" }, { '2', true, "insert" }, { '3', true, "delete" }, { '5', true, "pageup" }, { '6', true, "pagedown" }, }; for (size_t i = 0; i < sizeof(esc_map) / sizeof(esc_map[0]); i++) { if (seq[1] != esc_map[i].code) continue; if (esc_map[i].needs_tilde) { return (len >= 3 && seq[2] == '~') ? esc_map[i].name : NULL; } return esc_map[i].name; } return NULL; } if (seq[0] == 'O') { switch (seq[1]) { case 'P': return "f1"; case 'Q': return "f2"; case 'R': return "f3"; case 'S': return "f4"; default: return NULL; } } return NULL; } static void emit_keypress_event( ant_t *js, const char *str, size_t str_len, const char *name, bool ctrl, bool meta, bool shift, const char *sequence, size_t sequence_len ) { ant_process_state_t *ps = process_state(js); if (!ps) return; ant_value_t str_val = js_mkstr(js, str ? str : "", str ? str_len : 0); ant_value_t key_obj = Ant_Object_CreateBare(js); if (name) { js_set(js, key_obj, "name", js_mkstr(js, name, strlen(name))); } else { js_set(js, key_obj, "name", js_mkundef()); } js_set(js, key_obj, "ctrl", js_bool(ctrl)); js_set(js, key_obj, "meta", js_bool(meta)); js_set(js, key_obj, "shift", js_bool(shift)); if (sequence) { js_set(js, key_obj, "sequence", js_mkstr(js, sequence, sequence_len)); } ant_value_t args[2] = { str_val, key_obj }; eventemitter_emit_args(js, ps->stdin_obj, "keypress", args, 2); } static void process_keypress_data(ant_t *js, const char *data, size_t len) { ant_process_state_t *ps = process_state(js); if (!ps) return; for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)data[i]; if (ps->stdin_state.escape_state == 1) { ps->stdin_state.escape_buf[ps->stdin_state.escape_len++] = (char)c; if (c == '[' || c == 'O') { ps->stdin_state.escape_state = 2; continue; } emit_keypress_event(js, "\x1b", 1, "escape", false, false, false, "\x1b", 1); ps->stdin_state.escape_state = 0; ps->stdin_state.escape_len = 0; } if (ps->stdin_state.escape_state == 2) { ps->stdin_state.escape_buf[ps->stdin_state.escape_len++] = (char)c; if ((c >= 'A' && c <= 'Z') || c == '~' || ps->stdin_state.escape_len >= 15) { char sequence[18]; size_t seq_len = 0; sequence[seq_len++] = '\x1b'; memcpy(sequence + seq_len, ps->stdin_state.escape_buf, (size_t)ps->stdin_state.escape_len); seq_len += (size_t)ps->stdin_state.escape_len; const char *name = stdin_escape_name(ps->stdin_state.escape_buf, ps->stdin_state.escape_len); if (!name) name = "escape"; emit_keypress_event(js, "", 0, name, false, false, false, sequence, seq_len); ps->stdin_state.escape_state = 0; ps->stdin_state.escape_len = 0; } continue; } if (c == 27) { ps->stdin_state.escape_state = 1; ps->stdin_state.escape_len = 0; continue; } if (c == '\r' || c == '\n') { emit_keypress_event(js, "\r", 1, "return", false, false, false, "\r", 1); continue; } if (c == 127 || c == 8) { emit_keypress_event(js, "", 0, "backspace", false, false, false, NULL, 0); continue; } if (c == '\t') { emit_keypress_event(js, "\t", 1, "tab", false, false, false, "\t", 1); continue; } if (c < 32) { char name_buf[2] = { (char)('a' + c - 1), '\0' }; char seq = (char)c; emit_keypress_event(js, &seq, 1, name_buf, true, false, false, &seq, 1); continue; } char ch = (char)c; char name_buf[2] = { ch, '\0' }; emit_keypress_event(js, &ch, 1, name_buf, false, false, false, &ch, 1); } } static void process_keypress_flush(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return; if (ps->stdin_state.escape_state == 1) { emit_keypress_event(js, "\x1b", 1, "escape", false, false, false, "\x1b", 1); ps->stdin_state.escape_state = 0; ps->stdin_state.escape_len = 0; } } static bool stdin_is_tty(void) { return uv_guess_handle(STDIN_FILENO) == UV_TTY; } static bool stdout_is_tty(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return false; if (ps->sandbox_terminal_enabled) return (ps->sandbox_terminal_capabilities & ANT_SANDBOX_CAP_STDOUT_TTY) != 0; return uv_guess_handle(STDOUT_FILENO) == UV_TTY; } static bool stderr_is_tty(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return false; if (ps->sandbox_terminal_enabled) return (ps->sandbox_terminal_capabilities & ANT_SANDBOX_CAP_STDERR_TTY) != 0; return uv_guess_handle(STDERR_FILENO) == UV_TTY; } static ant_value_t process_binding(ant_params_t) { const char *name; ant_value_t constants; if (nargs < 1 || vtype(args[0]) != kTypeString) return js_mkerr_typed(js, JS_ERR_TYPE, "process.binding() requires a module name"); name = js_getstr(js, args[0], NULL); if (!name || strcmp(name, "constants") != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "No such module: %s", name ? name : ""); constants = Ant_Object_CreateBare(js); js_set(js, constants, "fs", fs_make_constants(js)); return constants; } static void get_tty_size(ant_t *js, int fd, int *rows, int *cols) { ant_process_state_t *ps = process_state(js); int out_rows = 24, out_cols = 80; if (ps && ps->sandbox_terminal_enabled && (fd == STDOUT_FILENO || fd == STDERR_FILENO)) { *rows = ps->sandbox_tty_rows ? ps->sandbox_tty_rows : 24; *cols = ps->sandbox_tty_cols ? ps->sandbox_tty_cols : 80; return; } #ifndef _WIN32 struct winsize ws; if (ioctl(fd, TIOCGWINSZ, &ws) == 0) { if (ws.ws_row > 0) out_rows = ws.ws_row; if (ws.ws_col > 0) out_cols = ws.ws_col; } #else CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { int width = csbi.srWindow.Right - csbi.srWindow.Left + 1; int height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; if (height > 0) out_rows = height; if (width > 0) out_cols = width; } #endif if (rows) *rows = out_rows; if (cols) *cols = out_cols; } static char *env_dup(const char *name) { const char *value = getenv(name); return value ? strdup(value) : NULL; } static void env_restore(const char *name, const char *saved) { if (saved) setenv(name, saved, 1); else unsetenv(name); } static void process_update_sandbox_env(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps || !ps->sandbox_terminal_enabled) return; if (!ps->sandbox_color_saved) { ps->sandbox_no_color = env_dup("NO_COLOR"); ps->sandbox_force_color = env_dup("FORCE_COLOR"); ps->sandbox_color_saved = true; } env_restore("NO_COLOR", ps->sandbox_no_color); env_restore("FORCE_COLOR", ps->sandbox_force_color); if (ps->sandbox_terminal_capabilities & ANT_SANDBOX_CAP_COLOR_STRIP) { setenv("NO_COLOR", "1", 1); unsetenv("FORCE_COLOR"); } else if (ps->sandbox_terminal_capabilities & ANT_SANDBOX_CAP_COLOR_FORCE) { unsetenv("NO_COLOR"); setenv("FORCE_COLOR", "1", 1); } } static ant_value_t process_exec_path(ant_t *js) { char path[PATH_MAX]; if (ant_get_exe_path(path, sizeof(path), js->runtime.argc, js->runtime.argv) == 0 && path[0] != '\0') return js_mkstr(js, path, strlen(path)); if (js->runtime.argc > 0) return js_mkstr(js, js->runtime.argv[0], strlen(js->runtime.argv[0])); return js_mkundef(); } void process_refresh_sandbox_argv(ant_t *js) { ant_value_t process_obj = js_get(js, js_glob(js), "process"); if (!is_special_object(process_obj)) return; ant_value_t argv_arr = js_mkarr(js); for (int i = 0; i < js->runtime.argc; i++) js_arr_push(js, argv_arr, js_mkstr(js, js->runtime.argv[i], strlen(js->runtime.argv[i]))); js_set(js, process_obj, "argv", argv_arr); js_set(js, process_obj, "argv0", js->runtime.argc > 0 ? js_mkstr(js, js->runtime.argv[0], strlen(js->runtime.argv[0])) : js_mkstr(js, "ant", 3)); js_set(js, process_obj, "execPath", process_exec_path(js)); } void process_set_sandbox_terminal(ant_t *js, uint32_t capabilities, uint16_t rows, uint16_t cols) { ant_process_state_t *ps = process_state(js); if (!ps) return; ps->sandbox_terminal_enabled = true; ps->sandbox_terminal_capabilities = capabilities; ps->sandbox_tty_rows = rows ? rows : 24; ps->sandbox_tty_cols = cols ? cols : 80; process_update_sandbox_env(js); ant_value_t process_obj = js_get(js, js_glob(js), "process"); if (!is_special_object(process_obj)) return; ant_value_t stdout_obj = js_get(js, process_obj, "stdout"); if (is_special_object(stdout_obj)) js_set(js, stdout_obj, "isTTY", js_bool(stdout_is_tty(js))); ant_value_t stderr_obj = js_get(js, process_obj, "stderr"); if (is_special_object(stderr_obj)) js_set(js, stderr_obj, "isTTY", js_bool(stderr_is_tty(js))); } static bool stdin_set_raw_mode(bool enable) { if (!stdin_is_tty()) return false; return tty_set_raw_mode(STDIN_FILENO, enable); } static bool stdin_is_stream_kind(uv_handle_type kind) { return kind == UV_TTY || kind == UV_NAMED_PIPE || kind == UV_TCP; } static bool stdin_handle_live(stdin_state_t *st) { return st->opened && !st->closed && stdin_is_stream_kind(st->kind); } static void stdin_alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = malloc(suggested_size); #ifdef _WIN32 buf->len = (ULONG)suggested_size; #else buf->len = suggested_size; #endif } static ant_value_t stdin_uv_error(ant_t *js, int status, const char *syscall) { const char *code = uv_err_name(status); ant_value_t props = Ant_Object_CreateBare(js); js_set(js, props, "code", js_mkstr(js, code, strlen(code))); js_set(js, props, "errno", js_mknum(status)); js_set(js, props, "syscall", js_mkstr(js, syscall, strlen(syscall))); return js_take_thrown(js, js_mkerr_props(js, JS_ERR_GENERIC, props, "%s %s", syscall, code)); } static bool stdin_open(ant_t *js, ant_process_state_t *ps) { stdin_state_t *st = &ps->stdin_state; if (st->opened) return !st->closed; int rc = UV_EINVAL; st->opened = true; st->kind = uv_guess_handle(STDIN_FILENO); switch (st->kind) { case UV_TTY: rc = uv_tty_init(js->loop, &st->h.tty, STDIN_FILENO, 1); break; case UV_NAMED_PIPE: rc = uv_pipe_init(js->loop, &st->h.pipe, 0); if (rc == 0 && (rc = uv_pipe_open(&st->h.pipe, STDIN_FILENO)) != 0) uv_close(&st->h.handle, NULL); break; case UV_TCP: rc = uv_tcp_init(js->loop, &st->h.tcp); if (rc == 0 && (rc = uv_tcp_open(&st->h.tcp, (uv_os_sock_t)(uintptr_t)uv_get_osfhandle(STDIN_FILENO))) != 0) uv_close(&st->h.handle, NULL); break; case UV_FILE: st->file = calloc(1, sizeof(*st->file)); rc = st->file ? 0 : UV_ENOMEM; if (st->file) st->file->js = js; break; default: break; } if (rc != 0) { st->closed = true; return false; } if (stdin_is_stream_kind(st->kind)) { st->h.handle.data = js; if (!st->refed) uv_unref(&st->h.handle); } return true; } static void stdin_release_file(stdin_state_t *st) { stdin_file_read_t *file = st->file; if (!file) return; st->file = NULL; if (!file->pending) { free(file); return; } file->detached = true; uv_cancel((uv_req_t *)&file->req); } static void stdin_close(ant_process_state_t *ps) { stdin_state_t *st = &ps->stdin_state; bool live = stdin_handle_live(st); stdin_release_file(st); if (live && st->reading) uv_read_stop(&st->h.stream); st->reading = false; if (st->opened) st->closed = true; if (live && !uv_is_closing(&st->h.handle)) uv_close(&st->h.handle, NULL); } static void stdin_finish(ant_t *js, ant_value_t error) { ant_process_state_t *ps = process_state(js); if (!ps || ps->stdin_state.eof) return; GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, error); ps->stdin_state.eof = true; ps->stdin_state.read_requested = false; stdin_update_reading(js); if (ps->stdin_byte_eof) ps->stdin_byte_eof(js); if (is_undefined(error)) stream_readable_push(js, ps->stdin_obj, js_mknull(), js_mkundef()); else stream_destroy(js, ps->stdin_obj, error); GC_ROOT_RESTORE(js, root_mark); } static void stdin_deliver(ant_t *js, const char *base, size_t len) { ant_process_state_t *ps = process_state(js); if (!ps) return; stdin_state_t *st = &ps->stdin_state; if (st->started) { ArrayBufferData *ab = create_array_buffer_data(js, len); if (!ab) { stdin_finish(js, stdin_uv_error(js, UV_ENOMEM, "read")); return; } memcpy(ab->data, base, len); ant_value_t chunk = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, js->builtins.buffer_proto); if (is_err(chunk)) { stdin_finish(js, js_take_thrown(js, chunk)); return; } GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, chunk); ant_value_t pushed = stream_readable_push(js, ps->stdin_obj, chunk, js_mkundef()); if (is_err(pushed)) js_take_thrown(js, pushed); else if (pushed == js_false) st->read_requested = false; GC_ROOT_RESTORE(js, root_mark); } bool want_keypress = st->keypress_enabled && eventemitter_listener_count(js, ps->stdin_obj, "keypress") > 0; bool fed_keypress = false; for (size_t i = 0; i < len; i++) { if (st->paused) break; if (ps->stdin_byte_consumer) ps->stdin_byte_consumer(js, base + i, 1); if (want_keypress) { process_keypress_data(js, base + i, 1); fed_keypress = true; } } if (fed_keypress && !st->paused) process_keypress_flush(js); } static void stdin_handle_read(ant_t *js, const char *base, ssize_t nread) { if (nread > 0) { stdin_deliver(js, base, (size_t)nread); stdin_update_reading(js); } else if (nread == UV_EOF) stdin_finish(js, js_mkundef()); else if (nread < 0) stdin_finish(js, stdin_uv_error(js, (int)nread, "read")); } static void on_stdin_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { ant_t *js = stream->data; if (js) stdin_handle_read(js, buf->base, nread); free(buf->base); } static void on_stdin_file_read(uv_fs_t *req) { stdin_file_read_t *file = req->data; ssize_t result = req->result; uv_fs_req_cleanup(req); file->pending = false; if (file->detached) free(file); else stdin_handle_read(file->js, file->buf, result == 0 ? UV_EOF : result); } static void stdin_file_read_next(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return; stdin_state_t *st = &ps->stdin_state; stdin_file_read_t *file = st->file; if (!st->reading || !file || file->pending || st->eof) return; uv_buf_t buf = uv_buf_init(file->buf, (unsigned int)STDIN_FILE_CHUNK); file->req.data = file; int rc = uv_fs_read(js->loop, &file->req, STDIN_FILENO, &buf, 1, -1, on_stdin_file_read); if (rc == 0) file->pending = true; else stdin_finish(js, stdin_uv_error(js, rc, "read")); } static bool stdin_wants_bytes(ant_t *js, ant_process_state_t *ps) { stdin_state_t *st = &ps->stdin_state; if (st->eof) return false; if (st->read_requested) return true; if (st->paused) return false; if (ps->stdin_byte_consumer) return true; return eventemitter_listener_count(js, ps->stdin_obj, "keypress") > 0; } void stdin_update_reading(ant_t *js) { if (js->worker) return; ant_process_state_t *ps = process_state(js); if (!ps) return; stdin_state_t *st = &ps->stdin_state; bool want = stdin_wants_bytes(js, ps); if (!want) { if (st->reading && stdin_handle_live(st)) uv_read_stop(&st->h.stream); st->reading = false; return; } if (st->reading) { if (st->kind == UV_FILE) stdin_file_read_next(js); return; } if (!stdin_open(js, ps)) { stdin_finish(js, js_mkundef()); return; } st->reading = true; if (st->kind == UV_FILE) { stdin_file_read_next(js); return; } int rc = uv_read_start(&st->h.stream, stdin_alloc_buffer, on_stdin_read); if (rc != 0) { st->reading = false; stdin_finish(js, stdin_uv_error(js, rc, "read")); } } void process_stdin_attach_reader(ant_t *js, stdin_byte_consumer_fn on_bytes, stdin_eof_fn on_eof) { ant_process_state_t *ps = process_state(js); if (!ps) return; ps->stdin_byte_consumer = on_bytes; ps->stdin_byte_eof = on_eof; ps->stdin_state.paused = false; stdin_update_reading(js); } void process_stdin_detach_reader(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return; ps->stdin_byte_consumer = NULL; ps->stdin_byte_eof = NULL; stdin_update_reading(js); } #ifndef _WIN32 static void on_sigwinch(uv_signal_t *handle, int signum) { ant_t *js = handle->data; if (!js) return; ant_process_state_t *ps = process_state(js); if (!ps) return; ant_value_t process_obj = js_get(js, js_glob(js), "process"); if (!is_special_object(process_obj)) return; ant_value_t stdout_obj = js_get(js, process_obj, "stdout"); if (!is_special_object(stdout_obj)) return; eventemitter_emit_args(js, ps->stdout_obj, "resize", NULL, 0); } #endif static void start_sigwinch_handler(ant_t *js); #ifndef _WIN32 static void on_sigwinch_close(uv_handle_t *handle) { ant_t *js = handle ? (ant_t *)handle->data : NULL; ant_process_state_t *ps = js ? js->process_state : NULL; if (!ps) return; ps->sigwinch_initialized = false; ps->sigwinch_closing = false; if (ps->sigwinch_requested) start_sigwinch_handler(js); } #endif static void start_sigwinch_handler(ant_t *js) { #ifndef _WIN32 ant_process_state_t *ps = process_state(js); if (!ps) return; ps->sigwinch_requested = true; if (ps->sigwinch_initialized || ps->sigwinch_closing) return; uv_loop_t *loop = js->loop; if (uv_signal_init(loop, &ps->sigwinch_handle) != 0) return; ps->sigwinch_initialized = true; ps->sigwinch_handle.data = js; if (uv_signal_start(&ps->sigwinch_handle, on_sigwinch, SIGWINCH) != 0) { ps->sigwinch_requested = false; ps->sigwinch_closing = true; uv_close((uv_handle_t *)&ps->sigwinch_handle, on_sigwinch_close); return; } uv_unref((uv_handle_t *)&ps->sigwinch_handle); #endif } static void stop_sigwinch_handler(ant_t *js) { #ifndef _WIN32 ant_process_state_t *ps = js ? js->process_state : NULL; if (!ps) return; ps->sigwinch_requested = false; if (!ps->sigwinch_initialized || ps->sigwinch_closing) return; uv_signal_stop(&ps->sigwinch_handle); ps->sigwinch_closing = true; uv_close((uv_handle_t *)&ps->sigwinch_handle, on_sigwinch_close); #endif } void cleanup_process_module(ant_t *js) { ant_process_state_t *ps = js->process_state; if (!ps) return; SignalHandle *sh, *tmp; HASH_ITER(hh, ps->signal_handles, sh, tmp) { HASH_DEL(ps->signal_handles, sh); uv_signal_stop(&sh->handle); uv_close((uv_handle_t *)&sh->handle, on_signal_handle_close); } stdin_close(ps); stop_sigwinch_handler(js); } void free_process_module(ant_t *js) { ant_process_state_t *ps = js->process_state; if (!ps) return; private_env_free(&ps->private_env); stdin_release_file(&ps->stdin_state); free(ps->sandbox_no_color); free(ps->sandbox_force_color); free(ps); js->process_state = NULL; } static ant_value_t js_stdin_set_raw_mode(ant_params_t) { bool enable = nargs > 0 ? js_truthy(js, args[0]) : true; return js_bool(stdin_set_raw_mode(enable)); } static ant_value_t js_stdin__read(ant_params_t) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "out of memory"); ps->stdin_state.started = true; ps->stdin_state.read_requested = true; ps->stdin_state.paused = false; stdin_update_reading(js); return js_mkundef(); } static ant_value_t js_stdin__destroy(ant_params_t) { ant_process_state_t *ps = process_state(js); ant_value_t error = nargs > 0 ? args[0] : js_mknull(); ant_value_t callback = nargs > 1 ? args[1] : js_mkundef(); if (ps) { ps->stdin_state.eof = true; ps->stdin_state.read_requested = false; stdin_update_reading(js); stdin_close(ps); } if (is_callable(callback)) sv_call_value(js, callback, js_mkundef(), &error, 1); return js_mkundef(); } static ant_value_t stdin_set_paused(ant_t *js, bool paused) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "out of memory"); if (paused) stream_readable_pause(js, ps->stdin_obj); else stream_readable_resume(js, ps->stdin_obj); ps->stdin_state.paused = paused; if (paused) ps->stdin_state.read_requested = false; stdin_update_reading(js); return ps->stdin_obj; } static ant_value_t js_stdin_resume(ant_params_t) { return stdin_set_paused(js, false); } static ant_value_t js_stdin_pause(ant_params_t) { return stdin_set_paused(js, true); } static ant_value_t js_stdin_ref(ant_params_t) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "out of memory"); ps->stdin_state.refed = true; if (stdin_handle_live(&ps->stdin_state)) uv_ref(&ps->stdin_state.h.handle); return js_getthis(js); } static ant_value_t js_stdin_unref(ant_params_t) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "out of memory"); ps->stdin_state.refed = false; if (stdin_handle_live(&ps->stdin_state)) uv_unref(&ps->stdin_state.h.handle); return js_getthis(js); } static ant_value_t process_write_stream(ant_native_params_t, FILE *stream, int fd) { if (nargs < 1) return js_false; size_t len = 0; const char *data = NULL; const uint8_t *bytes = NULL; ant_output_stream_t *out = NULL; if (buffer_source_get_bytes(js, args[0], &bytes, &len)) data = (const char *)bytes; else data = js_getstr(js, args[0], &len); if (!data) return js_false; if (!ant_output_has_writer() && uv_guess_handle(fd) == UV_TTY) return js_bool(tty_ctrl_write_fd(fd, data, len)); out = ant_output_stream(stream); ant_output_stream_begin(out); if (!ant_output_stream_append(out, data, len)) return js_false; return ant_output_stream_flush(out) ? js_true : js_false; } static ant_value_t js_stdout_write(ant_params_t) { return process_write_stream(js, args, nargs, stdout, STDOUT_FILENO); } static ant_value_t js_stdout_get_window_size(ant_params_t) { (void)args; (void)nargs; int rows = 0, cols = 0; get_tty_size(js, STDOUT_FILENO, &rows, &cols); ant_value_t arr = js_mkarr(js); js_arr_push(js, arr, js_mknum(cols)); js_arr_push(js, arr, js_mknum(rows)); return arr; } static ant_value_t js_stdout_rows_getter(ant_params_t) { (void)args; (void)nargs; int rows = 0, cols = 0; get_tty_size(js, STDOUT_FILENO, &rows, &cols); return js_mknum(rows); } static ant_value_t js_stdout_columns_getter(ant_params_t) { int rows = 0, cols = 0; get_tty_size(js, STDOUT_FILENO, &rows, &cols); return js_mknum(cols); } static ant_value_t js_stderr_write(ant_params_t) { return process_write_stream(js, args, nargs, stderr, STDERR_FILENO); } static ant_value_t process_uptime(ant_params_t) { (void)args; (void)nargs; ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "out of memory"); uint64_t now = uv_hrtime(); double seconds = (double)(now - ps->process_start_time) / 1e9; return js_mknum(seconds); } static ant_value_t process_hrtime(ant_params_t) { uint64_t now = uv_hrtime(); if (nargs > 0 && vtype(args[0]) != kTypeUndefined) { bool time_is_array = false; ant_value_t checked = js_is_array_value_checked(js, args[0], &time_is_array); if (is_err(checked)) return checked; if (!time_is_array) return Ant_Error_CreateInvalidArgType(js, "time", "an instance of Array", args[0]); ant_value_t time_len = js_get(js, args[0], "length"); if (is_err(time_len)) return time_len; if (vtype(time_len) != kTypeNumber || js_getnum(time_len) != 2) { char message[96]; snprintf(message, sizeof(message), "The value of \"time\" is out of range. It must be 2. Received %s", js_str(js, time_len)); return js_mkerr_code(js, JS_ERR_RANGE, "ERR_OUT_OF_RANGE", message); } ant_value_t prev_sec = js_get(js, args[0], "0"); ant_value_t prev_nsec = js_get(js, args[0], "1"); if (vtype(prev_sec) == kTypeNumber && vtype(prev_nsec) == kTypeNumber) { uint64_t prev = (uint64_t)js_getnum(prev_sec) * 1000000000ULL + (uint64_t)js_getnum(prev_nsec); now = now - prev; } } ant_value_t arr = js_mkarr(js); uint64_t secs = now / 1000000000ULL; uint64_t nsecs = now % 1000000000ULL; js_arr_push(js, arr, js_mknum((double)secs)); js_arr_push(js, arr, js_mknum((double)nsecs)); return arr; } static ant_value_t process_hrtime_bigint(ant_params_t) { (void)args; (void)nargs; uint64_t now = uv_hrtime(); char buf[32]; snprintf(buf, sizeof(buf), "%llu", (unsigned long long)now); return js_mkbigint(js, buf, strlen(buf), false); } static ant_value_t process_memory_usage(ant_params_t) { (void)args; (void)nargs; ant_value_t obj = Ant_Object_Create(js); size_t rss = 0; uv_resident_set_memory(&rss); js_set(js, obj, "rss", js_mknum((double)rss)); #ifdef _WIN32 PROCESS_MEMORY_COUNTERS_EX pmc; if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) { js_set(js, obj, "heapTotal", js_mknum((double)pmc.WorkingSetSize)); js_set(js, obj, "heapUsed", js_mknum((double)pmc.PrivateUsage)); } else { js_set(js, obj, "heapTotal", js_mknum(0)); js_set(js, obj, "heapUsed", js_mknum(0)); } #else struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) == 0) { js_set(js, obj, "heapTotal", js_mknum((double)rss)); js_set(js, obj, "heapUsed", js_mknum((double)rss)); } else { js_set(js, obj, "heapTotal", js_mknum(0)); js_set(js, obj, "heapUsed", js_mknum(0)); } #endif js_set(js, obj, "external", js_mknum(0)); js_set(js, obj, "arrayBuffers", js_mknum(0)); return obj; } static ant_value_t process_memory_usage_rss(ant_params_t) { (void)args; (void)nargs; size_t rss = 0; uv_resident_set_memory(&rss); return js_mknum((double)rss); } static ant_value_t process_cpu_usage(ant_params_t) { ant_value_t obj = Ant_Object_Create(js); uv_rusage_t rusage; if (uv_getrusage(&rusage) == 0) { int64_t user_usec = rusage.ru_utime.tv_sec * 1000000LL + rusage.ru_utime.tv_usec; int64_t sys_usec = rusage.ru_stime.tv_sec * 1000000LL + rusage.ru_stime.tv_usec; if (nargs > 0 && is_special_object(args[0])) { ant_value_t prev_user = js_get(js, args[0], "user"); ant_value_t prev_system = js_get(js, args[0], "system"); if (vtype(prev_user) == kTypeNumber) user_usec -= (int64_t)js_getnum(prev_user); if (vtype(prev_system) == kTypeNumber) sys_usec -= (int64_t)js_getnum(prev_system); } js_set(js, obj, "user", js_mknum((double)user_usec)); js_set(js, obj, "system", js_mknum((double)sys_usec)); } else { js_set(js, obj, "user", js_mknum(0)); js_set(js, obj, "system", js_mknum(0)); } return obj; } static ant_value_t process_resource_usage(ant_params_t) { uv_rusage_t ru; int rc = uv_getrusage(&ru); if (rc != 0) return js_mkerr_typed(js, JS_ERR_GENERIC, "uv_getrusage: %s", uv_strerror(rc)); const struct { const char *name; double value; } fields[] = { { "userCPUTime", (double)ru.ru_utime.tv_sec * 1e6 + (double)ru.ru_utime.tv_usec }, { "systemCPUTime", (double)ru.ru_stime.tv_sec * 1e6 + (double)ru.ru_stime.tv_usec }, { "maxRSS", (double)ru.ru_maxrss }, { "sharedMemorySize", (double)ru.ru_ixrss }, { "unsharedDataSize", (double)ru.ru_idrss }, { "unsharedStackSize", (double)ru.ru_isrss }, { "minorPageFault", (double)ru.ru_minflt }, { "majorPageFault", (double)ru.ru_majflt }, { "swappedOut", (double)ru.ru_nswap }, { "fsRead", (double)ru.ru_inblock }, { "fsWrite", (double)ru.ru_oublock }, { "ipcSent", (double)ru.ru_msgsnd }, { "ipcReceived", (double)ru.ru_msgrcv }, { "signalsCount", (double)ru.ru_nsignals }, { "voluntaryContextSwitches", (double)ru.ru_nvcsw }, { "involuntaryContextSwitches", (double)ru.ru_nivcsw }, }; ant_value_t obj = Ant_Object_Create(js); for (size_t i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) js_set(js, obj, fields[i].name, js_mknum(fields[i].value)); return obj; } static ant_value_t process_kill(ant_params_t) { if (nargs < 1) return js_mkerr(js, "process.kill requires at least 1 argument"); if (vtype(args[0]) != kTypeNumber) return js_mkerr(js, "pid must be a number"); int pid = (int)js_getnum(args[0]); int sig = SIGTERM; if (nargs > 1) { if (vtype(args[1]) == kTypeNumber) { sig = (int)js_getnum(args[1]); } else if (vtype(args[1]) == kTypeString) { char *sig_name = js_getstr(js, args[1], NULL); if (sig_name) { int signum = process_signal_number(sig_name); if (signum > 0) sig = signum; else return js_mkerr(js, "Unknown signal"); } } } int result = uv_kill(pid, sig); if (result != 0) { const char *code = uv_err_name(result); ant_value_t props = Ant_Object_CreateBare(js); js_set(js, props, "code", js_mkstr(js, code, strlen(code))); js_set(js, props, "errno", js_mknum(result)); js_set(js, props, "syscall", js_mkstr(js, "kill", 4)); return js_mkerr_props(js, JS_ERR_GENERIC, props, "kill %s", code); } return js_true; } static ant_value_t process_abort(ant_params_t) { abort(); return js_mkundef(); } static ant_value_t process_chdir(ant_params_t) { if (js->worker) return js_mkerr_code(js, JS_ERR_TYPE, "ERR_WORKER_UNSUPPORTED_OPERATION", "process.chdir() is not supported in workers"); if (nargs < 1) return js_mkerr(js, "process.chdir requires 1 argument"); char *dir = js_getstr(js, args[0], NULL); if (!dir) return js_mkerr(js, "directory must be a string"); int result = uv_chdir(dir); if (result != 0) return js_mkerr(js, "ENOENT: no such file or directory, chdir"); atomic_fetch_add_explicit(&process_cwd_epoch, 1, memory_order_release); return js_mkundef(); } static ant_value_t process_umask(ant_params_t) { #ifdef _WIN32 (void)args; (void)nargs; return js_mknum(0); #else if (nargs > 0 && vtype(args[0]) == kTypeNumber) { int new_mask = (int)js_getnum(args[0]); int old_mask = umask((mode_t)new_mask); return js_mknum(old_mask); } int cur = umask(0); umask((mode_t)cur); return js_mknum(cur); #endif } #ifndef _WIN32 static ant_value_t process_getuid(ant_params_t) { (void)args; (void)nargs; return js_mknum((double)getuid()); } static ant_value_t process_geteuid(ant_params_t) { (void)args; (void)nargs; return js_mknum((double)geteuid()); } static ant_value_t process_getgid(ant_params_t) { (void)args; (void)nargs; return js_mknum((double)getgid()); } static ant_value_t process_getegid(ant_params_t) { (void)args; (void)nargs; return js_mknum((double)getegid()); } static ant_value_t process_getgroups(ant_params_t) { (void)args; (void)nargs; int ngroups = getgroups(0, NULL); if (ngroups < 0) return js_mkarr(js); gid_t *groups = malloc(sizeof(gid_t) * (size_t)ngroups); if (!groups) return js_mkarr(js); ngroups = getgroups(ngroups, groups); ant_value_t arr = js_mkarr(js); for (int i = 0; i < ngroups; i++) { js_arr_push(js, arr, js_mknum((double)groups[i])); } free(groups); return arr; } static ant_value_t process_setuid(ant_params_t) { if (nargs < 1) return js_mkerr(js, "process.setuid requires 1 argument"); uid_t uid; if (vtype(args[0]) == kTypeNumber) { uid = (uid_t)js_getnum(args[0]); } else if (vtype(args[0]) == kTypeString) { char *name = js_getstr(js, args[0], NULL); struct passwd *pwd = getpwnam(name); if (!pwd) return js_mkerr(js, "setuid user not found"); uid = pwd->pw_uid; } else { return js_mkerr(js, "uid must be a number or string"); } if (setuid(uid) != 0) return js_mkerr(js, "setuid failed"); return js_mkundef(); } static ant_value_t process_setgid(ant_params_t) { if (nargs < 1) return js_mkerr(js, "process.setgid requires 1 argument"); gid_t gid; if (vtype(args[0]) == kTypeNumber) { gid = (gid_t)js_getnum(args[0]); } else if (vtype(args[0]) == kTypeString) { char *name = js_getstr(js, args[0], NULL); struct group *grp = getgrnam(name); if (!grp) return js_mkerr(js, "setgid group not found"); gid = grp->gr_gid; } else { return js_mkerr(js, "gid must be a number or string"); } if (setgid(gid) != 0) return js_mkerr(js, "setgid failed"); return js_mkundef(); } static ant_value_t process_seteuid(ant_params_t) { if (nargs < 1) return js_mkerr(js, "process.seteuid requires 1 argument"); uid_t uid; if (vtype(args[0]) == kTypeNumber) { uid = (uid_t)js_getnum(args[0]); } else if (vtype(args[0]) == kTypeString) { char *name = js_getstr(js, args[0], NULL); struct passwd *pwd = getpwnam(name); if (!pwd) return js_mkerr(js, "seteuid user not found"); uid = pwd->pw_uid; } else { return js_mkerr(js, "uid must be a number or string"); } if (seteuid(uid) != 0) return js_mkerr(js, "seteuid failed"); return js_mkundef(); } static ant_value_t process_setegid(ant_params_t) { if (nargs < 1) return js_mkerr(js, "process.setegid requires 1 argument"); gid_t gid; if (vtype(args[0]) == kTypeNumber) { gid = (gid_t)js_getnum(args[0]); } else if (vtype(args[0]) == kTypeString) { char *name = js_getstr(js, args[0], NULL); struct group *grp = getgrnam(name); if (!grp) return js_mkerr(js, "setegid group not found"); gid = grp->gr_gid; } else { return js_mkerr(js, "gid must be a number or string"); } if (setegid(gid) != 0) return js_mkerr(js, "setegid failed"); return js_mkundef(); } static ant_value_t process_setgroups(ant_params_t) { if (nargs < 1 || !js_is_array(args[0])) { return js_mkerr(js, "process.setgroups requires an array"); } ant_value_t len_val = js_get(js, args[0], "length"); int len = (int)js_getnum(len_val); gid_t *groups = malloc(sizeof(gid_t) * (size_t)len); if (!groups) return js_mkerr(js, "allocation failed"); for (int i = 0; i < len; i++) { char idx[16]; snprintf(idx, sizeof(idx), "%d", i); ant_value_t val = js_get(js, args[0], idx); if (vtype(val) == kTypeNumber) { groups[i] = (gid_t)js_getnum(val); } else if (vtype(val) == kTypeString) { char *name = js_getstr(js, val, NULL); struct group *grp = getgrnam(name); if (!grp) { free(groups); return js_mkerr(js, "group not found"); } groups[i] = grp->gr_gid; } else { free(groups); return js_mkerr(js, "group id must be number or string"); } } if (setgroups(len, groups) != 0) { free(groups); return js_mkerr(js, "setgroups failed"); } free(groups); return js_mkundef(); } static ant_value_t process_initgroups(ant_params_t) { if (nargs < 2) return js_mkerr(js, "process.initgroups requires 2 arguments"); char *user = js_getstr(js, args[0], NULL); if (!user) return js_mkerr(js, "user must be a string"); gid_t gid; if (vtype(args[1]) == kTypeNumber) { gid = (gid_t)js_getnum(args[1]); } else if (vtype(args[1]) == kTypeString) { char *name = js_getstr(js, args[1], NULL); struct group *grp = getgrnam(name); if (!grp) return js_mkerr(js, "group not found"); gid = grp->gr_gid; } else { return js_mkerr(js, "gid must be a number or string"); } if (initgroups(user, gid) != 0) return js_mkerr(js, "initgroups failed"); return js_mkundef(); } #endif static bool env_name_ok(const char *key, size_t key_len) { return key_len > 0 && !memchr(key, '=', key_len) && !memchr(key, '\0', key_len); } static ant_mutex_t env_lock = ANT_MUTEX_INIT; static env_entry_t **private_env(ant_t *js) { ant_process_state_t *ps = js->process_state; return ps && ps->has_private_env ? &ps->private_env : NULL; } static bool private_env_put(env_entry_t **table, const char *key, size_t key_len, const char *value, size_t val_len) { env_entry_t *entry = NULL; HASH_FIND(hh, *table, key, key_len, entry); char *copy = strndup(value, val_len); if (!copy) return false; if (entry) { free(entry->value); entry->value = copy; return true; } if (!(entry = calloc(1, sizeof(*entry))) || !(entry->key = strndup(key, key_len))) { free(entry); free(copy); return false; } entry->value = copy; HASH_ADD_KEYPTR(hh, *table, entry->key, key_len, entry); return true; } static void private_env_free(env_entry_t **table) { env_entry_t *entry, *tmp; HASH_ITER(hh, *table, entry, tmp) { HASH_DEL(*table, entry); free(entry->key); free(entry->value); free(entry); } } void process_set_private_env(ant_t *js, char **entries) { ant_process_state_t *ps = process_state(js); if (!ps) return; private_env_free(&ps->private_env); ps->has_private_env = true; for (char **it = entries; it && *it; it++) { const char *eq = strchr(*it, '='); if (eq) private_env_put(&ps->private_env, *it, (size_t)(eq - *it), eq + 1, strlen(eq + 1)); } } static char *env_join(const char *key, size_t key_len, const char *value) { size_t val_len = strlen(value); char *out = malloc(key_len + val_len + 2); if (!out) return NULL; memcpy(out, key, key_len); out[key_len] = '='; memcpy(out + key_len + 1, value, val_len + 1); return out; } char **process_env_snapshot(ant_t *js) { env_entry_t **table = private_env(js); size_t count = 0, i = 0; char **out = NULL; if (table) { count = HASH_COUNT(*table); if (!(out = calloc(count + 1, sizeof(*out)))) return NULL; for (env_entry_t *e = *table; e; e = e->hh.next) out[i++] = env_join(e->key, strlen(e->key), e->value); return out; } ant_mutex_lock(&env_lock); for (char **env = environ; env && *env; env++) count++; if ((out = calloc(count + 1, sizeof(*out)))) for (char **env = environ; env && *env && i < count; env++) out[i++] = strdup(*env); ant_mutex_unlock(&env_lock); return out; } char **process_private_env_snapshot(ant_t *js) { return private_env(js) ? process_env_snapshot(js) : NULL; } static ant_value_t env_get(ant_t *js, ant_value_t target, const char *key, size_t key_len) { if (!env_name_ok(key, key_len)) return js_mkundef(); env_entry_t **table = private_env(js); if (table) { env_entry_t *entry = NULL; HASH_FIND(hh, *table, key, key_len, entry); return entry ? js_mkstr(js, entry->value, strlen(entry->value)) : js_mkundef(); } CSTR_BUF(buf, 256); char *key_str = CSTR_INIT(buf, key, key_len); if (!key_str) return js_mkundef(); ant_mutex_lock(&env_lock); char *value = getenv(key_str); ant_value_t result = value ? js_mkstr(js, value, strlen(value)) : js_mkundef(); ant_mutex_unlock(&env_lock); cstr_free(&buf); return result; } static ant_value_t env_set(ant_t *js, ant_value_t target, const char *key, size_t key_len, ant_value_t value) { if (vtype(value) == kTypeSymbol) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert a Symbol value to a string"); ant_value_t str_val = coerce_to_str(js, value); if (is_err(str_val)) return str_val; size_t val_len = 0; const char *val_str = js_getstr(js, str_val, &val_len); if (!val_str) return js_mkerr(js, "out of memory"); if (!env_name_ok(key, key_len)) return js_true; env_entry_t **table = private_env(js); if (table) return private_env_put(table, key, key_len, val_str, val_len) ? js_true : js_mkerr(js, "out of memory"); CSTR_BUF(buf, 256); char *key_str = CSTR_INIT(buf, key, key_len); if (!key_str) return js_mkerr(js, "out of memory"); ant_mutex_lock(&env_lock); const char *current = getenv(key_str); bool changed = !current || strlen(current) != val_len || memcmp(current, val_str, val_len) != 0; bool stored = !changed || setenv(key_str, val_str, 1) == 0; ant_mutex_unlock(&env_lock); cstr_free(&buf); return stored ? js_true : js_mkerr(js, "out of memory"); } static ant_value_t env_del(ant_t *js, ant_value_t target, const char *key, size_t key_len) { if (!env_name_ok(key, key_len)) return js_true; env_entry_t **table = private_env(js); if (table) { env_entry_t *entry = NULL; HASH_FIND(hh, *table, key, key_len, entry); if (entry) { HASH_DEL(*table, entry); free(entry->key); free(entry->value); free(entry); } return js_true; } CSTR_BUF(buf, 256); char *key_str = CSTR_INIT(buf, key, key_len); if (!key_str) return js_mkerr(js, "out of memory"); ant_mutex_lock(&env_lock); unsetenv(key_str); ant_mutex_unlock(&env_lock); cstr_free(&buf); return js_true; } static void load_dotenv_file(void) { FILE *fp = fopen(".env", "r"); if (fp == NULL) return; char line[1024]; while (fgets(line, sizeof(line), fp) != NULL) { size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } if (len > 0 && line[len - 1] == '\r') { line[len - 1] = '\0'; len--; } if (len == 0 || line[0] == '#') continue; char *equals = strchr(line, '='); if (equals == NULL) continue; *equals = '\0'; char *key = line; char *value = equals + 1; while (*key == ' ' || *key == '\t') key++; char *key_end = key + strlen(key) - 1; while (key_end > key && (*key_end == ' ' || *key_end == '\t')) { *key_end = '\0'; key_end--; } while (*value == ' ' || *value == '\t') value++; char *value_end = value + strlen(value) - 1; while (value_end > value && (*value_end == ' ' || *value_end == '\t')) { *value_end = '\0'; value_end--; } if (strlen(value) >= 2 && ((value[0] == '"' && value[strlen(value) - 1] == '"') || (value[0] == '\'' && value[strlen(value) - 1] == '\''))) { value[strlen(value) - 1] = '\0'; value++; } setenv(key, value, 0); } fclose(fp); } static ant_value_t process_set_exit_code(ant_t *js, ant_process_state_t *ps, ant_value_t code) { if (vtype(code) == kTypeUndefined || vtype(code) == kTypeNull) { ps->has_exit_code = false; return js_mkundef(); } size_t len = 0; ant_value_type_t type = vtype(code); if (type == kTypeString) js_getstr(js, code, &len); double n = type == kTypeNumber ? js_getnum(code) : len > 0 ? tod(js_to_numeric(js, code)) : JS_NAN; if (isnan(n) && type != kTypeNumber) return Ant_Error_CreateInvalidArgType(js, "code", "of type number", code); if (!isfinite(n) || n != trunc(n) || fabs(n) > 9007199254740991.0) { char buf[64], message[160]; js_cstr_t received = js_to_cstr(js, js_mknum(n), buf, sizeof(buf)); snprintf( message, sizeof(message), "The value of \"code\" is out of range. It must be %s. Received %s", isfinite(n) && n == trunc(n) ? ">= -9007199254740991 && <= 9007199254740991" : "an integer", received.ptr ); if (received.needs_free) free((void *)received.ptr); return js_mkerr_code(js, JS_ERR_RANGE, "ERR_OUT_OF_RANGE", message); } ps->exit_code = (int32_t)(uint32_t)(int64_t)n; ps->has_exit_code = true; return js_mkundef(); } static ant_value_t process_exit_code_getter(ant_params_t) { ant_process_state_t *ps = process_state(js); return ps && ps->has_exit_code ? js_mknum(ps->exit_code) : js_mkundef(); } static ant_value_t process_exit_code_setter(ant_params_t) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkundef(); return process_set_exit_code(js, ps, nargs > 0 ? args[0] : js_mkundef()); } int process_exit_code(ant_t *js) { ant_process_state_t *ps = js->process_state; return ps && ps->has_exit_code ? ps->exit_code : 0; } int process_run_exit_handlers(ant_t *js, int status) { ant_process_state_t *ps = js->process_state; if (!ps) return status; if (status != EXIT_SUCCESS) { ps->exit_code = status; ps->has_exit_code = true; } if (ps->exiting || !is_object_type(ps->process_obj)) return process_exit_code(js); ps->exiting = true; js_set(js, ps->process_obj, "_exiting", js_true); ant_value_t code = js_mknum(process_exit_code(js)); emit_process_event(js, "exit", &code, 1); if (print_uncaught_throw(js) && !ps->has_exit_code) { ps->exit_code = EXIT_FAILURE; ps->has_exit_code = true; } return process_exit_code(js); } void process_exit_with(ant_t *js, int status) { int code = process_run_exit_handlers(js, status); if (js->worker) ant_worker_exit(js, code); else exit(code); } static ant_value_t process_exit(ant_params_t) { ant_process_state_t *ps = process_state(js); if (ps && nargs > 0 && vtype(args[0]) != kTypeUndefined) { ant_value_t result = process_set_exit_code(js, ps, args[0]); if (is_err(result)) return result; } if (js->worker) return ant_worker_exit(js, process_run_exit_handlers(js, EXIT_SUCCESS)); process_exit_with(js, EXIT_SUCCESS); return js_mkundef(); } typedef struct { char *buf; size_t pos; size_t cap; } env_str_ctx; typedef void (*env_iter_cb)( ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx ); static void env_foreach(ant_t *js, env_iter_cb cb, void *ctx) { char **snapshot = process_env_snapshot(js); for (char **env = snapshot; env && *env != NULL; env++) { char *entry = *env; char *equals = strchr(entry, '='); if (equals == NULL) continue; size_t key_len = (size_t)(equals - entry); char *value = equals + 1; cb(js, entry, key_len, value, strlen(value), ctx); } for (char **env = snapshot; env && *env; env++) free(*env); free(snapshot); } static void env_to_object_cb(ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx) { ant_value_t obj = *(ant_value_t *)ctx; CSTR_BUF(buf, 256); char *key_str = CSTR_INIT(buf, key, key_len); if (!key_str) return; js_set(js, obj, key_str, js_mkstr(js, value, val_len)); cstr_free(&buf); } static ant_value_t env_to_object(ant_params_t) { ant_value_t obj = Ant_Object_CreateBare(js); env_foreach(js, env_to_object_cb, &obj); return obj; } static void env_tostring_cb(ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx) { env_str_ctx *c = ctx; size_t entry_len = key_len + 1 + val_len; if (c->pos + entry_len + 2 >= c->cap) { size_t new_cap = c->cap * 2 + entry_len; char *new_buf = realloc(c->buf, new_cap); if (!new_buf) return; c->buf = new_buf; c->cap = new_cap; } if (c->pos > 0) c->buf[c->pos++] = '\n'; memcpy(c->buf + c->pos, key, key_len); c->pos += key_len; c->buf[c->pos++] = '='; memcpy(c->buf + c->pos, value, val_len); c->pos += val_len; } static ant_value_t env_toString(ant_params_t) { env_str_ctx ctx = { .buf = malloc(4096), .pos = 0, .cap = 4096 }; if (!ctx.buf) return js_mkstr(js, "", 0); env_foreach(js, env_tostring_cb, &ctx); ctx.buf[ctx.pos] = '\0'; ant_value_t ret = js_mkstr(js, ctx.buf, ctx.pos); free(ctx.buf); return ret; } static void env_keys_cb( ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx ) { js_arr_push(js, *(ant_value_t *)ctx, js_mkstr(js, key, key_len)); } static ant_value_t env_keys(ant_t *js, ant_value_t target) { GC_ROOT_SAVE(root_mark, js); ant_value_t arr = js_mkarr(js); GC_ROOT_PIN(js, arr); env_foreach(js, env_keys_cb, &arr); GC_ROOT_RESTORE(js, root_mark); return arr; } static void env_entries_cb( ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx ) { ant_value_t pairs = *(ant_value_t *)ctx; js_arr_push(js, pairs, js_mkstr(js, key, key_len)); js_arr_push(js, pairs, js_mkstr(js, value, val_len)); } static ant_value_t env_entries(ant_t *js, ant_value_t target) { GC_ROOT_SAVE(root_mark, js); ant_value_t pairs = js_mkarr(js); GC_ROOT_PIN(js, pairs); env_foreach(js, env_entries_cb, &pairs); GC_ROOT_RESTORE(js, root_mark); return pairs; } static const ant_host_proxy_ops_t env_host_ops = { .name = "process.env", .get = env_get, .set = env_set, .del = env_del, .keys = env_keys, .flat_entries = env_entries, }; static ant_value_t process_cwd(ant_params_t) { ant_process_state_t *ps = process_state(js); if (!ps) return js_mkerr(js, "Out of memory"); uint64_t epoch = atomic_load_explicit(&process_cwd_epoch, memory_order_acquire); if (ps->cwd_epoch == epoch && vtype(ps->cached_cwd) == kTypeString) return ps->cached_cwd; char inline_cwd[4096]; char *cwd = inline_cwd; size_t size = sizeof(inline_cwd); int status = uv_cwd(cwd, &size); if (status == UV_ENOBUFS) { cwd = malloc(size); if (!cwd) return js_mkerr(js, "Out of memory"); status = uv_cwd(cwd, &size); } ant_value_t result; if (status == 0) result = js_mkstr(js, cwd, size); else { const char *code = uv_err_name(status); ant_value_t props = Ant_Object_CreateBare(js); js_set(js, props, "code", js_mkstr(js, code, strlen(code))); js_set(js, props, "errno", js_mknum(status)); js_set(js, props, "syscall", js_mkstr(js, "uv_cwd", 6)); result = js_mkerr_props(js, JS_ERR_GENERIC, props, "%s: %s, uv_cwd", code, uv_strerror(status)); } if (cwd != inline_cwd) free(cwd); if (vtype(result) == kTypeString) { ps->cached_cwd = result; ps->cwd_epoch = epoch; } return result; } static bool process_is_error_object(ant_value_t value) { if (is_err(value)) return true; return is_object_type(value) && js_get_slot(value, SLOT_ERROR_BRAND) == js_true; } static void process_get_warning_options( ant_t *js, ant_value_t options, const char **type, const char **code, const char **detail, ant_offset_t *detail_len ) { if (!is_object_type(options)) return; ant_value_t type_val = js_get(js, options, "type"); ant_value_t code_val = js_get(js, options, "code"); ant_value_t detail_val = js_get(js, options, "detail"); if (vtype(type_val) == kTypeString) *type = js_getstr(js, type_val, NULL); if (vtype(code_val) == kTypeString) *code = js_getstr(js, code_val, NULL); if (vtype(detail_val) == kTypeString) *detail = (const char *)(uintptr_t)vstr(js, detail_val, detail_len); } static ant_value_t process_make_warning_object( ant_t *js, const char *type, js_cstr_t msg, const char *code, const char *detail, ant_offset_t detail_len ) { ant_value_t warning_obj = Ant_Object_CreateBare(js); js_set_proto_init(warning_obj, js->builtins.error_protos[JS_ERR_GENERIC]); js_set_slot(warning_obj, SLOT_ERROR_BRAND, js_true); js_set(js, warning_obj, "name", js_mkstr(js, type, strlen(type))); js_set(js, warning_obj, "message", js_mkstr(js, msg.ptr, msg.len)); if (code) js_set(js, warning_obj, "code", js_mkstr(js, code, strlen(code))); if (detail) js_set(js, warning_obj, "detail", js_mkstr(js, detail, detail_len)); js_capture_stack(js, warning_obj); return warning_obj; } static ant_value_t process_emit_warning(ant_params_t) { if (nargs < 1) return js_mkundef(); ant_value_t warning = args[0]; const char *type = "Warning"; const char *code = NULL; const char *detail = NULL; ant_offset_t detail_len = 0; if (nargs >= 2) { if (vtype(args[1]) == kTypeString) type = js_getstr(js, args[1], NULL); else process_get_warning_options(js, args[1], &type, &code, &detail, &detail_len); } if (nargs >= 3 && vtype(args[2]) == kTypeString) code = js_getstr(js, args[2], NULL); char msg_buf[512]; js_cstr_t msg = {0}; ant_value_t warning_event_arg = warning; bool is_error = process_is_error_object(warning); if (is_error) { ant_value_t warning_obj = js_as_obj(warning); ant_offset_t prop_len = 0; const char *name_prop = get_str_prop(js, warning_obj, "name", 4, &prop_len); if (name_prop) type = name_prop; const char *message_prop = get_str_prop(js, warning_obj, "message", 7, &prop_len); msg.ptr = message_prop ? message_prop : ""; msg.len = message_prop ? prop_len : 0; msg.needs_free = false; code = get_str_prop(js, warning_obj, "code", 4, NULL); detail = get_str_prop(js, warning_obj, "detail", 6, &detail_len); warning_event_arg = warning_obj; } else { msg = js_to_cstr(js, warning, msg_buf, sizeof(msg_buf)); warning_event_arg = process_make_warning_object(js, type, msg, code, detail, detail_len); } fprintf(stderr, "(%s:%d) ", "ant", (int)getpid()); if (code) fprintf(stderr, "[%s] ", code); fprintf(stderr, "%s: %.*s\n", type ? type : "Warning", (int)msg.len, msg.ptr); if (detail) fprintf(stderr, "%.*s\n", (int)detail_len, detail); emit_process_event(js, "warning", &warning_event_arg, 1); if (msg.needs_free) free((void *)msg.ptr); return js_mkundef(); } static ant_value_t process_next_tick(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "process.nextTick requires a callback"); ant_value_t cb = args[0]; if (vtype(cb) != kTypeFunction && vtype(cb) != kTypeBuiltin) return js_mkerr_typed(js, JS_ERR_TYPE, "process.nextTick callback is not a function"); if (nargs <= 1) queue_next_tick(js, cb); else queue_next_tick_with_args(js, cb, args + 1, nargs - 1); return js_mkundef(); } static bool process_event_key_is( ant_t *js, ant_value_t key, const char *expected ) { const char *value = NULL; size_t len = 0; size_t expected_len = strlen(expected); if (vtype(key) != kTypeString) return false; value = js_getstr(js, key, &len); return value && len == expected_len && memcmp(value, expected, len) == 0; } static void process_listener_change( ant_t *js, ant_value_t target, ant_value_t key, ant_offset_t listener_count, void *context ) { ant_process_state_t *ps = (ant_process_state_t *)context; if (!ps || vtype(key) != kTypeString) return; if (target == ps->process_obj) { const char *event = js_getstr(js, key, NULL); int signum = event ? process_signal_number(event) : -1; if (signum > 0) { if (listener_count > 0) start_signal_watch(js, signum); else stop_signal_watch(js, signum); } else if (listener_count > 0 && process_event_key_is(js, key, "workerMessage")) ant_worker_messaging_listen(js); return; } if (target == ps->stdin_obj && process_event_key_is(js, key, "keypress")) { if (listener_count > 0) ps->stdin_state.paused = false; stdin_update_reading(js); return; } if ( target == ps->stdout_obj && process_event_key_is(js, key, "resize") ) { if (listener_count > 0) start_sigwinch_handler(js); else stop_sigwinch_handler(js); } } static ant_value_t process_get_builtin_module(ant_params_t) { ant_value_t id = nargs > 0 ? args[0] : js_mkundef(); if (vtype(id) != kTypeString) return Ant_Error_CreateInvalidArgType(js, "id", "of type string", id); size_t len = 0; const char *name = js_getstr(js, id, &len); if (!name) return js_mkundef(); bool found = false; ant_value_t ns = js_esm_load_node_builtin(js, name, len, &found); return found ? esm_require_unwrap(js, ns) : js_mkundef(); } static void process_set_methods(ant_t *js, ant_value_t obj, bool include_event_methods) { js_set(js, obj, "exit", Ant_Function_CreateWithArity(process_exit, 1)); js_set(js, obj, "cwd", Ant_Function_Create(process_cwd)); js_set(js, obj, "chdir", Ant_Function_CreateWithArity(process_chdir, 1)); js_set(js, obj, "uptime", Ant_Function_Create(process_uptime)); js_set(js, obj, "cpuUsage", Ant_Function_CreateWithArity(process_cpu_usage, 1)); js_set(js, obj, "resourceUsage", Ant_Function_Create(process_resource_usage)); js_set(js, obj, "kill", Ant_Function_CreateWithArity(process_kill, 2)); js_set(js, obj, "abort", Ant_Function_Create(process_abort)); js_set(js, obj, "umask", Ant_Function_CreateWithArity(process_umask, 1)); js_set(js, obj, "nextTick", Ant_Function_CreateWithArity(process_next_tick, 1)); js_set(js, obj, "emitWarning", Ant_Function_CreateWithArity(process_emit_warning, 4)); js_set(js, obj, "getBuiltinModule", Ant_Function_CreateWithArity(process_get_builtin_module, 1)); js_set(js, obj, "dlopen", Ant_Function_Create(napi_process_dlopen_js)); ant_value_t mem_usage_fn = Ant_Function_CreateWithData(js, process_memory_usage, "memoryUsage", 0, js_mkundef()); js_set(js, mem_usage_fn, "rss", Ant_Function_Create(process_memory_usage_rss)); js_set(js, obj, "memoryUsage", mem_usage_fn); ant_value_t hrtime_fn = Ant_Function_CreateWithData(js, process_hrtime, "hrtime", 1, js_mkundef()); js_set(js, hrtime_fn, "bigint", Ant_Function_Create(process_hrtime_bigint)); js_set(js, obj, "hrtime", hrtime_fn); if (include_event_methods) { ant_value_t ee_proto = eventemitter_prototype(js); if (is_object_type(ee_proto)) js_set_proto_init(obj, ee_proto); } #ifndef _WIN32 js_set(js, obj, "getuid", Ant_Function_Create(process_getuid)); js_set(js, obj, "geteuid", Ant_Function_Create(process_geteuid)); js_set(js, obj, "getgid", Ant_Function_Create(process_getgid)); js_set(js, obj, "getegid", Ant_Function_Create(process_getegid)); js_set(js, obj, "getgroups", Ant_Function_Create(process_getgroups)); js_set(js, obj, "setuid", Ant_Function_CreateWithArity(process_setuid, 1)); js_set(js, obj, "setgid", Ant_Function_CreateWithArity(process_setgid, 1)); js_set(js, obj, "seteuid", Ant_Function_CreateWithArity(process_seteuid, 1)); js_set(js, obj, "setegid", Ant_Function_CreateWithArity(process_setegid, 1)); js_set(js, obj, "setgroups", Ant_Function_CreateWithArity(process_setgroups, 1)); js_set(js, obj, "initgroups", Ant_Function_CreateWithArity(process_initgroups, 2)); #endif } ant_value_t process_library(ant_t *js) { ant_value_t process_obj = js_get(js, js_glob(js), "process"); js_set(js, process_obj, "default", process_obj); js_set_slot_wb(js, process_obj, SLOT_DEFAULT, process_obj); return process_obj; } static void process_set_string(ant_t *js, ant_value_t obj, const char *key, const char *value) { js_set(js, obj, key, js_mkstr(js, value, strlen(value))); } void init_process_module(ant_t *js) { ant_process_state_t *ps = process_state(js); ant_value_t ee_proto = eventemitter_prototype(js); if (!ps) return; ps->process_start_time = uv_hrtime(); ant_value_t process_proto = Ant_Object_Create(js); process_set_methods(js, process_proto, true); js_set_tostringtag(js, process_proto, js_mkstr(js, "process", 7)); ant_value_t process_obj = Ant_Object_CreateBare(js); ant_value_t env_obj = Ant_Object_Create(js); js_set_proto_init(process_obj, process_proto); ps->process_obj = process_obj; eventemitter_set_listener_change_hook(js, process_obj, process_listener_change, ps); process_set_methods(js, process_obj, false); js_set(js, process_obj, "binding", Ant_Function_CreateWithArity(process_binding, 1)); js_set(js, process_obj, "_exiting", js_false); js_set_accessor_desc( js, process_obj, "exitCode", 8, Ant_Function_Create(process_exit_code_getter), Ant_Function_Create(process_exit_code_setter), JS_DESC_E ); if (!js->worker) load_dotenv_file(); defmethod(js, env_obj, "toObject", 8, Ant_Function_Create(env_to_object)); defmethod(js, env_obj, "toString", 8, Ant_Function_Create(env_toString)); ant_value_t env_proxy = js_mkproxy_host(js, env_obj, &env_host_ops); if (is_err(env_proxy)) return; js->builtins.process_env = env_proxy; js_set(js, process_obj, "env", env_proxy); process_update_sandbox_env(js); ant_value_t argv_arr = js_mkarr(js); for (int i = 0; i < js->runtime.argc; i++) js_arr_push( js, argv_arr, js_mkstr(js, js->runtime.argv[i], strlen(js->runtime.argv[i])) ); js_set(js, process_obj, "argv", argv_arr); js_set(js, process_obj, "execArgv", js_mkarr(js)); js_set(js, process_obj, "argv0", js->runtime.argc > 0 ? js_mkstr(js, js->runtime.argv[0], strlen(js->runtime.argv[0])) : js_mkstr(js, "ant", 3) ); js_set(js, process_obj, "execPath", process_exec_path(js)); js_set(js, process_obj, "pid", js_mknum((double)getpid())); js_set(js, process_obj, "ppid", js_mknum((double)getppid())); ant_value_t features_obj = Ant_Object_Create(js); js_set(js, features_obj, "uv", js_true); process_set_string(js, features_obj, "tls", "BoringSSL"); process_set_string(js, features_obj, "typescript", "transform"); js_set(js, process_obj, "features", features_obj); process_set_string(js, process_obj, "version", "v26.8.1"); ant_value_t versions_obj = Ant_Object_Create(js); process_set_string(js, versions_obj, "ant", ANT_VERSION); process_set_string(js, versions_obj, "silver", ANT_VERSION); process_set_string(js, versions_obj, "skim", SKIM_VERSION); char uv_ver[32]; snprintf(uv_ver, sizeof(uv_ver), "%d.%d.%d", UV_VERSION_MAJOR, UV_VERSION_MINOR, UV_VERSION_PATCH); process_set_string(js, versions_obj, "uv", uv_ver); process_set_string(js, versions_obj, "node", "26.8.1"); process_set_string(js, versions_obj, "brotli", "1.1.0"); process_set_string(js, versions_obj, "llhttp", "9.3.1"); process_set_string(js, versions_obj, "simdjson", "0.12.0"); process_set_string(js, versions_obj, "pcre2", "10.47"); process_set_string(js, versions_obj, "libffi", "3.5.2"); process_set_string(js, versions_obj, "lmdb", "0.9.33"); process_set_string(js, versions_obj, "utf8proc", "2.10.0"); process_set_string(js, versions_obj, "zlib", "2.3.3"); process_set_string(js, versions_obj, "v8", "14.6.202.34-node.28"); process_set_string(js, versions_obj, "modules", "147"); process_set_string(js, versions_obj, "napi", "10"); process_set_string(js, versions_obj, "wamr", "26756a5c5846c262c9e5c89f3fc8e1e693ee2539"); process_set_string(js, versions_obj, "boringssl", "297b11798a0ed6bc7736aa57328909a4afbbf67a"); js_set(js, process_obj, "versions", versions_obj); ant_value_t release_obj = Ant_Object_Create(js); js_set(js, release_obj, "name", js_mkstr(js, "ant", 3)); js_set(js, process_obj, "release", release_obj); // process.platform #if defined(__APPLE__) js_set(js, process_obj, "platform", js_mkstr(js, "darwin", 6)); #elif defined(__linux__) js_set(js, process_obj, "platform", js_mkstr(js, "linux", 5)); #elif defined(_WIN32) || defined(_WIN64) js_set(js, process_obj, "platform", js_mkstr(js, "win32", 5)); #elif defined(__FreeBSD__) js_set(js, process_obj, "platform", js_mkstr(js, "freebsd", 7)); #else js_set(js, process_obj, "platform", js_mkstr(js, "unknown", 7)); #endif // process.arch #if defined(__x86_64__) || defined(_M_X64) js_set(js, process_obj, "arch", js_mkstr(js, "x64", 3)); #elif defined(__i386__) || defined(_M_IX86) js_set(js, process_obj, "arch", js_mkstr(js, "ia32", 4)); #elif defined(__aarch64__) || defined(_M_ARM64) js_set(js, process_obj, "arch", js_mkstr(js, "arm64", 5)); #elif defined(__arm__) || defined(_M_ARM) js_set(js, process_obj, "arch", js_mkstr(js, "arm", 3)); #else js_set(js, process_obj, "arch", js_mkstr(js, "unknown", 7)); #endif ant_value_t stdin_proto = Ant_Object_CreateBare(js); js_set_proto_init(stdin_proto, stream_readable_prototype(js)); js_set(js, stdin_proto, "setRawMode", Ant_Function_Create(js_stdin_set_raw_mode)); js_set(js, stdin_proto, "_read", Ant_Function_CreateWithArity(js_stdin__read, 1)); js_set(js, stdin_proto, "_destroy", Ant_Function_CreateWithArity(js_stdin__destroy, 2)); js_set(js, stdin_proto, "resume", Ant_Function_Create(js_stdin_resume)); js_set(js, stdin_proto, "pause", Ant_Function_Create(js_stdin_pause)); js_set(js, stdin_proto, "ref", Ant_Function_Create(js_stdin_ref)); js_set(js, stdin_proto, "unref", Ant_Function_Create(js_stdin_unref)); js_set_tostringtag(js, stdin_proto, js_mkstr(js, "ReadStream", 10)); ant_value_t stdin_obj = stream_construct_readable(js, stdin_proto, js_mkundef(), js_mkundef()); ps->stdin_obj = stdin_obj; eventemitter_set_listener_change_hook(js, stdin_obj, process_listener_change, ps); js_set(js, stdin_obj, "isTTY", js_bool(stdin_is_tty())); js_set(js, process_obj, "stdin", stdin_obj); ant_value_t stdout_proto = Ant_Object_CreateBare(js); if (is_object_type(ee_proto)) js_set_proto_init(stdout_proto, ee_proto); js_set(js, stdout_proto, "write", Ant_Function_Create(js_stdout_write)); js_set(js, stdout_proto, "getWindowSize", Ant_Function_Create(js_stdout_get_window_size)); js_set_tostringtag(js, stdout_proto, js_mkstr(js, "WriteStream", 11)); ant_value_t stdout_obj = Ant_Object_CreateBare(js); js_set_proto_init(stdout_obj, stdout_proto); ps->stdout_obj = stdout_obj; eventemitter_set_listener_change_hook(js, stdout_obj, process_listener_change, ps); js_set(js, stdout_obj, "isTTY", js_bool(stdout_is_tty(js))); js_set_getter_desc(js, stdout_obj, "rows", 4, Ant_Function_Create(js_stdout_rows_getter), JS_DESC_E | JS_DESC_C); js_set_getter_desc(js, stdout_obj, "columns", 7, Ant_Function_Create(js_stdout_columns_getter), JS_DESC_E | JS_DESC_C); js_set(js, process_obj, "stdout", stdout_obj); ant_value_t stderr_proto = Ant_Object_CreateBare(js); if (is_object_type(ee_proto)) js_set_proto_init(stderr_proto, ee_proto); js_set(js, stderr_proto, "write", Ant_Function_Create(js_stderr_write)); js_set_tostringtag(js, stderr_proto, js_mkstr(js, "WriteStream", 11)); ant_value_t stderr_obj = Ant_Object_CreateBare(js); js_set_proto_init(stderr_obj, stderr_proto); ps->stderr_obj = stderr_obj; eventemitter_set_listener_change_hook(js, stderr_obj, process_listener_change, ps); js_set(js, stderr_obj, "isTTY", js_bool(stderr_is_tty(js))); js_set(js, process_obj, "stderr", stderr_obj); js_set_global_builtin(js, "process", process_obj); } bool has_active_stdin(ant_t *js) { ant_process_state_t *ps = js->process_state; return ps && ps->stdin_state.reading && ps->stdin_state.refed; } void gc_mark_process(ant_t *js, gc_mark_fn mark) { ant_process_state_t *ps = js->process_state; if (!ps) return; mark(js, ps->cached_cwd); if (is_object_type(ps->process_obj)) mark(js, ps->process_obj); if (is_object_type(ps->stdin_obj)) mark(js, ps->stdin_obj); if (is_object_type(ps->stdout_obj)) mark(js, ps->stdout_obj); if (is_object_type(ps->stderr_obj)) mark(js, ps->stderr_obj); } void process_enable_keypress_events(ant_t *js) { ant_process_state_t *ps = process_state(js); if (!ps) return; ps->stdin_state.keypress_enabled = true; ps->stdin_state.escape_state = 0; ps->stdin_state.escape_len = 0; }