diff --git a/examples/spec/events.js b/examples/spec/events.js index 68d3782..e4b7733 100644 --- a/examples/spec/events.js +++ b/examples/spec/events.js @@ -111,6 +111,20 @@ eeA.emit('x'); test('separate instances are isolated (A)', aVal, 1); test('separate instances are isolated (B)', bVal, 0); +const eeC = new EventEmitter(); +eeC.on('a', () => {}); +eeC.on('b', () => {}); +eeC.removeAllListeners(); +test('removeAllListeners without event clears all listeners', eeC.listenerCount('a') + eeC.listenerCount('b'), 0); + +const eeD = new EventEmitter(); +let listenerThis = null; +eeD.on('ctx', function () { + listenerThis = this; +}); +eeD.emit('ctx'); +test('EventEmitter listeners receive emitter as this', listenerThis, eeD); + console.log('\nEventTarget Tests\n'); const et = new EventTarget(); diff --git a/examples/spec/readline.js b/examples/spec/readline.js index c3beb0a..2a04487 100644 --- a/examples/spec/readline.js +++ b/examples/spec/readline.js @@ -1,4 +1,4 @@ -import { test, summary } from './helpers.js'; +import { test, testDeep, summary } from './helpers.js'; import * as readline from 'node:readline'; import * as readlinePromises from 'node:readline/promises'; @@ -193,4 +193,24 @@ test('ctrl+u clears line', rl5.line, ''); rl5.close(); +const rlAsync = readlinePromises.createInterface({ + input: process.stdin, + output: process.stdout +}); + +const asyncLines = []; +const consumeAsyncLines = (async () => { + for await (const line of rlAsync) { + asyncLines.push(line); + if (asyncLines.length === 2) break; + } +})(); + +rlAsync.write('alpha\n'); +rlAsync.write('beta\n'); +await consumeAsyncLines; + +testDeep('async iterator yields lines', asyncLines, ['alpha', 'beta']); +test('async iterator closes interface after break', rlAsync.closed, true); + summary(); diff --git a/include/modules/events.h b/include/modules/events.h index 8fe8388..81fc565 100644 --- a/include/modules/events.h +++ b/include/modules/events.h @@ -5,6 +5,7 @@ #include "types.h" ant_value_t events_library(ant_t *js); +ant_value_t eventemitter_prototype(ant_t *js); void init_events_module(void); void js_dispatch_global_event(ant_t *js, ant_value_t event_obj); @@ -33,4 +34,26 @@ bool eventemitter_emit_args_val( ant_value_t *args, int nargs ); +bool eventemitter_remove_listener( + ant_t *js, + ant_value_t target, const char *event_type, + ant_value_t listener +); + +bool eventemitter_remove_listener_val( + ant_t *js, + ant_value_t target, ant_value_t key, + ant_value_t listener +); + +ant_offset_t eventemitter_listener_count( + ant_t *js, + ant_value_t target, const char *event_type +); + +ant_offset_t eventemitter_listener_count_val( + ant_t *js, + ant_value_t target, ant_value_t key +); + #endif diff --git a/src/modules/events.c b/src/modules/events.c index 9e3b7e5..ae96655 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -26,6 +26,8 @@ typedef struct { } event_data_t; static ant_value_t g_isTrusted_getter = 0; +static ant_value_t g_eventemitter_ctor = 0; +static ant_value_t g_eventemitter_proto = 0; static ant_value_t g_event_proto = 0; static ant_value_t g_customevent_proto = 0; static ant_value_t g_errorevent_proto = 0; @@ -637,6 +639,44 @@ static bool eventemitter_add_listener_impl( return true; } +static bool eventemitter_remove_listener_impl( + ant_t *js, + ant_value_t target, ant_value_t key, + ant_value_t listener +) { + EventType *evt = NULL; + uint8_t t = 0; + + if (!is_object_type(target) || !key) return false; + t = vtype(listener); + if (t != T_FUNC && t != T_CFUNC) return false; + + evt = find_emitter_event_type(js, target, key); + if (!evt) return false; + + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { + EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); + if (entry->callback == listener) { + utarray_erase(evt->listeners, i, 1); + return true; + }} + + return false; +} + +static ant_offset_t eventemitter_listener_count_impl( + ant_t *js, + ant_value_t target, ant_value_t key +) { + EventType *evt = NULL; + + if (!is_object_type(target) || !key) return 0; + evt = find_emitter_event_type(js, target, key); + if (!evt) return 0; + + return (ant_offset_t)utarray_len(evt->listeners); +} + static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); ant_value_t key = evt_key_from_arg(args[0]); @@ -674,7 +714,7 @@ static bool eventemitter_emit_args_impl( if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) continue; if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC) continue; - ant_value_t result = sv_vm_call(js->vm, js, cb, js_mkundef(), args, nargs, NULL, false); + ant_value_t result = sv_vm_call(js->vm, js, cb, target, args, nargs, NULL, false); if (vtype(result) == T_ERR) { if (vtype(key) == T_STR) fprintf(stderr, "Error in event listener for '%s': %s\n", js_str(js, key), js_str(js, result)); else fprintf(stderr, "Error in event listener: %s\n", js_str(js, result)); @@ -727,6 +767,36 @@ bool eventemitter_add_listener( return eventemitter_add_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener, once); } +bool eventemitter_remove_listener_val( + ant_t *js, + ant_value_t target, ant_value_t key, + ant_value_t listener +) { + return eventemitter_remove_listener_impl(js, target, key, listener); +} + +bool eventemitter_remove_listener( + ant_t *js, + ant_value_t target, const char *event_type, + ant_value_t listener +) { + return eventemitter_remove_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener); +} + +ant_offset_t eventemitter_listener_count_val( + ant_t *js, + ant_value_t target, ant_value_t key +) { + return eventemitter_listener_count_impl(js, target, key); +} + +ant_offset_t eventemitter_listener_count( + ant_t *js, + ant_value_t target, const char *event_type +) { + return eventemitter_listener_count_val(js, target, js_mkstr(js, event_type, strlen(event_type))); +} + static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) { if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); ant_value_t key = evt_key_from_arg(args[0]); @@ -756,11 +826,20 @@ static ant_value_t js_eventemitter_prepend_once_listener(ant_t *js, ant_value_t static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); - if (nargs < 1) return this_obj; - + EventType **events = NULL; + + if (nargs < 1 || vtype(args[0]) == T_UNDEF) { + events = get_or_create_emitter_events(js, this_obj); + if (!events || !*events) return this_obj; + + EventType *evt, *tmp; + HASH_ITER(hh, *events, evt, tmp) utarray_clear(evt->listeners); + return this_obj; + } + ant_value_t key = evt_key_from_arg(args[0]); if (!key) return this_obj; - + EventType *evt = find_emitter_event_type(js, this_obj, key); if (evt) utarray_clear(evt->listeners); @@ -794,7 +873,18 @@ static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int ant_value_t events_library(ant_t *js) { ant_value_t lib = js_mkobj(js); - ant_value_t eventemitter_ctor = js_mkobj(js); + + eventemitter_prototype(js); + js_set_module_default(js, lib, g_eventemitter_ctor, "EventEmitter"); + js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); + + return lib; +} + +ant_value_t eventemitter_prototype(ant_t *js) { + if (g_eventemitter_proto) return g_eventemitter_proto; + + ant_value_t eventemitter_ctor = js_mkobj(js); ant_value_t eventemitter_proto = js_mkobj(js); js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); @@ -814,11 +904,10 @@ ant_value_t events_library(ant_t *js) { js_mkprop_fast(js, eventemitter_ctor, "name", 4, ANT_STRING("EventEmitter")); js_set_descriptor(js, eventemitter_ctor, "name", 4, 0); - ant_value_t ctor_fn = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); - js_set_module_default(js, lib, ctor_fn, "EventEmitter"); - js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); + g_eventemitter_proto = eventemitter_proto; + g_eventemitter_ctor = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); - return lib; + return g_eventemitter_proto; } void init_events_module(void) { @@ -903,6 +992,8 @@ void gc_mark_events(ant_t *js, gc_mark_fn mark) { if (*reg->events) mark_event_type_listeners(js, mark, *reg->events); } if (g_isTrusted_getter) mark(js, g_isTrusted_getter); + if (g_eventemitter_ctor) mark(js, g_eventemitter_ctor); + if (g_eventemitter_proto) mark(js, g_eventemitter_proto); if (g_event_proto) mark(js, g_event_proto); if (g_customevent_proto) mark(js, g_customevent_proto); if (g_errorevent_proto) mark(js, g_errorevent_proto); diff --git a/src/modules/readline.c b/src/modules/readline.c index 8e23506..5826687 100644 --- a/src/modules/readline.c +++ b/src/modules/readline.c @@ -33,6 +33,7 @@ #include "silver/engine.h" #include "gc/modules.h" +#include "modules/events.h" #include "modules/readline.h" #include "modules/process.h" #include "modules/symbol.h" @@ -42,21 +43,6 @@ #define DEFAULT_PROMPT "> " #define DEFAULT_HISTORY_SIZE 30 #define DEFAULT_TAB_SIZE 8 -#define MAX_INTERFACES 64 - -typedef struct { - ant_value_t listener; - bool once; -} RLEventListener; - -#define MAX_LISTENERS_PER_EVENT 16 - -typedef struct { - char *event_type; - RLEventListener listeners[MAX_LISTENERS_PER_EVENT]; - int listener_count; - UT_hash_handle hh; -} RLEventType; typedef struct { char **lines; @@ -87,7 +73,6 @@ typedef struct rl_interface { int tab_size; ant_value_t pending_question_resolve; ant_value_t pending_question_reject; - RLEventType *events; uv_tty_t tty_in; uv_tty_t tty_out; bool tty_initialized; @@ -104,8 +89,10 @@ typedef struct rl_interface { #endif } rl_interface_t; -static rl_interface_t *interfaces = NULL; static uint64_t next_interface_id = 1; +static rl_interface_t *interfaces = NULL; +static ant_value_t g_rl_async_iter_proto = 0; +static ant_value_t g_rl_interface_proto = 0; static void rl_history_init(rl_history_t *hist, int capacity) { hist->capacity = capacity > 0 ? capacity : DEFAULT_HISTORY_SIZE; @@ -114,29 +101,35 @@ static void rl_history_init(rl_history_t *hist, int capacity) { hist->current = -1; } -static void rl_history_add(rl_history_t *hist, const char *line, bool remove_duplicates) { - if (!line || strlen(line) == 0) return; +static void rl_history_remove_at(rl_history_t *hist, int index) { + if (!hist || index < 0 || index >= hist->count) return; + + free(hist->lines[index]); + if (index < hist->count - 1) memmove( + hist->lines + index, + hist->lines + index + 1, + sizeof(char *) * (size_t)(hist->count - index - 1) + ); + hist->count--; +} + +static void rl_history_add(rl_history_t *hist, const char *line, bool remove_duplicates) { + int duplicate_index = -1; + + if (!line || line[0] == '\0') return; + if (!remove_duplicates && hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) return; + if (remove_duplicates) { - for (int i = 0; i < hist->count; i++) { - if (strcmp(hist->lines[i], line) == 0) { - free(hist->lines[i]); - for (int j = i; j < hist->count - 1; j++) { - hist->lines[j] = hist->lines[j + 1]; - } - hist->count--; break; - } - } - } else if (hist->count > 0 && strcmp(hist->lines[hist->count - 1], line) == 0) { - return; - } - - if (hist->count >= hist->capacity) { - free(hist->lines[0]); - memmove(hist->lines, hist->lines + 1, sizeof(char*) * (hist->capacity - 1)); - hist->count--; - } - + for (int i = 0; i < hist->count; i++) { + if (strcmp(hist->lines[i], line) != 0) continue; + duplicate_index = i; + break; + }} + + if (duplicate_index >= 0) rl_history_remove_at(hist, duplicate_index); + if (hist->count >= hist->capacity) rl_history_remove_at(hist, 0); + hist->lines[hist->count++] = strdup(line); hist->current = hist->count; } @@ -169,63 +162,6 @@ static void rl_history_free(rl_history_t *hist) { hist->current = -1; } -static RLEventType *find_or_create_event_type(rl_interface_t *iface, const char *event_type) { - RLEventType *evt = NULL; - HASH_FIND_STR(iface->events, event_type, evt); - - if (evt == NULL) { - evt = malloc(sizeof(RLEventType)); - evt->event_type = strdup(event_type); - evt->listener_count = 0; - HASH_ADD_KEYPTR(hh, iface->events, evt->event_type, strlen(evt->event_type), evt); - } - - return evt; -} - -static void emit_event(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t *args, int nargs) { - RLEventType *evt = NULL; - HASH_FIND_STR(iface->events, event_type, evt); - - if (evt == NULL || evt->listener_count == 0) return; - - int i = 0; - while (i < evt->listener_count) { - RLEventListener *listener = &evt->listeners[i]; - sv_vm_call(js->vm, js, listener->listener, js_mkundef(), args, nargs, NULL, false); - - if (listener->once) { - for (int j = i; j < evt->listener_count - 1; j++) { - evt->listeners[j] = evt->listeners[j + 1]; - } - evt->listener_count--; - } else i++; - } -} - -static bool rl_has_event_listener(rl_interface_t *iface, const char *event_type) { - RLEventType *evt = NULL; - if (!iface || !event_type) return false; - HASH_FIND_STR(iface->events, event_type, evt); - return evt != NULL && evt->listener_count > 0; -} - -static ant_value_t get_history_array(ant_t *js, rl_interface_t *iface) { - ant_value_t arr = js_mkarr(js); - for (int i = 0; i < iface->history.count; i++) js_arr_push( - js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i])) - ); - return arr; -} - -static void emit_history_event(ant_t *js, rl_interface_t *iface) { - ant_value_t history_arr = get_history_array(js, iface); - emit_event(js, iface, "history", &history_arr, 1); -} - -static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); -static void on_stdin_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); - #ifndef _WIN32 static void enter_raw_mode(rl_interface_t *iface) { if (iface->raw_mode) return; @@ -412,38 +348,37 @@ static void handle_delete(rl_interface_t *iface) { static void handle_escape_sequence(rl_interface_t *iface, const char *seq, int len) { if (len >= 2 && seq[0] == '[') { - switch (seq[1]) { - case 'A': handle_history_up(iface); break; - case 'B': handle_history_down(iface); break; - case 'C': - if (iface->line_pos < iface->line_len) { - iface->line_pos++; - printf("\033[C"); - fflush(stdout); - } - break; - case 'D': - if (iface->line_pos > 0) { - iface->line_pos--; - printf("\033[D"); - fflush(stdout); - } - break; - case 'H': - iface->line_pos = 0; - refresh_line(iface); - break; - case 'F': - iface->line_pos = iface->line_len; - refresh_line(iface); - break; - case '3': - if (len >= 3 && seq[2] == '~') { - handle_delete(iface); - } - break; - } - } + switch (seq[1]) { + case 'A': handle_history_up(iface); break; + case 'B': handle_history_down(iface); break; + case 'C': + if (iface->line_pos < iface->line_len) { + iface->line_pos++; + printf("\033[C"); + fflush(stdout); + } + break; + case 'D': + if (iface->line_pos > 0) { + iface->line_pos--; + printf("\033[D"); + fflush(stdout); + } + break; + case 'H': + iface->line_pos = 0; + refresh_line(iface); + break; + case 'F': + iface->line_pos = iface->line_len; + refresh_line(iface); + break; + case '3': + if (len >= 3 && seq[2] == '~') { + handle_delete(iface); + } + break; + }} } static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { @@ -451,6 +386,25 @@ static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *b buf->len = suggested_size; } +static void emit_event(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t *args, int nargs) { + if (!iface || vtype(iface->js_obj) == T_UNDEF) return; + eventemitter_emit_args(js, iface->js_obj, event_type, args, nargs); +} + +static ant_value_t get_history_array(ant_t *js, rl_interface_t *iface) { + ant_value_t arr = js_mkarr(js); + for (int i = 0; i < iface->history.count; i++) js_arr_push( + js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i])) + ); + return arr; +} + +static void emit_history_event(ant_t *js, rl_interface_t *iface) { + ant_value_t history_arr = get_history_array(js, iface); + emit_event(js, iface, "history", &history_arr, 1); +} + + static void stop_reading(rl_interface_t *iface) { if (!iface->reading) return; @@ -464,6 +418,59 @@ static void stop_reading(rl_interface_t *iface) { #endif } +static bool rl_has_event_listener(ant_t *js, rl_interface_t *iface, const char *event_type) { + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return false; + return eventemitter_listener_count(js, iface->js_obj, event_type) > 0; +} + +static bool rl_add_listener(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t listener, bool once) { + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return false; + return eventemitter_add_listener(js, iface->js_obj, event_type, listener, once); +} + +static void rl_remove_listener(ant_t *js, rl_interface_t *iface, const char *event_type, ant_value_t listener) { + if (!iface || !event_type || vtype(iface->js_obj) == T_UNDEF) return; + eventemitter_remove_listener(js, iface->js_obj, event_type, listener); +} + +static ant_value_t rl_async_iter_state(ant_t *js, ant_value_t iterator) { + ant_value_t state = is_object_type(iterator) ? js_get_slot(iterator, SLOT_DATA) : js_mkundef(); + return is_object_type(state) ? state : js_mkundef(); +} + +static ant_value_t rl_async_iter_queue(ant_t *js, ant_value_t state, const char *queue_key) { + ant_value_t queue = is_object_type(state) ? js_get(js, state, queue_key) : js_mkundef(); + return vtype(queue) == T_ARR ? queue : js_mkundef(); +} + +static ant_offset_t rl_async_iter_queue_head(ant_t *js, ant_value_t state, const char *head_key) { + ant_value_t head = is_object_type(state) ? js_get(js, state, head_key) : js_mkundef(); + return vtype(head) == T_NUM ? (ant_offset_t)js_getnum(head) : 0; +} + +static void rl_async_iter_set_queue_head(ant_t *js, ant_value_t state, const char *head_key, ant_offset_t head) { + if (is_object_type(state)) js_set(js, state, head_key, js_mknum((double)head)); +} + +static void rl_close_interface(ant_t *js, rl_interface_t *iface) { + if (!iface || iface->closed) return; + stop_reading(iface); + + if (iface->tty_initialized) { + uv_close((uv_handle_t *)&iface->tty_in, NULL); +#ifndef _WIN32 + if (!iface->sigint_watcher_active && uv_is_active((uv_handle_t *)&iface->sigint_watcher)) { + uv_close((uv_handle_t *)&iface->sigint_watcher, NULL); + } + exit_raw_mode(iface); +#endif + iface->tty_initialized = false; + } + + iface->closed = true; + emit_event(js, iface, "close", NULL, 0); +} + static void process_line(ant_t *js, rl_interface_t *iface) { char *line = strdup(iface->line_buffer); @@ -548,7 +555,7 @@ static void on_sigint(uv_signal_t *handle, int signum) { rl_interface_t *iface = (rl_interface_t *)handle->data; ant_t *js = rt->js; - if (rl_has_event_listener(iface, "SIGINT")) { + if (rl_has_event_listener(js, iface, "SIGINT")) { emit_event(js, iface, "SIGINT", NULL, 0); } else if (process_has_event_listeners("SIGINT")) { ant_value_t sig_arg = js_mkstr(js, "SIGINT", 6); @@ -597,157 +604,19 @@ static rl_interface_t *get_interface(ant_t *js, ant_value_t this_obj) { return iface; } -static ant_value_t rl_interface_on(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - if (!iface) return js_mkerr(js, "Invalid Interface"); - if (nargs < 2) return js_mkerr(js, "on requires 2 arguments"); - - char *event = js_getstr(js, args[0], NULL); - if (!event) return js_mkerr(js, "event must be a string"); - int t = vtype(args[1]); - if (t != T_FUNC && t != T_CFUNC) return js_mkerr(js, "listener must be a function"); - - RLEventType *evt = find_or_create_event_type(iface, event); - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum listeners reached"); - } - - evt->listeners[evt->listener_count].listener = args[1]; - evt->listeners[evt->listener_count].once = false; - evt->listener_count++; - - return this_obj; -} - -static ant_value_t rl_interface_once(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - if (!iface) return js_mkerr(js, "Invalid Interface"); - if (nargs < 2) return js_mkerr(js, "once requires 2 arguments"); - - char *event = js_getstr(js, args[0], NULL); - if (!event) return js_mkerr(js, "event must be a string"); - int t = vtype(args[1]); - if (t != T_FUNC && t != T_CFUNC) return js_mkerr(js, "listener must be a function"); - - RLEventType *evt = find_or_create_event_type(iface, event); - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum listeners reached"); - } - - evt->listeners[evt->listener_count].listener = args[1]; - evt->listeners[evt->listener_count].once = true; - evt->listener_count++; - - return this_obj; -} - -static ant_value_t rl_interface_off(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - if (!iface) return js_mkerr(js, "Invalid Interface"); - if (nargs < 2) return this_obj; - - char *event = js_getstr(js, args[0], NULL); - if (!event) return this_obj; - - RLEventType *evt = NULL; - HASH_FIND_STR(iface->events, event, evt); - if (!evt) return this_obj; - - for (int i = 0; i < evt->listener_count; i++) { - if (evt->listeners[i].listener == args[1]) { - for (int j = i; j < evt->listener_count - 1; j++) { - evt->listeners[j] = evt->listeners[j + 1]; - } - evt->listener_count--; - break; - } - } - - return this_obj; -} - -static ant_value_t rl_interface_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - RLEventType *evt = NULL; - RLEventType *tmp = NULL; - char *event = NULL; - - if (!iface) return js_mkerr(js, "Invalid Interface"); - if (nargs < 1 || vtype(args[0]) == T_UNDEF) { - HASH_ITER(hh, iface->events, evt, tmp) { - HASH_DEL(iface->events, evt); - free(evt->event_type); - free(evt); - } - return this_obj; - } - - event = js_getstr(js, args[0], NULL); - if (!event) return this_obj; - - HASH_FIND_STR(iface->events, event, evt); - if (!evt) return this_obj; - - HASH_DEL(iface->events, evt); - free(evt->event_type); - free(evt); - - return this_obj; -} - -static ant_value_t rl_interface_emit(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - if (!iface) return js_mkerr(js, "Invalid Interface"); - if (nargs < 1) return js_false; - - char *event = js_getstr(js, args[0], NULL); - if (!event) return js_false; - - emit_event(js, iface, event, nargs > 1 ? &args[1] : NULL, nargs - 1); - return js_true; -} - static ant_value_t rl_interface_close(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); if (!iface || iface->closed) return js_mkundef(); - - stop_reading(iface); - - if (iface->tty_initialized) { - uv_close((uv_handle_t *)&iface->tty_in, NULL); -#ifndef _WIN32 - if (!iface->sigint_watcher_active && uv_is_active((uv_handle_t *)&iface->sigint_watcher)) { - uv_close((uv_handle_t *)&iface->sigint_watcher, NULL); - } - exit_raw_mode(iface); -#endif - iface->tty_initialized = false; - } - - iface->closed = true; - emit_event(js, iface, "close", NULL, 0); + rl_close_interface(js, iface); return js_mkundef(); } static ant_value_t rl_interface_pause(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); - if (!iface) return js_mkerr(js, "Invalid Interface"); if (!iface->paused) { @@ -760,10 +629,8 @@ static ant_value_t rl_interface_pause(ant_t *js, ant_value_t *args, int nargs) { } static ant_value_t rl_interface_resume(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); - if (!iface) return js_mkerr(js, "Invalid Interface"); if (iface->paused) { @@ -822,10 +689,8 @@ static ant_value_t rl_interface_set_prompt(ant_t *js, ant_value_t *args, int nar } static ant_value_t rl_interface_get_prompt(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); - if (!iface) return js_mkerr(js, "Invalid Interface"); return js_mkstr(js, iface->prompt, strlen(iface->prompt)); @@ -928,7 +793,6 @@ static ant_value_t rl_interface_write(ant_t *js, ant_value_t *args, int nargs) { } static ant_value_t rl_interface_line_getter(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); @@ -937,7 +801,6 @@ static ant_value_t rl_interface_line_getter(ant_t *js, ant_value_t *args, int na } static ant_value_t rl_interface_cursor_getter(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); @@ -962,15 +825,7 @@ static ant_value_t rl_interface_question_callback(ant_t *js, ant_value_t *args, } write_output(iface, query); - - RLEventType *evt = find_or_create_event_type(iface, "line"); - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum listeners reached"); - } - - evt->listeners[evt->listener_count].listener = args[1]; - evt->listeners[evt->listener_count].once = true; - evt->listener_count++; + if (!rl_add_listener(js, iface, "line", args[1], true)) return js_mkerr(js, "listener must be a function"); return js_mkundef(); } @@ -997,7 +852,6 @@ static ant_value_t rl_interface_question_promise(ant_t *js, ant_value_t *args, i } static ant_value_t rl_interface_get_cursor_pos(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); @@ -1034,7 +888,6 @@ static ant_value_t rl_interface_get_cursor_pos(ant_t *js, ant_value_t *args, int } static ant_value_t rl_interface_closed_getter(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); rl_interface_t *iface = get_interface(js, this_obj); @@ -1042,21 +895,6 @@ static ant_value_t rl_interface_closed_getter(ant_t *js, ant_value_t *args, int return js_bool(iface->closed); } -static ant_value_t rl_interface_async_iterator(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; - ant_value_t this_obj = js_getthis(js); - rl_interface_t *iface = get_interface(js, this_obj); - - if (!iface) return js_mkerr(js, "Invalid Interface"); - - ant_value_t iterator = js_mkobj(js); - js_set(js, iterator, "_rl_id", js_mknum((double)iface->id)); - js_set(js, iterator, "_lines", js_mkarr(js)); - js_set(js, iterator, "_done", js_false); - - return iterator; -} - static void free_interface(rl_interface_t *iface) { if (!iface) return; @@ -1065,15 +903,292 @@ static void free_interface(rl_interface_t *iface) { free(iface->prompt); free(iface->line_buffer); rl_history_free(&iface->history); + free(iface); +} + +static ant_value_t rl_clear_line(ant_t *js, ant_value_t *args, int nargs) { + int dir = 0; + if (!tty_ctrl_parse_clear_line_dir(args, nargs, 1, &dir)) return js_false; + + size_t seq_len = 0; + const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); +} + +static ant_value_t rl_clear_screen_down(ant_t *js, ant_value_t *args, int nargs) { + size_t seq_len = 0; + const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); +} + +static ant_value_t rl_cursor_to(ant_t *js, ant_value_t *args, int nargs) { + tty_ctrl_cursor_to_args_t parsed; + if (!tty_ctrl_parse_cursor_to_args(args, nargs, 1, 2, &parsed)) return js_false; + + char seq[64]; + size_t seq_len = 0; + bool ok = tty_ctrl_build_cursor_to( + seq, sizeof(seq), + parsed.x, parsed.has_y, parsed.y, + &seq_len + ); + if (!ok) return js_false; + + return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); +} + +static ant_value_t rl_move_cursor(ant_t *js, ant_value_t *args, int nargs) { + int dx = 0; + int dy = 0; + if (!tty_ctrl_parse_move_cursor_args(args, nargs, 1, 2, &dx, &dy)) return js_false; + + bool ok = true; + if (dx != 0) { + char seq_x[32]; + size_t len_x = 0; + ok = tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x); + if (ok) ok = tty_ctrl_write_stream(stdout, seq_x, len_x, false); + } + + if (ok && dy != 0) { + char seq_y[32]; + size_t len_y = 0; + ok = tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y); + if (ok) ok = tty_ctrl_write_stream(stdout, seq_y, len_y, false); + } + + if (!ok) return js_false; + return tty_ctrl_bool_result(js, fflush(stdout) == 0); +} + +static ant_value_t rl_emit_keypress_events(ant_t *js, ant_value_t *args, int nargs) { + if (nargs > 0) { + ant_value_t stdin_obj = js_get(js, js_get(js, js_glob(js), "process"), "stdin"); + if (stdin_obj != args[0]) { + return js_mkerr(js, "emitKeypressEvents only supports process.stdin"); + } + } + process_enable_keypress_events(); + return js_mkundef(); +} + +bool has_active_readline_interfaces(void) { + rl_interface_t *iface, *tmp; + HASH_ITER(hh, interfaces, iface, tmp) { + if (!iface->closed && iface->reading) return true; + } + return false; +} + +static void rl_async_iter_compact_queue(ant_t *js, ant_value_t state, const char *queue_key, const char *head_key) { + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); + ant_offset_t head = rl_async_iter_queue_head(js, state, head_key); + ant_offset_t len = vtype(queue) == T_ARR ? js_arr_len(js, queue) : 0; + ant_value_t compact = 0; + + if (vtype(queue) != T_ARR || head == 0) return; + + if (head >= len) { + js_set(js, state, queue_key, js_mkarr(js)); + js_set(js, state, head_key, js_mknum(0)); + return; + } + + if (head <= 32 && head * 2 < len) return; + + compact = js_mkarr(js); + for (ant_offset_t i = head; i < len; i++) js_arr_push(js, compact, js_arr_get(js, queue, i)); + js_set(js, state, queue_key, compact); + js_set(js, state, head_key, js_mknum(0)); +} + +static void rl_async_iter_queue_push(ant_t *js, ant_value_t state, const char *queue_key, ant_value_t value) { + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); + if (vtype(queue) == T_ARR) js_arr_push(js, queue, value); +} + +static ant_value_t rl_async_iter_queue_shift(ant_t *js, ant_value_t state, const char *queue_key, const char *head_key) { + ant_value_t queue = rl_async_iter_queue(js, state, queue_key); + ant_offset_t head = rl_async_iter_queue_head(js, state, head_key); + ant_offset_t len = vtype(queue) == T_ARR ? js_arr_len(js, queue) : 0; + ant_value_t value = js_mkundef(); + + if (vtype(queue) != T_ARR || head >= len) return js_mkundef(); + value = js_arr_get(js, queue, head); + rl_async_iter_set_queue_head(js, state, head_key, head + 1); + rl_async_iter_compact_queue(js, state, queue_key, head_key); - RLEventType *evt, *tmp; - HASH_ITER(hh, iface->events, evt, tmp) { - HASH_DEL(iface->events, evt); - free(evt->event_type); - free(evt); + return value; +} + +static void rl_async_iter_cleanup(ant_t *js, ant_value_t state) { + ant_value_t iface_obj = is_object_type(state) ? js_get(js, state, "iface") : js_mkundef(); + rl_interface_t *iface = get_interface(js, iface_obj); + if (!iface) return; + + rl_remove_listener(js, iface, "line", js_get(js, state, "onLine")); + rl_remove_listener(js, iface, "close", js_get(js, state, "onClose")); +} + +static void rl_async_iter_finish(ant_t *js, ant_value_t state) { + if (!is_object_type(state)) return; + + js_set(js, state, "done", js_true); + rl_async_iter_cleanup(js, state); + + for (;;) { + ant_value_t pending = rl_async_iter_queue_shift(js, state, "pending", "pendingHead"); + if (vtype(pending) != T_PROMISE) break; + js_resolve_promise(js, pending, js_iter_result(js, false, js_mkundef())); + } +} + +static ant_value_t rl_async_iter_on_line(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t state = rl_async_iter_state(js, js_getcurrentfunc(js)); + ant_value_t line = nargs > 0 ? args[0] : js_mkundef(); + ant_value_t pending = 0; + + if (!is_object_type(state)) return js_mkundef(); + if (js_truthy(js, js_get(js, state, "done"))) return js_mkundef(); + + pending = rl_async_iter_queue_shift(js, state, "pending", "pendingHead"); + if (vtype(pending) == T_PROMISE) { + js_resolve_promise(js, pending, js_iter_result(js, true, line)); + } else rl_async_iter_queue_push(js, state, "buffer", line); + + return js_mkundef(); +} + +static ant_value_t rl_async_iter_on_close(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t state = rl_async_iter_state(js, js_getcurrentfunc(js)); + rl_async_iter_finish(js, state); + return js_mkundef(); +} + +static ant_value_t rl_async_iter_next(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t state = rl_async_iter_state(js, js_getthis(js)); + ant_value_t promise = js_mkpromise(js); + ant_value_t value = 0; + + if (!is_object_type(state)) { + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); + return promise; + } + + value = rl_async_iter_queue_shift(js, state, "buffer", "bufferHead"); + if (vtype(value) != T_UNDEF) { + js_resolve_promise(js, promise, js_iter_result(js, true, value)); + return promise; + } + + if (js_truthy(js, js_get(js, state, "done"))) { + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); + return promise; + } + + rl_async_iter_queue_push(js, state, "pending", promise); + return promise; +} + +static ant_value_t rl_async_iter_return(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t state = rl_async_iter_state(js, js_getthis(js)); + ant_value_t promise = js_mkpromise(js); + + if (!is_object_type(state)) { + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); + return promise; } + + if (!js_truthy(js, js_get(js, state, "done"))) { + ant_value_t iface_obj = js_get(js, state, "iface"); + rl_interface_t *iface = get_interface(js, iface_obj); + + if (iface && !iface->closed) { + ant_value_t old_this = js_getthis(js); + js_setthis(js, iface_obj); + rl_interface_close(js, NULL, 0); + js_setthis(js, old_this); + } else rl_async_iter_finish(js, state); + } + + js_resolve_promise(js, promise, js_iter_result(js, false, js_mkundef())); + return promise; +} + +static ant_value_t rl_get_async_iter_proto(ant_t *js) { + if (is_object_type(g_rl_async_iter_proto)) return g_rl_async_iter_proto; + + g_rl_async_iter_proto = js_mkobj(js); + js_set(js, g_rl_async_iter_proto, "next", js_mkfun(rl_async_iter_next)); + js_set(js, g_rl_async_iter_proto, "return", js_mkfun(rl_async_iter_return)); + js_set_sym(js, g_rl_async_iter_proto, get_asyncIterator_sym(), js_mkfun(sym_this_cb)); + js_set_sym(js, g_rl_async_iter_proto, get_toStringTag_sym(), js_mkstr(js, "AsyncIterator", 13)); + + return g_rl_async_iter_proto; +} + +static ant_value_t rl_interface_async_iterator(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t this_obj = js_getthis(js); + rl_interface_t *iface = get_interface(js, this_obj); - free(iface); + ant_value_t iterator = 0; + ant_value_t state = 0; + ant_value_t on_line = 0; + ant_value_t on_close = 0; + + if (!iface) return js_mkerr(js, "Invalid Interface"); + + iterator = js_mkobj(js); + state = js_mkobj(js); + + js_set_proto_init(iterator, rl_get_async_iter_proto(js)); + js_set_slot_wb(js, iterator, SLOT_DATA, state); + + js_set(js, state, "iface", this_obj); + js_set(js, state, "buffer", js_mkarr(js)); + js_set(js, state, "bufferHead", js_mknum(0)); + js_set(js, state, "pending", js_mkarr(js)); + js_set(js, state, "pendingHead", js_mknum(0)); + js_set(js, state, "done", js_bool(iface->closed)); + + if (!iface->closed) { + on_line = js_heavy_mkfun(js, rl_async_iter_on_line, state); + on_close = js_heavy_mkfun(js, rl_async_iter_on_close, state); + js_set(js, state, "onLine", on_line); + js_set(js, state, "onClose", on_close); + + if (!rl_add_listener(js, iface, "line", on_line, false) || !rl_add_listener(js, iface, "close", on_close, false)) { + rl_remove_listener(js, iface, "line", on_line); + rl_remove_listener(js, iface, "close", on_close); + return js_mkerr(js, "listener must be a function"); + }} + + return iterator; +} + +static ant_value_t rl_get_interface_proto(ant_t *js) { + if (is_object_type(g_rl_interface_proto)) return g_rl_interface_proto; + + g_rl_interface_proto = js_mkobj(js); + js_set_proto_init(g_rl_interface_proto, eventemitter_prototype(js)); + + js_set(js, g_rl_interface_proto, "close", js_mkfun(rl_interface_close)); + js_set(js, g_rl_interface_proto, "pause", js_mkfun(rl_interface_pause)); + js_set(js, g_rl_interface_proto, "resume", js_mkfun(rl_interface_resume)); + js_set(js, g_rl_interface_proto, "prompt", js_mkfun(rl_interface_prompt)); + js_set(js, g_rl_interface_proto, "setPrompt", js_mkfun(rl_interface_set_prompt)); + js_set(js, g_rl_interface_proto, "getPrompt", js_mkfun(rl_interface_get_prompt)); + js_set(js, g_rl_interface_proto, "write", js_mkfun(rl_interface_write)); + js_set(js, g_rl_interface_proto, "question", js_mkfun(rl_interface_question_callback)); + js_set(js, g_rl_interface_proto, "getCursorPos", js_mkfun(rl_interface_get_cursor_pos)); + + js_set_getter_desc(js, g_rl_interface_proto, "line", 4, js_mkfun(rl_interface_line_getter), JS_DESC_E | JS_DESC_C); + js_set_getter_desc(js, g_rl_interface_proto, "cursor", 6, js_mkfun(rl_interface_cursor_getter), JS_DESC_E | JS_DESC_C); + js_set_getter_desc(js, g_rl_interface_proto, "closed", 6, js_mkfun(rl_interface_closed_getter), JS_DESC_E | JS_DESC_C); + js_set_sym(js, g_rl_interface_proto, get_asyncIterator_sym(), js_mkfun(rl_interface_async_iterator)); + js_set_sym(js, g_rl_interface_proto, get_toStringTag_sym(), js_mkstr(js, "Interface", 9)); + + return g_rl_interface_proto; } static ant_value_t rl_create_interface(ant_t *js, ant_value_t *args, int nargs) { @@ -1095,7 +1210,6 @@ static ant_value_t rl_create_interface(ant_t *js, ant_value_t *args, int nargs) iface->reading = false; iface->pending_question_resolve = js_mkundef(); iface->pending_question_reject = js_mkundef(); - iface->events = NULL; iface->tty_initialized = false; iface->escape_state = 0; iface->escape_len = 0; @@ -1161,33 +1275,10 @@ static ant_value_t rl_create_interface(ant_t *js, ant_value_t *args, int nargs) HASH_ADD(hh, interfaces, id, sizeof(uint64_t), iface); ant_value_t obj = js_mkobj(js); + js_set_proto_init(obj, rl_get_interface_proto(js)); + iface->js_obj = obj; js_set(js, obj, "_rl_id", js_mknum((double)iface->id)); - - js_set(js, obj, "on", js_mkfun(rl_interface_on)); - js_set(js, obj, "once", js_mkfun(rl_interface_once)); - js_set(js, obj, "off", js_mkfun(rl_interface_off)); - js_set(js, obj, "addListener", js_mkfun(rl_interface_on)); - js_set(js, obj, "removeListener", js_mkfun(rl_interface_off)); - js_set(js, obj, "removeAllListeners", js_mkfun(rl_interface_remove_all_listeners)); - js_set(js, obj, "emit", js_mkfun(rl_interface_emit)); - - js_set(js, obj, "close", js_mkfun(rl_interface_close)); - js_set(js, obj, "pause", js_mkfun(rl_interface_pause)); - js_set(js, obj, "resume", js_mkfun(rl_interface_resume)); - js_set(js, obj, "prompt", js_mkfun(rl_interface_prompt)); - js_set(js, obj, "setPrompt", js_mkfun(rl_interface_set_prompt)); - js_set(js, obj, "getPrompt", js_mkfun(rl_interface_get_prompt)); - js_set(js, obj, "write", js_mkfun(rl_interface_write)); - js_set(js, obj, "question", js_mkfun(rl_interface_question_callback)); - js_set(js, obj, "getCursorPos", js_mkfun(rl_interface_get_cursor_pos)); - - js_set_getter_desc(js, obj, "line", 4, js_mkfun(rl_interface_line_getter), JS_DESC_E | JS_DESC_C); - js_set_getter_desc(js, obj, "cursor", 6, js_mkfun(rl_interface_cursor_getter), JS_DESC_E | JS_DESC_C); - js_set_getter_desc(js, obj, "closed", 6, js_mkfun(rl_interface_closed_getter), JS_DESC_E | JS_DESC_C); - js_set(js, obj, "terminal", js_bool(iface->terminal)); - js_set_sym(js, obj, get_asyncIterator_sym(), js_mkfun(rl_interface_async_iterator)); - js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, "Interface", 9)); start_reading(iface); @@ -1202,81 +1293,6 @@ static ant_value_t rl_create_interface_promises(ant_t *js, ant_value_t *args, in return iface_obj; } -static ant_value_t rl_clear_line(ant_t *js, ant_value_t *args, int nargs) { - int dir = 0; - if (!tty_ctrl_parse_clear_line_dir(args, nargs, 1, &dir)) return js_false; - - size_t seq_len = 0; - const char *seq = tty_ctrl_clear_line_seq(dir, &seq_len); - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); -} - -static ant_value_t rl_clear_screen_down(ant_t *js, ant_value_t *args, int nargs) { - size_t seq_len = 0; - const char *seq = tty_ctrl_clear_screen_down_seq(&seq_len); - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); -} - -static ant_value_t rl_cursor_to(ant_t *js, ant_value_t *args, int nargs) { - tty_ctrl_cursor_to_args_t parsed; - if (!tty_ctrl_parse_cursor_to_args(args, nargs, 1, 2, &parsed)) return js_false; - - char seq[64]; - size_t seq_len = 0; - bool ok = tty_ctrl_build_cursor_to( - seq, sizeof(seq), - parsed.x, parsed.has_y, parsed.y, - &seq_len - ); - if (!ok) return js_false; - - return tty_ctrl_bool_result(js, tty_ctrl_write_stream(stdout, seq, seq_len, true)); -} - -static ant_value_t rl_move_cursor(ant_t *js, ant_value_t *args, int nargs) { - int dx = 0; - int dy = 0; - if (!tty_ctrl_parse_move_cursor_args(args, nargs, 1, 2, &dx, &dy)) return js_false; - - bool ok = true; - if (dx != 0) { - char seq_x[32]; - size_t len_x = 0; - ok = tty_ctrl_build_move_cursor_axis(seq_x, sizeof(seq_x), dx, true, &len_x); - if (ok) ok = tty_ctrl_write_stream(stdout, seq_x, len_x, false); - } - - if (ok && dy != 0) { - char seq_y[32]; - size_t len_y = 0; - ok = tty_ctrl_build_move_cursor_axis(seq_y, sizeof(seq_y), dy, false, &len_y); - if (ok) ok = tty_ctrl_write_stream(stdout, seq_y, len_y, false); - } - - if (!ok) return js_false; - return tty_ctrl_bool_result(js, fflush(stdout) == 0); -} - -static ant_value_t rl_emit_keypress_events(ant_t *js, ant_value_t *args, int nargs) { - if (nargs > 0) { - ant_value_t stdin_obj = js_get(js, js_get(js, js_glob(js), "process"), "stdin"); - if (stdin_obj != args[0]) { - return js_mkerr(js, "emitKeypressEvents only supports process.stdin"); - } - } - process_enable_keypress_events(); - return js_mkundef(); -} - -bool has_active_readline_interfaces(void) { - rl_interface_t *iface, *tmp; - HASH_ITER(hh, interfaces, iface, tmp) { - if (!iface->closed && iface->reading) return true; - } - return false; -} - - ant_value_t readline_library(ant_t *js) { ant_value_t lib = js_mkobj(js); @@ -1306,6 +1322,9 @@ ant_value_t readline_promises_library(ant_t *js) { } void gc_mark_readline(ant_t *js, gc_mark_fn mark) { + if (g_rl_async_iter_proto) mark(js, g_rl_async_iter_proto); + if (g_rl_interface_proto) mark(js, g_rl_interface_proto); + rl_interface_t *iface, *tmp; HASH_ITER(hh, interfaces, iface, tmp) { mark(js, iface->input_stream); @@ -1314,9 +1333,5 @@ void gc_mark_readline(ant_t *js, gc_mark_fn mark) { mark(js, iface->js_obj); mark(js, iface->pending_question_resolve); mark(js, iface->pending_question_reject); - - RLEventType *evt, *evt_tmp; - HASH_ITER(hh, iface->events, evt, evt_tmp) for (int i = 0; i < evt->listener_count; i++) - mark(js, evt->listeners[i].listener); } } diff --git a/src/modules/tty.c b/src/modules/tty.c index 26f9ac7..ba3d539 100644 --- a/src/modules/tty.c +++ b/src/modules/tty.c @@ -37,8 +37,6 @@ #include "modules/symbol.h" #include "modules/tty.h" - - static bool parse_fd(ant_value_t value, int *fd_out) { int fd = 0; if (!tty_ctrl_parse_int_value(value, &fd)) return false;