MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate coro and event loop to seperate files

+604 -603
-6
include/ant.h
··· 28 28 #define JS_NEG_INF ((double)(-INFINITY)) 29 29 30 30 #define ANT_LIMIT_SIZE_CACHE 16384 31 - #define CORO_PER_TICK_LIMIT 10000 32 31 33 32 #define JS_DESC_W (1 << 0) 34 33 #define JS_DESC_E (1 << 1) ··· 156 155 void js_reject_promise(ant_t *js, jsval_t promise, jsval_t value); 157 156 void js_check_unhandled_rejections(ant_t *js); 158 157 void js_process_promise_handlers(ant_t *js, uint32_t promise_id); 159 - 160 - void js_run_event_loop(ant_t *js); 161 - void js_poll_events(ant_t *js); 162 158 void js_setup_import_meta(ant_t *js, const char *filename); 163 159 164 160 typedef jsval_t (*ant_library_init_fn)(ant_t *js); ··· 188 184 void js_print_stack_trace(FILE *stream); 189 185 void js_set_needs_gc(ant_t *js, bool needs); 190 186 void js_set_gc_suppress(ant_t *js, bool suppress); 191 - 192 - size_t js_gc_compact(ant_t *js); 193 187 194 188 #endif
+3
include/gc.h
··· 2 2 #define GC_H 3 3 4 4 #include <types.h> 5 + #include <stddef.h> 5 6 6 7 #define GC_FWD_LOAD_FACTOR 70 7 8 #define GC_ROOTS_INITIAL_CAP 32 ··· 9 10 #define GC_FWD_ARGS jsval_t (*fwd_val)(void *ctx, jsval_t old), void *ctx 10 11 #define GC_UPDATE_ARGS ant_t *js, jsoff_t (*fwd_off)(void *ctx, jsoff_t old), GC_FWD_ARGS 11 12 #define GC_OP_VAL_ARGS void (*op_val)(void *ctx, jsval_t *val), void *ctx 13 + 14 + size_t js_gc_compact(ant_t *js); 12 15 13 16 #endif
+17 -3
include/internal.h
··· 3 3 4 4 #include "ant.h" 5 5 #include "gc.h" 6 + #include <utarray.h> 7 + 8 + extern const UT_icd jsoff_icd; 9 + extern const UT_icd jsval_icd; 10 + 11 + extern UT_array *global_scope_stack; 12 + extern UT_array *saved_scope_stack; 6 13 7 14 struct for_let_ctx { 8 15 const char *var_name; // interned variable name ··· 94 101 #define T_OBJECT_MASK (TYPE_FLAG(T_OBJ) | TYPE_FLAG(T_ARR) | TYPE_FLAG(T_FUNC) | TYPE_FLAG(T_PROMISE)) 95 102 #define T_NON_NUMERIC_MASK (TYPE_FLAG(T_STR) | TYPE_FLAG(T_ARR) | TYPE_FLAG(T_FUNC) | TYPE_FLAG(T_CFUNC) | TYPE_FLAG(T_OBJ)) 96 103 97 - jsoff_t esize(jsoff_t w); 104 + bool is_internal_prop(const char *key, jsoff_t klen); 98 105 99 106 void js_gc_reserve_roots(GC_UPDATE_ARGS); 100 107 void js_gc_update_roots(GC_UPDATE_ARGS); 101 108 102 - bool js_has_pending_coroutines(void); 103 - bool is_internal_prop(const char *key, jsoff_t klen); 109 + jsoff_t esize(jsoff_t w); 110 + jsval_t tov(double d); 111 + double tod(jsval_t v); 104 112 105 113 jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len); 106 114 jsoff_t vstr(struct js *js, jsval_t value, jsoff_t *len); 107 115 108 116 jsval_t mkval(uint8_t type, uint64_t data); 109 117 jsval_t resolveprop(struct js *js, jsval_t v); 118 + 119 + jsval_t call_js(ant_t *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope); 120 + jsval_t call_js_internal(ant_t *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *bound_args, int bound_argc, jsval_t func_val); 121 + 122 + jsval_t call_js_with_args(ant_t *js, jsval_t fn, jsval_t *args, int nargs); 123 + jsval_t call_js_code_with_args(ant_t *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *args, int nargs, jsval_t func_val); 110 124 111 125 #define is_non_numeric(v) ((1u << vtype(v)) & T_NON_NUMERIC_MASK) 112 126 #define is_object_type(v) ((1u << vtype(v)) & T_OBJECT_MASK)
+1 -1
include/modules/crypto.h
··· 1 1 #ifndef CRYPTO_H 2 2 #define CRYPTO_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 void init_crypto_module(); 7 7 jsval_t crypto_library(struct js *js);
+1 -1
include/modules/events.h
··· 1 1 #ifndef EVENTS_H 2 2 #define EVENTS_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 void init_events_module(void); 7 7 jsval_t events_library(struct js *js);
+1 -1
include/modules/io.h
··· 1 1 #ifndef IO_H 2 2 #define IO_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 #include <stdio.h> 6 6 #include <stdbool.h> 7 7
+1 -1
include/modules/json.h
··· 2 2 #define JSON_H 3 3 #define YYJSON_SKIP_VALUE ((yyjson_mut_val *)-1) 4 4 5 - #include "ant.h" 5 + #include "types.h" 6 6 7 7 void init_json_module(void); 8 8
+1 -1
include/modules/os.h
··· 1 1 #ifndef ANT_OS_MODULE_H 2 2 #define ANT_OS_MODULE_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 jsval_t os_library(struct js *js); 7 7
+1 -1
include/modules/path.h
··· 1 1 #ifndef ANT_PATH_MODULE_H 2 2 #define ANT_PATH_MODULE_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 jsval_t path_library(struct js *js); 7 7
+1 -1
include/modules/shell.h
··· 1 1 #ifndef ANT_SHELL_MODULE_H 2 2 #define ANT_SHELL_MODULE_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 jsval_t shell_library(struct js *js); 7 7
+2 -1
include/modules/symbol.h
··· 1 1 #ifndef SYMBOL_H 2 2 #define SYMBOL_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 + #include <stddef.h> 5 6 6 7 void init_symbol_module(void); 7 8
-2
include/modules/timer.h
··· 5 5 #include "types.h" 6 6 7 7 void init_timer_module(void); 8 - void process_timers(struct js *js); 9 8 void process_microtasks(struct js *js); 10 9 void process_immediates(struct js *js); 11 10 void queue_microtask(struct js *js, jsval_t callback); ··· 15 14 int has_pending_timers(void); 16 15 int has_pending_microtasks(void); 17 16 int has_pending_immediates(void); 18 - int64_t get_next_timer_timeout(void); 19 17 20 18 #endif
+27
include/reactor.h
··· 1 + #ifndef REACTOR_H 2 + #define REACTOR_H 3 + 4 + #include "types.h" 5 + 6 + typedef enum { 7 + WORK_MICROTASKS = 1 << 0, 8 + WORK_TIMERS = 1 << 1, 9 + WORK_IMMEDIATES = 1 << 2, 10 + WORK_COROUTINES = 1 << 3, 11 + WORK_COROUTINES_READY = 1 << 4, 12 + WORK_FETCHES = 1 << 5, 13 + WORK_FS_OPS = 1 << 6, 14 + WORK_CHILD_PROCS = 1 << 7, 15 + WORK_READLINE = 1 << 8, 16 + WORK_STDIN = 1 << 9, 17 + } work_flags_t; 18 + 19 + #define WORK_TASKS (WORK_MICROTASKS | WORK_TIMERS | WORK_IMMEDIATES | WORK_COROUTINES | WORK_FETCHES) 20 + #define WORK_PENDING (WORK_TASKS | WORK_FS_OPS | WORK_CHILD_PROCS | WORK_READLINE | WORK_STDIN) 21 + #define WORK_BLOCKING (WORK_MICROTASKS | WORK_IMMEDIATES | WORK_COROUTINES_READY) 22 + #define WORK_ASYNC (WORK_READLINE | WORK_STDIN | WORK_TIMERS | WORK_FETCHES | WORK_FS_OPS | WORK_CHILD_PROCS) 23 + 24 + void js_poll_events(ant_t *js) ; 25 + void js_run_event_loop(ant_t *js); 26 + 27 + #endif
+1 -1
include/roots.h
··· 1 1 #ifndef ROOTS_H 2 2 #define ROOTS_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 6 6 jshdl_t js_root(struct js *js, jsval_t val); 7 7 jsval_t js_deref(struct js *js, jshdl_t h);
+1 -1
include/runtime.h
··· 1 1 #ifndef RUNTIME_H 2 2 #define RUNTIME_H 3 3 4 - #include "ant.h" 4 + #include "types.h" 5 5 #include <argtable3.h> 6 6 7 7 #define ANT_RUNTIME_CRYPTO_INIT (1u << 0)
+3 -3
include/snapshot.h
··· 1 1 #ifndef ANT_SNAPSHOT_LOADER_H 2 2 #define ANT_SNAPSHOT_LOADER_H 3 3 4 - #include <stdint.h> 5 - #include "ant.h" 4 + #include <stddef.h> 5 + #include "types.h" 6 6 7 - jsval_t ant_load_snapshot(struct js *js); 7 + jsval_t ant_load_snapshot(ant_t *js); 8 8 const uint8_t *ant_get_snapshot_source(size_t *len); 9 9 10 10 #endif
+88
include/sugar.h
··· 1 + #ifndef SUGAR_H 2 + #define SUGAR_H 3 + 4 + #include "types.h" 5 + 6 + #include <stddef.h> 7 + #include <stdlib.h> 8 + #include <utarray.h> 9 + #include <minicoro.h> 10 + 11 + #define CORO_MALLOC(size) calloc(1, size) 12 + #define CORO_FREE(ptr) free(ptr) 13 + 14 + #define CORO_PER_TICK_LIMIT 100000 15 + 16 + typedef enum { 17 + CORO_ASYNC_AWAIT, 18 + CORO_GENERATOR, 19 + CORO_ASYNC_GENERATOR 20 + } coroutine_type_t; 21 + 22 + typedef struct coroutine { 23 + struct js *js; 24 + coroutine_type_t type; 25 + jsval_t scope; 26 + jsval_t this_val; 27 + jsval_t super_val; 28 + jsval_t new_target; 29 + jsval_t awaited_promise; 30 + jsval_t result; 31 + jsval_t async_func; 32 + jsval_t *args; 33 + int nargs; 34 + bool is_settled; 35 + bool is_error; 36 + bool is_done; 37 + jsoff_t resume_point; 38 + jsval_t yield_value; 39 + struct coroutine *prev; 40 + struct coroutine *next; 41 + mco_coro* mco; 42 + bool mco_started; 43 + bool is_ready; 44 + struct for_let_ctx *for_let_stack; 45 + int for_let_stack_len; 46 + int for_let_stack_cap; 47 + UT_array *scope_stack; 48 + } coroutine_t; 49 + 50 + typedef struct { 51 + coroutine_t *head; 52 + coroutine_t *tail; 53 + } coroutine_queue_t; 54 + 55 + typedef struct { 56 + struct js *js; 57 + const char *code; 58 + size_t code_len; 59 + jsval_t closure_scope; 60 + jsval_t result; 61 + jsval_t promise; 62 + bool has_error; 63 + coroutine_t *coro; 64 + } async_exec_context_t; 65 + 66 + typedef struct { 67 + jsval_t scope; 68 + UT_array *scope_stack; 69 + } coro_saved_state_t; 70 + 71 + extern coroutine_queue_t pending_coroutines; 72 + extern uint32_t coros_this_tick; 73 + 74 + void enqueue_coroutine(coroutine_t *coro); 75 + void remove_coroutine(coroutine_t *coro); 76 + void free_coroutine(coroutine_t *coro); 77 + 78 + coro_saved_state_t coro_enter(struct js *js, coroutine_t *coro); 79 + void coro_leave(struct js *js, coroutine_t *coro, coro_saved_state_t saved); 80 + 81 + jsval_t start_async_in_coroutine(struct js *js, const char *code, size_t code_len, jsval_t closure_scope, jsval_t *args, int nargs); 82 + jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 83 + jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 84 + 85 + bool has_ready_coroutines(void); 86 + bool has_pending_coroutines(void); 87 + 88 + #endif
+2
libant/meson.build
··· 25 25 '../src/roots.c', 26 26 '../src/utils.c', 27 27 '../src/utf8.c', 28 + '../src/reactor.c', 29 + '../src/sugar.c', 28 30 '../src/ant.c', 29 31 '../src/errors.c', 30 32 '../src/stack.c',
+8 -4
libant/scripts/header.sh
··· 18 18 "config.h:$CONFIG_H" 19 19 "common.h:$INCLUDE_DIR/common.h" 20 20 "types.h:$INCLUDE_DIR/types.h" 21 + "gc.h:$INCLUDE_DIR/gc.h" 21 22 "ant.h:$INCLUDE_DIR/ant.h" 22 - "internal.h:$INCLUDE_DIR/internal.h" 23 23 "roots.h:$INCLUDE_DIR/roots.h" 24 + "utarray.h:$VENDOR_DIR/uthash-2.3.0/src/utarray.h" 25 + "internal.h:$INCLUDE_DIR/internal.h" 26 + "minicoro.h:$VENDOR_DIR/minicoro/minicoro.h" 27 + "sugar.h:$INCLUDE_DIR/sugar.h" 28 + "reactor.h:$INCLUDE_DIR/reactor.h" 24 29 "tokens.h:$INCLUDE_DIR/tokens.h" 25 30 "stack.h:$INCLUDE_DIR/stack.h" 26 31 "errors.h:$INCLUDE_DIR/errors.h" 27 32 "compat.h:$INCLUDE_DIR/compat.h" 28 33 "utils.h:$INCLUDE_DIR/utils.h" 29 - "minicoro.h:$VENDOR_DIR/minicoro/minicoro.h" 30 34 "runtime.h:$INCLUDE_DIR/runtime.h" 31 35 "esm/remote.h:$INCLUDE_DIR/esm/remote.h" 32 36 "argtable3.h:$VENDOR_DIR/argtable-v3.3.0.116da6c/src/argtable3.h" ··· 96 100 continue 97 101 fi 98 102 99 - if [[ "$line" =~ ^[[:space:]]*#[[:space:]]*include[[:space:]]+\"(config\.h|common\.h|types\.h|compat\.h|ant\.h|utils\.h|arena\.h|runtime\.h|internal\.h)\" ]]; then 103 + if [[ "$line" =~ ^[[:space:]]*#[[:space:]]*include[[:space:]]+\"(config\.h|common\.h|gc.\h|types\.h|compat\.h|ant\.h|utils\.h|arena\.h|runtime\.h|internal\.h)\" ]]; then 100 104 continue 101 105 fi 102 106 if [[ "$line" =~ ^[[:space:]]*#[[:space:]]*include[[:space:]]+\"esm/ ]]; then ··· 106 110 continue 107 111 fi 108 112 109 - if [[ "$line" =~ ^[[:space:]]*#[[:space:]]*include[[:space:]]+\<(config|common|argtable3)\.h\> ]]; then 113 + if [[ "$line" =~ ^[[:space:]]*#[[:space:]]*include[[:space:]]+\<(config|common|argtable3|types|utarray|minicoro)\.h\> ]]; then 110 114 continue 111 115 fi 112 116
+2
meson.build
··· 24 24 'src/roots.c', 25 25 'src/utils.c', 26 26 'src/utf8.c', 27 + 'src/reactor.c', 28 + 'src/sugar.c', 27 29 'src/ant.c', 28 30 'src/errors.c', 29 31 'src/stack.c',
+18 -449
src/ant.c
··· 11 11 #include "utils.h" 12 12 #include "runtime.h" 13 13 #include "internal.h" 14 + #include "sugar.h" 14 15 #include "stack.h" 15 16 #include "errors.h" 16 17 #include "utf8.h" ··· 29 30 #include <float.h> 30 31 #include <tlsuv/tlsuv.h> 31 32 #include <tlsuv/http.h> 33 + #include <minicoro.h> 32 34 33 35 #ifdef _WIN32 34 36 #include <sys/stat.h> ··· 38 40 #include <sys/resource.h> 39 41 #endif 40 42 41 - #define MCO_USE_VMEM_ALLOCATOR 42 - #define MCO_ZERO_MEMORY 43 - #define MCO_DEFAULT_STACK_SIZE (1024 * 1024) 44 - #define MINICORO_IMPL 45 - #include <minicoro.h> 46 - 47 43 #include "modules/fs.h" 48 44 #include "modules/timer.h" 49 45 #include "modules/fetch.h" ··· 55 51 #include "modules/json.h" 56 52 #include "modules/buffer.h" 57 53 #include "esm/remote.h" 58 - 59 - #define CORO_MALLOC(size) calloc(1, size) 60 - #define CORO_FREE(ptr) free(ptr) 61 54 62 55 #define D(x) ((double)(x)) 63 56 ··· 78 71 int capacity; 79 72 } this_stack_t; 80 73 81 - typedef enum { 82 - CORO_ASYNC_AWAIT, 83 - CORO_GENERATOR, 84 - CORO_ASYNC_GENERATOR 85 - } coroutine_type_t; 86 - 87 - typedef struct coroutine { 88 - struct js *js; 89 - coroutine_type_t type; 90 - jsval_t scope; 91 - jsval_t this_val; 92 - jsval_t super_val; 93 - jsval_t new_target; 94 - jsval_t awaited_promise; 95 - jsval_t result; 96 - jsval_t async_func; 97 - jsval_t *args; 98 - int nargs; 99 - bool is_settled; 100 - bool is_error; 101 - bool is_done; 102 - jsoff_t resume_point; 103 - jsval_t yield_value; 104 - struct coroutine *prev; 105 - struct coroutine *next; 106 - mco_coro* mco; 107 - bool mco_started; 108 - bool is_ready; 109 - struct for_let_ctx *for_let_stack; 110 - int for_let_stack_len; 111 - int for_let_stack_cap; 112 - UT_array *scope_stack; 113 - } coroutine_t; 74 + static this_stack_t global_this_stack = {NULL, 0, 0}; 114 75 115 - typedef struct { 116 - coroutine_t *head; 117 - coroutine_t *tail; 118 - } coroutine_queue_t; 119 - 120 - typedef struct { 121 - struct js *js; 122 - const char *code; 123 - size_t code_len; 124 - jsval_t closure_scope; 125 - jsval_t result; 126 - jsval_t promise; 127 - bool has_error; 128 - coroutine_t *coro; 129 - } async_exec_context_t; 130 - 131 - static const UT_icd jsoff_icd = { 76 + const UT_icd jsoff_icd = { 132 77 .sz = sizeof(jsoff_t), 133 78 .init = NULL, 134 79 .copy = NULL, 135 80 .dtor = NULL, 136 81 }; 137 82 138 - static const UT_icd jsval_icd = { 83 + const UT_icd jsval_icd = { 139 84 .sz = sizeof(jsval_t), 140 85 .init = NULL, 141 86 .copy = NULL, 142 87 .dtor = NULL, 143 88 }; 144 89 145 - static UT_array *global_scope_stack = NULL; 146 - static UT_array *saved_scope_stack = NULL; 147 - static this_stack_t global_this_stack = {NULL, 0, 0}; 148 - 149 - static uint32_t coros_this_tick = 0; 150 - static coroutine_queue_t pending_coroutines = {NULL, NULL}; 90 + UT_array *global_scope_stack = NULL; 91 + UT_array *saved_scope_stack = NULL; 151 92 152 93 typedef struct { 153 94 const char *name; ··· 164 105 }; 165 106 166 107 static UT_array *label_stack = NULL; 167 - 168 108 static const char *break_target_label = NULL; 169 109 static jsoff_t break_target_label_len = 0; 170 110 static const char *continue_target_label = NULL; ··· 407 347 return (t < sizeof(names) / sizeof(names[0])) ? names[t] : "??"; 408 348 } 409 349 410 - static jsval_t tov(double d) { union { double d; jsval_t v; } u = {d}; return u.v; } 411 - static double tod(jsval_t v) { union { jsval_t v; double d; } u = {v}; return u.d; } 350 + jsval_t tov(double d) { 351 + union { double d; jsval_t v; } u = {d}; return u.v; 352 + } 353 + 354 + double tod(jsval_t v) { 355 + union { jsval_t v; double d; } u = {v}; return u.d; 356 + } 412 357 413 358 static bool is_tagged(jsval_t v) { 414 359 return (v >> 53) == NANBOX_PREFIX_CHK; ··· 789 734 static jsval_t proxy_set(struct js *js, jsval_t proxy, const char *key, size_t key_len, jsval_t value); 790 735 static jsval_t proxy_has(struct js *js, jsval_t proxy, const char *key, size_t key_len); 791 736 static jsval_t proxy_delete(struct js *js, jsval_t proxy, const char *key, size_t key_len); 792 - static jsval_t call_js(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope); 793 - static jsval_t call_js_internal(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *bound_args, int bound_argc, jsval_t func_val); 794 - 795 - static jsval_t call_js_with_args(struct js *js, jsval_t fn, jsval_t *args, int nargs); 796 - static jsval_t call_js_code_with_args(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *args, int nargs, jsval_t func_val); 797 737 798 738 static inline bool push_this(jsval_t this_value); 799 739 static inline jsval_t pop_this(void); ··· 862 802 return d != 0.0 && !isnan(d); 863 803 } 864 804 } 865 - 866 - static bool has_ready_coroutines(void); 867 - static bool coro_stack_size_initialized = false; 868 - 869 - static void free_coroutine(coroutine_t *coro); 870 - static void for_let_swap_with_coro(struct js *js, coroutine_t *coro); 871 - 872 - typedef struct { 873 - jsval_t scope; 874 - UT_array *scope_stack; 875 - } coro_saved_state_t; 876 - 877 - static inline coro_saved_state_t coro_enter(struct js *js, coroutine_t *coro) { 878 - extern UT_array *global_scope_stack; 879 - coro_saved_state_t saved = { js->scope, global_scope_stack }; 880 - js->scope = coro->scope; 881 - global_scope_stack = coro->scope_stack; 882 - for_let_swap_with_coro(js, coro); 883 - return saved; 884 - } 885 - 886 - static inline void coro_leave(struct js *js, coroutine_t *coro, coro_saved_state_t saved) { 887 - extern UT_array *global_scope_stack; 888 - coro->scope = js->scope; 889 - coro->scope_stack = global_scope_stack; 890 - js->scope = saved.scope; 891 - global_scope_stack = saved.scope_stack; 892 - for_let_swap_with_coro(js, coro); 893 - } 894 - 895 - static size_t calculate_coro_stack_size(void) { 896 - if (coro_stack_size_initialized) return 0; 897 - coro_stack_size_initialized = true; 898 - const char *env_stack = getenv("ANT_CORO_STACK_SIZE"); 899 - if (env_stack) { 900 - size_t size = (size_t)atoi(env_stack) * 1024; 901 - if (size >= 32 * 1024 && size <= 8 * 1024 * 1024) return size; 902 - } 903 - return 0; 904 - } 905 - 906 - static void mco_async_entry(mco_coro* mco) { 907 - async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(mco); 908 - 909 - struct js *js = ctx->js; 910 - coroutine_t *coro = ctx->coro; 911 - jsval_t result; 912 - 913 - jsval_t saved_super = js->super_val; 914 - jsval_t saved_new_target = js->new_target; 915 - if (coro) { 916 - js->super_val = coro->super_val; 917 - js->new_target = coro->new_target; 918 - } 919 - 920 - if (coro && coro->nargs > 0 && coro->args) { 921 - result = call_js_code_with_args(js, ctx->code, (jsoff_t)ctx->code_len, ctx->closure_scope, coro->args, coro->nargs, js_mkundef()); 922 - } else result = call_js(js, ctx->code, (jsoff_t)ctx->code_len, ctx->closure_scope); 923 - 924 - js->super_val = saved_super; 925 - js->new_target = saved_new_target; 926 - 927 - ctx->result = result; 928 - ctx->has_error = is_err(result); 929 - 930 - if (ctx->has_error) { 931 - jsval_t reject_value = js->thrown_value; 932 - if (vtype(reject_value) == T_UNDEF) { 933 - reject_value = js_mkstr(js, js->errmsg ? js->errmsg : "Unknown error", js->errmsg ? strlen(js->errmsg) : 13); 934 - } 935 - js->flags &= (uint8_t)~F_THROW; 936 - js->thrown_value = js_mkundef(); 937 - js_reject_promise(js, ctx->promise, reject_value); 938 - } else js_resolve_promise(js, ctx->promise, result); 939 - } 940 - 941 - static void enqueue_coroutine(coroutine_t *coro) { 942 - if (!coro) return; 943 - coro->next = NULL; 944 - coro->prev = pending_coroutines.tail; 945 - 946 - if (pending_coroutines.tail) { 947 - pending_coroutines.tail->next = coro; 948 - } else pending_coroutines.head = coro; 949 - pending_coroutines.tail = coro; 950 - } 951 - 952 - static void remove_coroutine(coroutine_t *coro) { 953 - if (!coro) return; 954 - 955 - if (coro->prev) { 956 - coro->prev->next = coro->next; 957 - } else pending_coroutines.head = coro->next; 958 - 959 - if (coro->next) { 960 - coro->next->prev = coro->prev; 961 - } else pending_coroutines.tail = coro->prev; 962 - 963 - coro->prev = NULL; 964 - coro->next = NULL; 965 - } 966 - 967 - inline bool js_has_pending_coroutines(void) { 968 - return pending_coroutines.head != NULL; 969 - } 970 - 971 - static bool has_ready_coroutines(void) { 972 - coroutine_t *temp = pending_coroutines.head; 973 - while (temp) { 974 - if (temp->is_ready) return true; 975 - temp = temp->next; 976 - } 977 - return false; 978 - } 979 - 980 - void js_poll_events(struct js *js) { 981 - coros_this_tick = 0; 982 - 983 - fetch_poll_events(); 984 - fs_poll_events(); 985 - child_process_poll_events(); 986 - 987 - int has_timers = has_pending_timers(); 988 - if (has_timers) { 989 - int64_t next_timeout_ms = get_next_timer_timeout(); 990 - if (next_timeout_ms <= 0) process_timers(js); 991 - } 992 - 993 - process_immediates(js); 994 - process_microtasks(js); 995 - 996 - for (coroutine_t *temp = pending_coroutines.head, *next; temp; temp = next) { 997 - next = temp->next; 998 - if (!temp->is_ready || !temp->mco || mco_status(temp->mco) != MCO_SUSPENDED) continue; 999 - remove_coroutine(temp); 1000 - 1001 - coro_saved_state_t saved = coro_enter(temp->js, temp); 1002 - mco_result res = mco_resume(temp->mco); 1003 - coro_leave(temp->js, temp, saved); 1004 - 1005 - if (res == MCO_SUCCESS && mco_status(temp->mco) != MCO_DEAD) { 1006 - temp->is_ready = false; 1007 - enqueue_coroutine(temp); 1008 - } else free_coroutine(temp); 1009 - } 1010 - 1011 - if (js->needs_gc && !js->gc_suppress) { 1012 - js->needs_gc = false; 1013 - js_gc_compact(js); 1014 - js->gc_alloc_since = 0; 1015 - } 1016 - } 1017 - 1018 - typedef enum { 1019 - WORK_MICROTASKS = 1 << 0, 1020 - WORK_TIMERS = 1 << 1, 1021 - WORK_IMMEDIATES = 1 << 2, 1022 - WORK_COROUTINES = 1 << 3, 1023 - WORK_COROUTINES_READY = 1 << 4, 1024 - WORK_FETCHES = 1 << 5, 1025 - WORK_FS_OPS = 1 << 6, 1026 - WORK_CHILD_PROCS = 1 << 7, 1027 - WORK_READLINE = 1 << 8, 1028 - WORK_STDIN = 1 << 9, 1029 - } work_flags_t; 1030 - 1031 - static inline work_flags_t get_pending_work(void) { 1032 - work_flags_t flags = 0; 1033 - if (has_pending_microtasks()) flags |= WORK_MICROTASKS; 1034 - if (has_pending_timers()) flags |= WORK_TIMERS; 1035 - if (has_pending_immediates()) flags |= WORK_IMMEDIATES; 1036 - if (js_has_pending_coroutines()) flags |= WORK_COROUTINES; 1037 - if (has_ready_coroutines()) flags |= WORK_COROUTINES_READY; 1038 - if (has_pending_fetches()) flags |= WORK_FETCHES; 1039 - if (has_pending_fs_ops()) flags |= WORK_FS_OPS; 1040 - if (has_pending_child_processes()) flags |= WORK_CHILD_PROCS; 1041 - if (has_active_readline_interfaces()) flags |= WORK_READLINE; 1042 - if (has_active_stdin()) flags |= WORK_STDIN; 1043 - return flags; 1044 - } 1045 - 1046 - #define WORK_TASKS (WORK_MICROTASKS | WORK_TIMERS | WORK_IMMEDIATES | WORK_COROUTINES | WORK_FETCHES) 1047 - #define WORK_PENDING (WORK_TASKS | WORK_FS_OPS | WORK_CHILD_PROCS | WORK_READLINE | WORK_STDIN) 1048 - #define WORK_BLOCKING (WORK_MICROTASKS | WORK_IMMEDIATES | WORK_COROUTINES_READY) 1049 - 1050 - void js_run_event_loop(struct js *js) { 1051 - work_flags_t work; 1052 - 1053 - while ((work = get_pending_work()) & WORK_PENDING) { 1054 - js_poll_events(js); 1055 - work = get_pending_work(); 1056 - 1057 - if (work & (WORK_READLINE | WORK_STDIN)) { 1058 - if (work & WORK_BLOCKING) uv_run(uv_default_loop(), UV_RUN_NOWAIT); else { 1059 - int64_t ms = has_pending_timers() ? get_next_timer_timeout() : 100; 1060 - if (ms > 100) ms = 100; 1061 - uv_run(uv_default_loop(), ms > 0 ? UV_RUN_ONCE : UV_RUN_NOWAIT); 1062 - } 1063 - } else if (!(work & WORK_BLOCKING) && (work & WORK_TIMERS)) { 1064 - jsoff_t gc_thresh = js->brk / 2; 1065 - if (gc_thresh < 4 * 1024 * 1024) gc_thresh = 4 * 1024 * 1024; 1066 - if (js->gc_alloc_since > gc_thresh || js->needs_gc) { 1067 - js->needs_gc = false; 1068 - js_gc_compact(js); 1069 - js->gc_alloc_since = 0; 1070 - } 1071 - 1072 - int64_t ms = get_next_timer_timeout(); 1073 - if (ms > 0) usleep(ms > 1000 ? 1000000 : (useconds_t)(ms * 1000)); 1074 - } 1075 - } 1076 - 1077 - js_poll_events(js); 1078 - } 1079 - 1080 - static jsval_t start_async_in_coroutine(struct js *js, const char *code, size_t code_len, jsval_t closure_scope, jsval_t *args, int nargs) { 1081 - if (++coros_this_tick > CORO_PER_TICK_LIMIT) { 1082 - js->fatal_error = true; 1083 - return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum async operations per tick exceeded"); 1084 - } 1085 - 1086 - jsval_t promise = js_mkpromise(js); 1087 - async_exec_context_t *ctx = (async_exec_context_t *)CORO_MALLOC(sizeof(async_exec_context_t)); 1088 - if (!ctx) return js_mkerr(js, "out of memory for async context"); 1089 - 1090 - ctx->js = js; 1091 - ctx->code = code; 1092 - ctx->code_len = code_len; 1093 - ctx->closure_scope = closure_scope; 1094 - ctx->result = js_mkundef(); 1095 - ctx->promise = promise; 1096 - ctx->has_error = false; 1097 - ctx->coro = NULL; 1098 - 1099 - size_t stack_size = calculate_coro_stack_size(); 1100 - mco_desc desc = mco_desc_init(mco_async_entry, stack_size); 1101 - desc.user_data = ctx; 1102 - 1103 - mco_coro* mco = NULL; 1104 - mco_result res = mco_create(&mco, &desc); 1105 - if (res != MCO_SUCCESS) { 1106 - CORO_FREE(ctx); 1107 - return js_mkerr(js, "failed to create minicoro coroutine"); 1108 - } 1109 - 1110 - coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 1111 - if (!coro) { 1112 - mco_destroy(mco); 1113 - CORO_FREE(ctx); 1114 - return js_mkerr(js, "out of memory for coroutine"); 1115 - } 1116 - 1117 - coro->js = js; 1118 - coro->type = CORO_ASYNC_AWAIT; 1119 - coro->scope = closure_scope; 1120 - coro->this_val = js->this_val; 1121 - coro->super_val = js->super_val; 1122 - coro->new_target = js->new_target; 1123 - coro->awaited_promise = js_mkundef(); 1124 - coro->result = js_mkundef(); 1125 - coro->async_func = js->current_func; 1126 - if (nargs > 0) { 1127 - coro->args = (jsval_t *)CORO_MALLOC(sizeof(jsval_t) * nargs); 1128 - if (coro->args) memcpy(coro->args, args, sizeof(jsval_t) * nargs); 1129 - } else { coro->args = NULL; } 1130 - coro->nargs = nargs; 1131 - coro->is_settled = false; 1132 - coro->is_error = false; 1133 - coro->is_done = false; 1134 - coro->resume_point = 0; 1135 - coro->yield_value = js_mkundef(); 1136 - coro->next = NULL; 1137 - coro->mco = mco; 1138 - coro->mco_started = false; 1139 - coro->is_ready = true; 1140 - coro->for_let_stack = NULL; 1141 - coro->for_let_stack_len = 0; 1142 - coro->for_let_stack_cap = 0; 1143 - 1144 - extern UT_array *global_scope_stack; 1145 - utarray_new(coro->scope_stack, &jsoff_icd); 1146 - jsoff_t glob_off = (jsoff_t)vdata(js_glob(js)); 1147 - utarray_push_back(coro->scope_stack, &glob_off); 1148 - 1149 - ctx->coro = coro; 1150 - enqueue_coroutine(coro); 1151 - 1152 - coro_saved_state_t saved = coro_enter(js, coro); 1153 - res = mco_resume(mco); 1154 - coro_leave(js, coro, saved); 1155 - 1156 - if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 1157 - remove_coroutine(coro); 1158 - free_coroutine(coro); 1159 - return js_mkerr(js, "failed to start coroutine"); 1160 - } 1161 - 1162 - coro->mco_started = true; 1163 - if (mco_status(mco) == MCO_DEAD) { 1164 - remove_coroutine(coro); 1165 - free_coroutine(coro); 1166 - } 1167 - 1168 - return promise; 1169 - } 1170 - 1171 - static void free_coroutine(coroutine_t *coro) { 1172 - if (coro) { 1173 - if (coro->mco) { 1174 - if (mco_running() == coro->mco) fprintf(stderr, "WARNING: Attempting to free a running coroutine\n"); 1175 - async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(coro->mco); 1176 - if (ctx) CORO_FREE(ctx); 1177 - mco_destroy(coro->mco); 1178 - coro->mco = NULL; 1179 - } 1180 - if (coro->args) CORO_FREE(coro->args); 1181 - if (coro->for_let_stack) free(coro->for_let_stack); 1182 - if (coro->scope_stack) utarray_free(coro->scope_stack); 1183 - CORO_FREE(coro); 1184 - } 1185 - } 1186 - 1187 - static jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 1188 - static jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 1189 805 1190 806 static size_t cpy(char *dst, size_t dstlen, const char *src, size_t srclen) { 1191 807 if (dstlen == 0) return srclen; ··· 4608 4224 return js->for_let_stack_len > 0 ? &js->for_let_stack[js->for_let_stack_len - 1] : NULL; 4609 4225 } 4610 4226 4611 - static void for_let_swap_with_coro(struct js *js, coroutine_t *coro) { 4612 - struct for_let_ctx *tmp_stack = js->for_let_stack; 4613 - int tmp_len = js->for_let_stack_len; 4614 - int tmp_cap = js->for_let_stack_cap; 4615 - 4616 - js->for_let_stack = coro->for_let_stack; 4617 - js->for_let_stack_len = coro->for_let_stack_len; 4618 - js->for_let_stack_cap = coro->for_let_stack_cap; 4619 - 4620 - coro->for_let_stack = tmp_stack; 4621 - coro->for_let_stack_len = tmp_len; 4622 - coro->for_let_stack_cap = tmp_cap; 4623 - } 4624 - 4625 4227 static void copy_body_scope_props(struct js *js, jsval_t body_scope, jsval_t closure_scope, const char *skip_var, jsoff_t skip_len) { 4626 4228 if (vtype(body_scope) != T_OBJ) return; 4627 4229 jsoff_t prop_off = loadoff(js, (jsoff_t)vdata(body_scope)) & ~(3U | FLAGMASK); ··· 6226 5828 } 6227 5829 } 6228 5830 6229 - static jsval_t call_js_internal( 5831 + jsval_t call_js_internal( 6230 5832 struct js *js, const char *fn, jsoff_t fnlen, 6231 5833 jsval_t closure_scope, jsval_t *bound_args, int bound_argc, jsval_t func_val 6232 5834 ) { ··· 6393 5995 return res; 6394 5996 } 6395 5997 6396 - static jsval_t call_js(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope) { 5998 + jsval_t call_js(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope) { 6397 5999 return call_js_internal(js, fn, fnlen, closure_scope, NULL, 0, js_mkundef()); 6398 6000 } 6399 6001 6400 - static jsval_t call_js_with_args(struct js *js, jsval_t func, jsval_t *args, int nargs) { 6002 + jsval_t call_js_with_args(struct js *js, jsval_t func, jsval_t *args, int nargs) { 6401 6003 if (vtype(func) == T_CFUNC) { 6402 6004 jsval_t (*fn)(struct js *, jsval_t *, int) = (jsval_t(*)(struct js *, jsval_t *, int)) vdata(func); 6403 6005 return fn(js, args, nargs); ··· 6497 6099 return result; 6498 6100 } 6499 6101 6500 - static jsval_t call_js_code_with_args(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *args, int nargs, jsval_t func_val) { 6102 + jsval_t call_js_code_with_args(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *args, int nargs, jsval_t func_val) { 6501 6103 jsoff_t parent_scope_offset; 6502 6104 if (vtype(closure_scope) == T_OBJ) { 6503 6105 parent_scope_offset = (jsoff_t) vdata(closure_scope); ··· 19503 19105 } 19504 19106 jsval_t res_args[] = { res }; 19505 19107 return builtin_Promise_resolve(js, res_args, 1); 19506 - } 19507 - 19508 - static jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 19509 - jsval_t me = js->current_func; 19510 - jsval_t coro_val = get_slot(js, me, SLOT_CORO); 19511 - if (vtype(coro_val) != T_NUM) return js_mkundef(); 19512 - 19513 - coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 19514 - if (!coro) return js_mkundef(); 19515 - 19516 - coro->result = nargs > 0 ? args[0] : js_mkundef(); 19517 - coro->is_settled = true; 19518 - coro->is_error = false; 19519 - coro->is_ready = true; 19520 - 19521 - return js_mkundef(); 19522 - } 19523 - 19524 - static jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 19525 - jsval_t me = js->current_func; 19526 - jsval_t coro_val = get_slot(js, me, SLOT_CORO); 19527 - 19528 - if (vtype(coro_val) != T_NUM) return js_mkundef(); 19529 - 19530 - coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 19531 - if (!coro) return js_mkundef(); 19532 - 19533 - coro->result = nargs > 0 ? args[0] : js_mkundef(); 19534 - coro->is_settled = true; 19535 - coro->is_error = true; 19536 - coro->is_ready = true; 19537 - 19538 - return js_mkundef(); 19539 19108 } 19540 19109 19541 19110 static jsval_t builtin_Promise_all_resolve_handler(struct js *js, jsval_t *args, int nargs) {
+1
src/main.c
··· 11 11 #include "ant.h" 12 12 #include "repl.h" 13 13 #include "utils.h" 14 + #include "reactor.h" 14 15 #include "runtime.h" 15 16 #include "snapshot.h" 16 17 #include "esm/remote.h"
+5 -13
src/modules/server.c
··· 10 10 #include <utarray.h> 11 11 12 12 #include "ant.h" 13 + #include "gc.h" 14 + #include "reactor.h" 13 15 #include "config.h" 14 16 #include "runtime.h" 15 17 #include "internal.h" ··· 108 110 109 111 static void on_js_timer(uv_timer_t *handle) { 110 112 struct js *js = (struct js*)handle->data; 111 - if (js) { 112 - if (has_pending_immediates()) process_immediates(js); 113 - if (has_pending_timers()) { 114 - int64_t next_timeout = get_next_timer_timeout(); 115 - if (next_timeout <= 0) process_timers(js); 116 - } 117 - } 113 + if (js) if (has_pending_immediates()) process_immediates(js); 118 114 } 119 115 120 116 static int parse_accept_encoding(const char *buffer, size_t len) { ··· 995 991 rt->flags |= ANT_RUNTIME_EXT_EVENT_LOOP; 996 992 997 993 while (uv_loop_alive(g_loop)) { 998 - if (has_pending_timers() || has_pending_immediates()) { 999 - int64_t next_timeout_ms = get_next_timer_timeout(); 1000 - if (next_timeout_ms >= 0) { 1001 - uint64_t timeout = has_pending_immediates() ? 0 : (next_timeout_ms > 0 ? (uint64_t)next_timeout_ms : 0); 1002 - uv_timer_start(&g_js_timer, on_js_timer, timeout, 0); 1003 - } 994 + if (has_pending_immediates()) { 995 + uv_timer_start(&g_js_timer, on_js_timer, 0, 0); 1004 996 } else uv_timer_stop(&g_js_timer); 1005 997 1006 998 uv_run(g_loop, UV_RUN_ONCE);
+55 -113
src/modules/timer.c
··· 4 4 #include <stdlib.h> 5 5 #include <string.h> 6 6 #include <time.h> 7 - 8 - #ifdef _WIN32 9 - #include <windows.h> 10 - #else 11 - #include <sys/time.h> 12 - #endif 7 + #include <uv.h> 13 8 14 9 #include "arena.h" 15 10 #include "errors.h" ··· 18 13 #include "modules/timer.h" 19 14 20 15 typedef struct timer_entry { 16 + uv_timer_t handle; 21 17 jsval_t callback; 22 - uint64_t target_time_ms; 23 - uint64_t interval_ms; 24 18 int timer_id; 25 19 int active; 26 20 int is_interval; 27 21 struct timer_entry *next; 22 + struct timer_entry *prev; 28 23 } timer_entry_t; 29 24 30 25 typedef struct microtask_entry { ··· 49 44 immediate_entry_t *immediates_tail; 50 45 int next_timer_id; 51 46 int next_immediate_id; 52 - } timer_state = {NULL, NULL, NULL, NULL, NULL, NULL, 1, 1}; 47 + int active_timer_count; 48 + } timer_state = {NULL, NULL, NULL, NULL, NULL, NULL, 1, 1, 0}; 53 49 54 - static uint64_t get_current_time_ms(void) { 55 - #ifdef _WIN32 56 - LARGE_INTEGER freq, count; 57 - QueryPerformanceFrequency(&freq); 58 - QueryPerformanceCounter(&count); 59 - return (uint64_t)(count.QuadPart * 1000 / freq.QuadPart); 60 - #else 61 - struct timeval tv; 62 - gettimeofday(&tv, NULL); 63 - return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000; 64 - #endif 50 + static void timer_callback(uv_timer_t *handle) { 51 + timer_entry_t *entry = (timer_entry_t *)handle->data; 52 + if (!entry || !entry->active) return; 53 + 54 + struct js *js = timer_state.js; 55 + jsval_t args[0]; 56 + js_call(js, entry->callback, args, 0); 57 + process_microtasks(js); 58 + 59 + if (!entry->is_interval) { 60 + entry->active = 0; 61 + uv_timer_stop(&entry->handle); 62 + timer_state.active_timer_count--; 63 + } 64 + } 65 + 66 + static void add_timer_entry(timer_entry_t *entry) { 67 + entry->next = timer_state.timers; 68 + entry->prev = NULL; 69 + if (timer_state.timers) { 70 + timer_state.timers->prev = entry; 71 + } 72 + timer_state.timers = entry; 73 + } 74 + 75 + static void remove_timer_entry(timer_entry_t *entry) { 76 + if (entry->prev) entry->prev->next = entry->next; 77 + else timer_state.timers = entry->next; 78 + if (entry->next) entry->next->prev = entry->prev; 65 79 } 66 80 67 81 // setTimeout(callback, delay) ··· 72 86 73 87 jsval_t callback = args[0]; 74 88 double delay_ms = js_getnum(args[1]); 75 - 76 - if (delay_ms < 0) { 77 - return js_mkerr(js, "setTimeout delay must be non-negative"); 78 - } 89 + if (delay_ms < 0) delay_ms = 0; 79 90 80 91 timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); 81 - if (entry == NULL) { 82 - return js_mkerr(js, "failed to allocate timer"); 83 - } 92 + if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); 84 93 94 + uv_timer_init(uv_default_loop(), &entry->handle); 95 + entry->handle.data = entry; 85 96 entry->callback = callback; 86 - entry->target_time_ms = get_current_time_ms() + (uint64_t)delay_ms; 87 - entry->interval_ms = 0; 88 97 entry->timer_id = timer_state.next_timer_id++; 89 98 entry->active = 1; 90 99 entry->is_interval = 0; 91 - entry->next = timer_state.timers; 92 - timer_state.timers = entry; 100 + 101 + add_timer_entry(entry); 102 + timer_state.active_timer_count++; 103 + uv_timer_start(&entry->handle, timer_callback, (uint64_t)delay_ms, 0); 93 104 94 105 return js_mknum((double)entry->timer_id); 95 106 } ··· 102 113 103 114 jsval_t callback = args[0]; 104 115 double delay_ms = js_getnum(args[1]); 105 - 106 - if (delay_ms < 0) { 107 - return js_mkerr(js, "setInterval delay must be non-negative"); 108 - } 116 + if (delay_ms < 0) delay_ms = 0; 109 117 110 118 timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); 111 - if (entry == NULL) { 112 - return js_mkerr(js, "failed to allocate timer"); 113 - } 119 + if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); 114 120 121 + uv_timer_init(uv_default_loop(), &entry->handle); 122 + entry->handle.data = entry; 115 123 entry->callback = callback; 116 - entry->target_time_ms = get_current_time_ms() + (uint64_t)delay_ms; 117 - entry->interval_ms = (uint64_t)delay_ms; 118 124 entry->timer_id = timer_state.next_timer_id++; 119 125 entry->active = 1; 120 126 entry->is_interval = 1; 121 - entry->next = timer_state.timers; 122 - timer_state.timers = entry; 127 + 128 + add_timer_entry(entry); 129 + timer_state.active_timer_count++; 130 + uv_timer_start(&entry->handle, timer_callback, (uint64_t)delay_ms, (uint64_t)delay_ms); 123 131 124 132 return js_mknum((double)entry->timer_id); 125 133 } 126 134 127 135 // clearTimeout(timerId) 128 136 static jsval_t js_clear_timeout(struct js *js, jsval_t *args, int nargs) { 129 - if (nargs < 1) { 130 - return js_mkundef(); 131 - } 132 - 137 + if (nargs < 1) return js_mkundef(); 133 138 int timer_id = (int)js_getnum(args[0]); 134 139 135 140 for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) { 136 - if (entry->timer_id == timer_id) { 141 + if (entry->timer_id == timer_id && entry->active) { 137 142 entry->active = 0; 138 - break; 143 + uv_timer_stop(&entry->handle); 144 + timer_state.active_timer_count--; break; 139 145 } 140 146 } 141 147 ··· 279 285 return 0; 280 286 } 281 287 282 - static void remove_timer(timer_entry_t *target) { 283 - timer_entry_t **ptr = &timer_state.timers; 284 - 285 - scan: 286 - if (!*ptr) return; 287 - if (*ptr == target) { 288 - *ptr = target->next; 289 - free(target); return; 290 - } 291 - ptr = &(*ptr)->next; 292 - goto scan; 293 - } 294 - 295 - void process_timers(struct js *js) { 296 - uint64_t current_time = get_current_time_ms(); 297 - timer_entry_t **ptr = &timer_state.timers; 298 - timer_entry_t *entry; 299 - 300 - scan: 301 - if (!*ptr) return; 302 - entry = *ptr; 303 - 304 - if (!entry->active) { 305 - *ptr = entry->next; 306 - free(entry); 307 - goto scan; 308 - } 309 - 310 - if (current_time < entry->target_time_ms) { 311 - ptr = &entry->next; 312 - goto scan; 313 - } 314 - 315 - jsval_t args[0]; 316 - js_call(js, entry->callback, args, 0); 317 - process_microtasks(js); 318 - 319 - if (entry->is_interval && entry->active) { 320 - entry->target_time_ms = get_current_time_ms() + entry->interval_ms; 321 - } else remove_timer(entry); 322 - 323 - current_time = get_current_time_ms(); 324 - ptr = &timer_state.timers; 325 - goto scan; 326 - } 327 - 328 288 int has_pending_timers(void) { 329 - for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) { 330 - if (entry->active) return 1; 331 - } 332 - return 0; 289 + return timer_state.active_timer_count > 0; 333 290 } 334 291 335 292 int has_pending_microtasks(void) { 336 293 return timer_state.microtasks != NULL ? 1 : 0; 337 - } 338 - 339 - int64_t get_next_timer_timeout(void) { 340 - uint64_t current_time = get_current_time_ms(); 341 - int64_t min_timeout = -1; 342 - 343 - for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) { 344 - if (!entry->active) continue; 345 - 346 - int64_t timeout = (int64_t)entry->target_time_ms - (int64_t)current_time; 347 - if (timeout <= 0) return 0; 348 - if (min_timeout == -1 || timeout < min_timeout) min_timeout = timeout; 349 - } 350 - 351 - return min_timeout; 352 294 } 353 295 354 296 void init_timer_module() {
+85
src/reactor.c
··· 1 + #include <internal.h> 2 + #include <uv.h> 3 + 4 + #include "gc.h" 5 + #include "sugar.h" 6 + #include "reactor.h" 7 + 8 + #include "modules/fs.h" 9 + #include "modules/timer.h" 10 + #include "modules/fetch.h" 11 + #include "modules/child_process.h" 12 + #include "modules/readline.h" 13 + #include "modules/process.h" 14 + 15 + void js_poll_events(ant_t *js) { 16 + coros_this_tick = 0; 17 + 18 + fetch_poll_events(); 19 + fs_poll_events(); 20 + child_process_poll_events(); 21 + 22 + process_immediates(js); 23 + process_microtasks(js); 24 + 25 + for (coroutine_t *temp = pending_coroutines.head, *next; temp; temp = next) { 26 + next = temp->next; 27 + if (!temp->is_ready || !temp->mco || mco_status(temp->mco) != MCO_SUSPENDED) continue; 28 + remove_coroutine(temp); 29 + 30 + coro_saved_state_t saved = coro_enter(temp->js, temp); 31 + mco_result res = mco_resume(temp->mco); 32 + coro_leave(temp->js, temp, saved); 33 + 34 + if (res == MCO_SUCCESS && mco_status(temp->mco) != MCO_DEAD) { 35 + temp->is_ready = false; 36 + enqueue_coroutine(temp); 37 + } else free_coroutine(temp); 38 + } 39 + 40 + if (js->needs_gc && !js->gc_suppress) { 41 + js->needs_gc = false; 42 + js_gc_compact(js); 43 + js->gc_alloc_since = 0; 44 + } 45 + } 46 + 47 + static inline work_flags_t get_pending_work(void) { 48 + work_flags_t flags = 0; 49 + if (has_pending_microtasks()) flags |= WORK_MICROTASKS; 50 + if (has_pending_timers()) flags |= WORK_TIMERS; 51 + if (has_pending_immediates()) flags |= WORK_IMMEDIATES; 52 + if (has_pending_coroutines()) flags |= WORK_COROUTINES; 53 + if (has_ready_coroutines()) flags |= WORK_COROUTINES_READY; 54 + if (has_pending_fetches()) flags |= WORK_FETCHES; 55 + if (has_pending_fs_ops()) flags |= WORK_FS_OPS; 56 + if (has_pending_child_processes()) flags |= WORK_CHILD_PROCS; 57 + if (has_active_readline_interfaces()) flags |= WORK_READLINE; 58 + if (has_active_stdin()) flags |= WORK_STDIN; 59 + return flags; 60 + } 61 + 62 + static void maybe_gc(ant_t *js) { 63 + jsoff_t thresh = js->brk / 2; 64 + if (thresh < 4 * 1024 * 1024) thresh = 4 * 1024 * 1024; 65 + 66 + if (js->gc_alloc_since > thresh || js->needs_gc) { 67 + js->needs_gc = false; 68 + js_gc_compact(js); 69 + js->gc_alloc_since = 0; 70 + } 71 + } 72 + 73 + void js_run_event_loop(ant_t *js) { 74 + work_flags_t work; 75 + 76 + while ((work = get_pending_work()) & WORK_PENDING) { 77 + js_poll_events(js); 78 + work = get_pending_work(); 79 + 80 + if (work & WORK_BLOCKING) uv_run(uv_default_loop(), UV_RUN_NOWAIT); 81 + else if (work & WORK_ASYNC) { maybe_gc(js); uv_run(uv_default_loop(), UV_RUN_ONCE); } 82 + } 83 + 84 + js_poll_events(js); 85 + }
+1
src/repl.c
··· 20 20 21 21 #include "ant.h" 22 22 #include "repl.h" 23 + #include "reactor.h" 23 24 #include "config.h" 24 25 #include "runtime.h" 25 26 #include "internal.h"
+2
src/runtime.c
··· 1 + #include "ant.h" 2 + 1 3 #include <stdio.h> 2 4 #include <stdlib.h> 3 5 #include <string.h>
+276
src/sugar.c
··· 1 + #include "internal.h" 2 + #include "errors.h" 3 + #include "sugar.h" 4 + 5 + #define MCO_USE_VMEM_ALLOCATOR 6 + #define MCO_ZERO_MEMORY 7 + #define MCO_DEFAULT_STACK_SIZE (1024 * 1024) 8 + #define MINICORO_IMPL 9 + #include <minicoro.h> 10 + 11 + uint32_t coros_this_tick = 0; 12 + coroutine_queue_t pending_coroutines = {NULL, NULL}; 13 + static bool coro_stack_size_initialized = false; 14 + 15 + bool has_pending_coroutines(void) { 16 + return pending_coroutines.head != NULL; 17 + } 18 + 19 + bool has_ready_coroutines(void) { 20 + coroutine_t *temp = pending_coroutines.head; 21 + while (temp) { 22 + if (temp->is_ready) return true; 23 + temp = temp->next; 24 + } 25 + return false; 26 + } 27 + 28 + void enqueue_coroutine(coroutine_t *coro) { 29 + if (!coro) return; 30 + coro->next = NULL; 31 + coro->prev = pending_coroutines.tail; 32 + 33 + if (pending_coroutines.tail) { 34 + pending_coroutines.tail->next = coro; 35 + } else pending_coroutines.head = coro; 36 + pending_coroutines.tail = coro; 37 + } 38 + 39 + void remove_coroutine(coroutine_t *coro) { 40 + if (!coro) return; 41 + 42 + if (coro->prev) { 43 + coro->prev->next = coro->next; 44 + } else pending_coroutines.head = coro->next; 45 + 46 + if (coro->next) { 47 + coro->next->prev = coro->prev; 48 + } else pending_coroutines.tail = coro->prev; 49 + 50 + coro->prev = NULL; 51 + coro->next = NULL; 52 + } 53 + 54 + void free_coroutine(coroutine_t *coro) { 55 + if (!coro) return; 56 + 57 + if (coro->mco) { 58 + if (mco_running() == coro->mco) fprintf(stderr, "WARNING: Attempting to free a running coroutine\n"); 59 + async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(coro->mco); 60 + if (ctx) CORO_FREE(ctx); 61 + mco_destroy(coro->mco); 62 + coro->mco = NULL; 63 + } 64 + 65 + if (coro->args) CORO_FREE(coro->args); 66 + if (coro->for_let_stack) free(coro->for_let_stack); 67 + if (coro->scope_stack) utarray_free(coro->scope_stack); 68 + 69 + CORO_FREE(coro); 70 + } 71 + 72 + static void for_let_swap_with_coro(struct js *js, coroutine_t *coro) { 73 + struct for_let_ctx *tmp_stack = js->for_let_stack; 74 + int tmp_len = js->for_let_stack_len; 75 + int tmp_cap = js->for_let_stack_cap; 76 + 77 + js->for_let_stack = coro->for_let_stack; 78 + js->for_let_stack_len = coro->for_let_stack_len; 79 + js->for_let_stack_cap = coro->for_let_stack_cap; 80 + 81 + coro->for_let_stack = tmp_stack; 82 + coro->for_let_stack_len = tmp_len; 83 + coro->for_let_stack_cap = tmp_cap; 84 + } 85 + 86 + coro_saved_state_t coro_enter(struct js *js, coroutine_t *coro) { 87 + extern UT_array *global_scope_stack; 88 + coro_saved_state_t saved = { js->scope, global_scope_stack }; 89 + js->scope = coro->scope; 90 + global_scope_stack = coro->scope_stack; 91 + for_let_swap_with_coro(js, coro); 92 + return saved; 93 + } 94 + 95 + void coro_leave(struct js *js, coroutine_t *coro, coro_saved_state_t saved) { 96 + extern UT_array *global_scope_stack; 97 + coro->scope = js->scope; 98 + coro->scope_stack = global_scope_stack; 99 + js->scope = saved.scope; 100 + global_scope_stack = saved.scope_stack; 101 + for_let_swap_with_coro(js, coro); 102 + } 103 + 104 + static size_t calculate_coro_stack_size(void) { 105 + if (coro_stack_size_initialized) return 0; 106 + coro_stack_size_initialized = true; 107 + const char *env_stack = getenv("ANT_CORO_STACK_SIZE"); 108 + if (env_stack) { 109 + size_t size = (size_t)atoi(env_stack) * 1024; 110 + if (size >= 32 * 1024 && size <= 8 * 1024 * 1024) return size; 111 + } 112 + return 0; 113 + } 114 + 115 + static void mco_async_entry(mco_coro* mco) { 116 + async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(mco); 117 + 118 + struct js *js = ctx->js; 119 + coroutine_t *coro = ctx->coro; 120 + jsval_t result; 121 + 122 + jsval_t saved_super = js->super_val; 123 + jsval_t saved_new_target = js->new_target; 124 + if (coro) { 125 + js->super_val = coro->super_val; 126 + js->new_target = coro->new_target; 127 + } 128 + 129 + if (coro && coro->nargs > 0 && coro->args) { 130 + result = call_js_code_with_args(js, ctx->code, (jsoff_t)ctx->code_len, ctx->closure_scope, coro->args, coro->nargs, js_mkundef()); 131 + } else result = call_js(js, ctx->code, (jsoff_t)ctx->code_len, ctx->closure_scope); 132 + 133 + js->super_val = saved_super; 134 + js->new_target = saved_new_target; 135 + 136 + ctx->result = result; 137 + ctx->has_error = is_err(result); 138 + 139 + if (ctx->has_error) { 140 + jsval_t reject_value = js->thrown_value; 141 + if (vtype(reject_value) == T_UNDEF) { 142 + reject_value = js_mkstr(js, js->errmsg ? js->errmsg : "Unknown error", js->errmsg ? strlen(js->errmsg) : 13); 143 + } 144 + js->flags &= (uint8_t)~F_THROW; 145 + js->thrown_value = js_mkundef(); 146 + js_reject_promise(js, ctx->promise, reject_value); 147 + } else js_resolve_promise(js, ctx->promise, result); 148 + } 149 + 150 + jsval_t start_async_in_coroutine(struct js *js, const char *code, size_t code_len, jsval_t closure_scope, jsval_t *args, int nargs) { 151 + if (++coros_this_tick > CORO_PER_TICK_LIMIT) { 152 + js->fatal_error = true; 153 + return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum async operations per tick exceeded"); 154 + } 155 + 156 + jsval_t promise = js_mkpromise(js); 157 + async_exec_context_t *ctx = (async_exec_context_t *)CORO_MALLOC(sizeof(async_exec_context_t)); 158 + if (!ctx) return js_mkerr(js, "out of memory for async context"); 159 + 160 + ctx->js = js; 161 + ctx->code = code; 162 + ctx->code_len = code_len; 163 + ctx->closure_scope = closure_scope; 164 + ctx->result = js_mkundef(); 165 + ctx->promise = promise; 166 + ctx->has_error = false; 167 + ctx->coro = NULL; 168 + 169 + size_t stack_size = calculate_coro_stack_size(); 170 + mco_desc desc = mco_desc_init(mco_async_entry, stack_size); 171 + desc.user_data = ctx; 172 + 173 + mco_coro* mco = NULL; 174 + mco_result res = mco_create(&mco, &desc); 175 + if (res != MCO_SUCCESS) { 176 + CORO_FREE(ctx); 177 + return js_mkerr(js, "failed to create minicoro coroutine"); 178 + } 179 + 180 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 181 + if (!coro) { 182 + mco_destroy(mco); 183 + CORO_FREE(ctx); 184 + return js_mkerr(js, "out of memory for coroutine"); 185 + } 186 + 187 + *coro = (coroutine_t){ 188 + .js = js, 189 + .type = CORO_ASYNC_AWAIT, 190 + .scope = closure_scope, 191 + .this_val = js->this_val, 192 + .super_val = js->super_val, 193 + .new_target = js->new_target, 194 + .awaited_promise = js_mkundef(), 195 + .result = js_mkundef(), 196 + .async_func = js->current_func, 197 + .args = NULL, 198 + .nargs = nargs, 199 + .is_settled = false, 200 + .is_error = false, 201 + .is_done = false, 202 + .resume_point = 0, 203 + .yield_value = js_mkundef(), 204 + .next = NULL, 205 + .mco = mco, 206 + .mco_started = false, 207 + .is_ready = true, 208 + .for_let_stack = NULL, 209 + .for_let_stack_len = 0, 210 + .for_let_stack_cap = 0, 211 + }; 212 + 213 + if (nargs > 0) { 214 + coro->args = (jsval_t *)CORO_MALLOC(sizeof(jsval_t) * nargs); 215 + if (coro->args) memcpy(coro->args, args, sizeof(jsval_t) * nargs); 216 + } 217 + 218 + extern UT_array *global_scope_stack; 219 + utarray_new(coro->scope_stack, &jsoff_icd); 220 + jsoff_t glob_off = (jsoff_t)vdata(js_glob(js)); 221 + utarray_push_back(coro->scope_stack, &glob_off); 222 + 223 + ctx->coro = coro; 224 + enqueue_coroutine(coro); 225 + 226 + coro_saved_state_t saved = coro_enter(js, coro); 227 + res = mco_resume(mco); 228 + coro_leave(js, coro, saved); 229 + 230 + if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 231 + remove_coroutine(coro); 232 + free_coroutine(coro); 233 + return js_mkerr(js, "failed to start coroutine"); 234 + } 235 + 236 + coro->mco_started = true; 237 + if (mco_status(mco) == MCO_DEAD) { 238 + remove_coroutine(coro); 239 + free_coroutine(coro); 240 + } 241 + 242 + return promise; 243 + } 244 + 245 + jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 246 + jsval_t me = js->current_func; 247 + jsval_t coro_val = js_get_slot(js, me, SLOT_CORO); 248 + if (vtype(coro_val) != T_NUM) return js_mkundef(); 249 + 250 + coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 251 + if (!coro) return js_mkundef(); 252 + 253 + coro->result = nargs > 0 ? args[0] : js_mkundef(); 254 + coro->is_settled = true; 255 + coro->is_error = false; 256 + coro->is_ready = true; 257 + 258 + return js_mkundef(); 259 + } 260 + 261 + jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 262 + jsval_t me = js->current_func; 263 + jsval_t coro_val = js_get_slot(js, me, SLOT_CORO); 264 + 265 + if (vtype(coro_val) != T_NUM) return js_mkundef(); 266 + 267 + coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 268 + if (!coro) return js_mkundef(); 269 + 270 + coro->result = nargs > 0 ? args[0] : js_mkundef(); 271 + coro->is_settled = true; 272 + coro->is_error = true; 273 + coro->is_ready = true; 274 + 275 + return js_mkundef(); 276 + }