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.

update 0.3: add the garbage collector!

+754 -105
+5 -1
include/ant.h
··· 4 4 #pragma once 5 5 #define PCRE2_CODE_UNIT_WIDTH 8 6 6 7 + #include <config.h> 7 8 #include <stdbool.h> 8 9 #include <stddef.h> 9 10 #include <stdint.h> 10 11 #include <stdio.h> 11 - #include <config.h> 12 12 13 13 #define STR_PROTO "__proto__" 14 14 #define STR_PROTO_LEN 9 ··· 18 18 19 19 typedef uint32_t jsoff_t; 20 20 typedef uint64_t jsval_t; 21 + 22 + #define GC_FWD_ARGS jsval_t (*fwd_val)(void *ctx, jsval_t old), void *ctx 23 + #define GC_UPDATE_ARGS struct js *js, jsoff_t (*fwd_off)(void *ctx, jsoff_t old), GC_FWD_ARGS 21 24 22 25 enum { 23 26 JS_UNDEF, JS_NULL, JS_TRUE, JS_FALSE, JS_STR, JS_NUM, ··· 160 163 jsoff_t js_loadoff(struct js *js, jsoff_t off); 161 164 162 165 void js_print_stack_trace(FILE *stream); 166 + size_t js_gc_compact(struct js *js); 163 167 164 168 #endif
+2
include/config.h
··· 44 44 SLOT_TARGET_FUNC, 45 45 SLOT_VERSION, 46 46 SLOT_NAME, 47 + SLOT_MAP, 48 + SLOT_SET, 47 49 SLOT_MAX = 255 48 50 } internal_slot_t; 49 51
+2
include/config.h.in
··· 36 36 SLOT_TARGET_FUNC, 37 37 SLOT_VERSION, 38 38 SLOT_NAME, 39 + SLOT_MAP, 40 + SLOT_SET, 39 41 SLOT_MAX = 255 40 42 } internal_slot_t; 41 43
+70
include/internal.h
··· 1 + #ifndef ANT_INTERNAL_H 2 + #define ANT_INTERNAL_H 3 + 4 + #include "ant.h" 5 + 6 + struct js { 7 + jsoff_t css; // max observed C stack size 8 + jsoff_t lwm; // JS ram low watermark: min free ram observed 9 + const char *code; // currently parsed code snippet 10 + char *errmsg; // dynamic error message buffer 11 + size_t errmsg_size; // size of error message buffer 12 + const char *filename; // current filename for error reporting 13 + uint8_t tok; // last parsed token value 14 + uint8_t consumed; // indicator that last parsed token was consumed 15 + uint8_t flags; // execution flags, see F_* constants below 16 + #define F_NOEXEC 1U // parse code, but not execute 17 + #define F_LOOP 2U // we are inside the loop 18 + #define F_CALL 4U // we are inside a function call 19 + #define F_BREAK 8U // exit the loop 20 + #define F_RETURN 16U // return has been executed 21 + #define F_THROW 32U // throw has been executed 22 + #define F_SWITCH 64U // we are inside a switch statement 23 + #define F_STRICT 128U // strict mode is enabled 24 + jsoff_t clen; // code snippet length 25 + jsoff_t pos; // current parsing position 26 + jsoff_t toff; // offset of the last parsed token 27 + jsoff_t tlen; // length of the last parsed token 28 + jsoff_t nogc; // entity offset to exclude from GC 29 + jsval_t tval; // holds last parsed numeric or string literal value 30 + jsval_t scope; // current scope 31 + jsval_t this_val; // 'this' value for currently executing function 32 + jsval_t module_ns; // current ESM module namespace 33 + uint8_t *mem; // available JS memory 34 + jsoff_t size; // memory size 35 + jsoff_t brk; // current mem usage boundary 36 + jsoff_t maxcss; // maximum allowed C stack size usage 37 + void *cstk; // C stack pointer at the beginning of js_eval() 38 + jsval_t current_func; // currently executing function (for native closures) 39 + bool var_warning_shown; // flag to show var deprecation warning only once 40 + bool owns_mem; // true if js owns the memory buffer (dynamic allocation) 41 + jsoff_t max_size; // maximum allowed memory size (for dynamic growth) 42 + bool had_newline; // true if newline was crossed before current token 43 + jsval_t thrown_value; // stores the actual thrown value for catch blocks 44 + bool is_hoisting; // true during function declaration hoisting pass 45 + uint64_t sym_counter; // counter for generating unique symbol IDs 46 + }; 47 + 48 + #define JS_T_OBJ 0 49 + #define JS_T_PROP 1 50 + #define JS_T_STR 2 51 + 52 + #define JS_V_OBJ 0 53 + #define JS_V_PROP 1 54 + #define JS_V_STR 2 55 + #define JS_V_FUNC 7 56 + #define JS_V_ARR 11 57 + #define JS_V_PROMISE 12 58 + #define JS_V_BIGINT 14 59 + #define JS_V_GENERATOR 17 60 + 61 + #define NANBOX_PREFIX 0x7FC0000000000000ULL 62 + #define NANBOX_PREFIX_CHK 0x3FEULL 63 + #define NANBOX_TYPE_SHIFT 48 64 + #define NANBOX_TYPE_MASK 0x1F 65 + #define NANBOX_DATA_MASK 0x0000FFFFFFFFFFFFULL 66 + 67 + void js_gc_update_roots(GC_UPDATE_ARGS); 68 + bool js_has_pending_coroutines(void); 69 + 70 + #endif
+4
include/modules/fetch.h
··· 1 1 #ifndef FETCH_H 2 2 #define FETCH_H 3 3 4 + #include "ant.h" 5 + 4 6 void init_fetch_module(void); 5 7 void fetch_poll_events(void); 8 + void fetch_gc_update_roots(GC_FWD_ARGS); 9 + 6 10 int has_pending_fetches(void); 7 11 8 12 #endif
+2
include/modules/ffi.h
··· 6 6 jsval_t ffi_library(struct js *js); 7 7 jsval_t ffi_call_by_index(struct js *js, unsigned int func_index, jsval_t *args, int nargs); 8 8 9 + void ffi_gc_update_roots(GC_FWD_ARGS); 10 + 9 11 #endif
+3 -1
include/modules/fs.h
··· 4 4 #include "ant.h" 5 5 6 6 jsval_t fs_library(struct js *js); 7 - void fs_poll_events(void); 8 7 int has_pending_fs_ops(void); 8 + 9 + void fs_poll_events(void); 10 + void fs_gc_update_roots(GC_FWD_ARGS); 9 11 10 12 #endif
+2
include/modules/timer.h
··· 8 8 void process_microtasks(struct js *js); 9 9 void process_immediates(struct js *js); 10 10 void queue_microtask(struct js *js, jsval_t callback); 11 + void timer_gc_update_roots(GC_FWD_ARGS); 12 + 11 13 int has_pending_timers(void); 12 14 int has_pending_microtasks(void); 13 15 int has_pending_immediates(void);
+2 -1
meson.build
··· 17 17 sources = files( 18 18 'src/main.c', 19 19 'src/ant.c', 20 + 'src/gc.c', 20 21 'src/repl.c', 21 22 'src/runtime.c', 22 23 'src/snapshot.c', ··· 85 86 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 86 87 87 88 version_conf = configuration_data() 88 - version_conf.set('ANT_VERSION', '0.2.3.20') 89 + version_conf.set('ANT_VERSION', '0.3.1.11') 89 90 version_conf.set('ANT_GIT_HASH', git_hash) 90 91 version_conf.set('ANT_BUILD_DATE', build_date) 91 92
+228 -100
src/ant.c
··· 5 5 #include "ant.h" 6 6 #include "config.h" 7 7 #include "arena.h" 8 + #include "runtime.h" 9 + #include "internal.h" 8 10 9 11 #include <assert.h> 10 12 #include <math.h> ··· 340 342 static dynamic_accessors_t *accessor_registry = NULL; 341 343 static descriptor_entry_t *desc_registry = NULL; 342 344 345 + typedef struct map_registry_entry { 346 + map_entry_t **head; 347 + UT_hash_handle hh; 348 + } map_registry_entry_t; 349 + 350 + typedef struct set_registry_entry { 351 + set_entry_t **head; 352 + UT_hash_handle hh; 353 + } set_registry_entry_t; 354 + 355 + static map_registry_entry_t *map_registry = NULL; 356 + static set_registry_entry_t *set_registry = NULL; 357 + 343 358 void ant_register_library(ant_library_init_fn init_fn, const char *name, ...) { 344 359 va_list args; 345 360 const char *alias = name; ··· 371 386 return lib; 372 387 } 373 388 374 - struct js { 375 - jsoff_t css; // max observed C stack size 376 - jsoff_t lwm; // JS ram low watermark: min free ram observed 377 - const char *code; // currently parsed code snippet 378 - char *errmsg; // dynamic error message buffer 379 - size_t errmsg_size; // size of error message buffer 380 - const char *filename; // current filename for error reporting 381 - uint8_t tok; // last parsed token value 382 - uint8_t consumed; // indicator that last parsed token was consumed 383 - uint8_t flags; // execution flags, see F_* constants below 384 - #define F_NOEXEC 1U // parse code, but not execute 385 - #define F_LOOP 2U // we are inside the loop 386 - #define F_CALL 4U // we are inside a function call 387 - #define F_BREAK 8U // exit the loop 388 - #define F_RETURN 16U // return has been executed 389 - #define F_THROW 32U // throw has been executed 390 - #define F_SWITCH 64U // we are inside a switch statement 391 - #define F_STRICT 128U // strict mode is enabled 392 - jsoff_t clen; // code snippet length 393 - jsoff_t pos; // current parsing position 394 - jsoff_t toff; // offset of the last parsed token 395 - jsoff_t tlen; // length of the last parsed token 396 - jsoff_t nogc; // entity offset to exclude from GC 397 - jsval_t tval; // holds last parsed numeric or string literal value 398 - jsval_t scope; // current scope 399 - jsval_t this_val; // 'this' value for currently executing function 400 - jsval_t module_ns; // current ESM module namespace 401 - uint8_t *mem; // available JS memory 402 - jsoff_t size; // memory size 403 - jsoff_t brk; // current mem usage boundary 404 - jsoff_t maxcss; // maximum allowed C stack size usage 405 - void *cstk; // C stack pointer at the beginning of js_eval() 406 - jsval_t current_func; // currently executing function (for native closures) 407 - bool var_warning_shown; // flag to show var deprecation warning only once 408 - bool owns_mem; // true if js owns the memory buffer (dynamic allocation) 409 - jsoff_t max_size; // maximum allowed memory size (for dynamic growth) 410 - bool had_newline; // true if newline was crossed before current token 411 - jsval_t thrown_value; // stores the actual thrown value for catch blocks 412 - bool is_hoisting; // true during function declaration hoisting pass 413 - uint64_t sym_counter; // counter for generating unique symbol IDs 414 - }; 415 - 416 389 enum { 417 390 TOK_ERR, TOK_EOF, TOK_IDENTIFIER, TOK_NUMBER, TOK_STRING, TOK_SEMICOLON, TOK_BIGINT, 418 391 TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE, TOK_LBRACKET, TOK_RBRACKET, ··· 469 442 static jsval_t tov(double d) { union { double d; jsval_t v; } u = {d}; return u.v; } 470 443 static double tod(jsval_t v) { union { jsval_t v; double d; } u = {v}; return u.d; } 471 444 472 - #define NANBOX_PREFIX 0x7FC0000000000000ULL 473 - #define NANBOX_PREFIX_CHK 0x3FEULL 474 - #define NANBOX_TYPE_SHIFT 48 475 - #define NANBOX_TYPE_MASK 0x1F 476 - #define NANBOX_DATA_MASK 0x0000FFFFFFFFFFFFULL 477 - 478 445 static bool is_tagged(jsval_t v) { 479 446 return (v >> 53) == NANBOX_PREFIX_CHK; 480 447 } ··· 531 498 static jsoff_t offtolen(jsoff_t off) { return (off >> 2) - 1; } 532 499 static jsoff_t align32(jsoff_t v) { return ((v + 3) >> 2) << 2; } 533 500 534 - static void saveoff(struct js *js, jsoff_t off, jsoff_t val) { memcpy(&js->mem[off], &val, sizeof(val)); } 535 - static void saveval(struct js *js, jsoff_t off, jsval_t val) { memcpy(&js->mem[off], &val, sizeof(val)); } 501 + static void saveoff(struct js *js, jsoff_t off, jsoff_t val) { 502 + memcpy(&js->mem[off], &val, sizeof(val)); 503 + } 504 + static void saveval(struct js *js, jsoff_t off, jsval_t val) { 505 + memcpy(&js->mem[off], &val, sizeof(val)); 506 + } 536 507 537 508 static const char *typestr(uint8_t t) { 538 509 if (t == T_CFUNC) return "function"; ··· 615 586 if (!prim_propref_stack || idx < 0 || idx >= (int)utarray_len(prim_propref_stack)) return NULL; 616 587 617 588 return (prim_propref_data_t *)utarray_eltptr(prim_propref_stack, (unsigned)idx); 589 + } 590 + 591 + inline size_t js_getbrk(struct js *js) { 592 + return (size_t) js->brk; 618 593 } 619 594 620 595 static inline bool is_err(jsval_t v) { ··· 929 904 coro->next = NULL; 930 905 } 931 906 932 - static bool has_pending_coroutines(void) { 907 + inline bool js_has_pending_coroutines(void) { 933 908 return pending_coroutines.head != NULL; 934 909 } 935 910 ··· 980 955 } 981 956 982 957 void js_run_event_loop(struct js *js) { 983 - while (has_pending_microtasks() || has_pending_timers() || has_pending_immediates() || has_pending_coroutines() || has_pending_fetches() || has_pending_fs_ops()) { 958 + while (has_pending_microtasks() || has_pending_timers() || has_pending_immediates() || js_has_pending_coroutines() || has_pending_fetches() || has_pending_fs_ops()) { 984 959 js_poll_events(js); 985 960 986 961 if (!has_pending_microtasks() && !has_pending_immediates() && has_pending_timers() && !has_ready_coroutines()) { ··· 988 963 if (next_timeout_ms > 0) usleep(next_timeout_ms > 1000000 ? 1000000 : next_timeout_ms * 1000); 989 964 } 990 965 991 - if (!has_pending_microtasks() && !has_pending_timers() && !has_pending_immediates() && !has_pending_coroutines() && !has_pending_fetches() && !has_pending_fs_ops()) break; 966 + if (!has_pending_microtasks() && !has_pending_timers() && !has_pending_immediates() && !js_has_pending_coroutines() && !has_pending_fetches() && !has_pending_fs_ops()) break; 992 967 } 993 968 994 969 js_poll_events(js); ··· 1572 1547 is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0); 1573 1548 1574 1549 if (is_map) { 1575 - jsoff_t map_off = lkp(js, obj, "__map", 5); 1576 - if (map_off == 0) goto print_tagged_object; 1577 - 1578 - jsval_t map_val = resolveprop(js, mkval(T_PROP, map_off)); 1579 - if (vtype(map_val) != T_NUM) goto print_tagged_object; 1550 + jsval_t map_val = js_get_slot(js, obj, SLOT_MAP); 1551 + if (vtype(map_val) == T_UNDEF) goto print_tagged_object; 1580 1552 1581 - map_entry_t **map_ptr = (map_entry_t**)(size_t)vdata(map_val); 1553 + map_entry_t **map_ptr = (map_entry_t**)(size_t)tod(map_val); 1582 1554 n += cpy(buf + n, len - n, "Map(", 4); 1583 1555 1584 1556 unsigned int count = 0; ··· 1617 1589 } 1618 1590 1619 1591 if (is_set) { 1620 - jsoff_t set_off = lkp(js, obj, "__set", 5); 1621 - if (set_off == 0) goto print_tagged_object; 1592 + jsval_t set_val = js_get_slot(js, obj, SLOT_SET); 1593 + if (vtype(set_val) == T_UNDEF) goto print_tagged_object; 1622 1594 1623 - jsval_t set_val = resolveprop(js, mkval(T_PROP, set_off)); 1624 - if (vtype(set_val) != T_NUM) goto print_tagged_object; 1625 - 1626 - set_entry_t **set_ptr = (set_entry_t**)(size_t)vdata(set_val); 1595 + set_entry_t **set_ptr = (set_entry_t**)(size_t)tod(set_val); 1627 1596 n += cpy(buf + n, len - n, "Set(", 4); 1628 1597 1629 1598 unsigned int count = 0; ··· 2278 2247 const char *js_str(struct js *js, jsval_t value) { 2279 2248 if (is_err(value)) return js->errmsg; 2280 2249 2281 - size_t min_needed = sizeof(jsoff_t) + 256; 2282 - if (js->brk + min_needed > js->size) { 2283 - if (!js_try_grow_memory(js, min_needed)) return ""; 2284 - } 2285 - 2286 - char *buf = (char *) &js->mem[js->brk + sizeof(jsoff_t)]; 2287 - size_t len, available = js->size - js->brk - sizeof(jsoff_t); 2288 - 2289 2250 multiref_count = 0; 2290 2251 multiref_next_id = 0; 2291 2252 stringify_depth = 0; 2292 2253 scan_refs(js, value); 2293 2254 2294 - stringify_depth = 0; 2295 - stringify_indent = 0; 2296 - len = tostr(js, value, buf, available); 2297 - js_mkstr(js, NULL, len); 2298 - return buf; 2255 + size_t capacity = 4096; 2256 + char *buf = (char *)ANT_GC_MALLOC(capacity); 2257 + if (!buf) return ""; 2258 + 2259 + size_t len; 2260 + for (;;) { 2261 + stringify_depth = 0; 2262 + stringify_indent = 0; 2263 + len = tostr(js, value, buf, capacity); 2264 + 2265 + if (len < capacity - 1) break; 2266 + 2267 + capacity *= 2; 2268 + buf = (char *)ANT_GC_REALLOC(buf, capacity); 2269 + if (!buf) return ""; 2270 + } 2271 + 2272 + jsval_t str = js_mkstr(js, buf, len); 2273 + 2274 + if (is_err(str)) return ""; 2275 + return (const char *)&js->mem[vdata(str) + sizeof(jsoff_t)]; 2299 2276 } 2300 2277 2301 2278 static bool bigint_is_zero(struct js *js, jsval_t v); ··· 4408 4385 } 4409 4386 4410 4387 return setprop(js, obj, key, val); 4388 + } 4389 + 4390 + if (vtype(lhs) != T_PROP) { 4391 + if (js->flags & F_STRICT) { 4392 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side in assignment"); 4393 + } 4394 + return val; 4411 4395 } 4412 4396 4413 4397 jsoff_t propoff = (jsoff_t) vdata(lhs); ··· 9427 9411 if (!js_truthy(js, v)) break; 9428 9412 } 9429 9413 9430 - jsval_t iter_scope = js_mkundef(); 9414 + bool iter_scope = false; 9431 9415 jsval_t loop_var_val = js_mkundef(); 9432 9416 if (is_let_loop && let_var_len > 0) { 9433 9417 jsoff_t var_off = lkp_scope(js, js->scope, &js->code[let_var_off], let_var_len); ··· 9435 9419 loop_var_val = resolveprop(js, mkval(T_PROP, var_off)); 9436 9420 } 9437 9421 mkscope(js); 9438 - iter_scope = js->scope; 9422 + iter_scope = true; 9439 9423 jsval_t var_key = js_mkstr(js, &js->code[let_var_off], let_var_len); 9440 9424 mkprop(js, js->scope, var_key, loop_var_val, false); 9441 9425 } ··· 9445 9429 js->consumed = 1; 9446 9430 v = js_block_or_stmt(js); 9447 9431 if (is_err2(&v, &res)) { 9448 - if (vtype(iter_scope) != T_UNDEF) delscope(js); 9432 + if (iter_scope) delscope(js); 9449 9433 goto done; 9450 9434 } 9451 9435 9452 9436 if (is_let_loop && let_var_len > 0) { 9453 - jsoff_t iter_var_off = lkp(js, iter_scope, &js->code[let_var_off], let_var_len); 9437 + jsoff_t iter_var_off = lkp(js, js->scope, &js->code[let_var_off], let_var_len); 9454 9438 if (iter_var_off != 0) { 9455 9439 loop_var_val = resolveprop(js, mkval(T_PROP, iter_var_off)); 9456 9440 } ··· 13396 13380 } 13397 13381 13398 13382 jsval_t prop_key = js_mkstr(js, prop_str, prop_len); 13399 - bool mark_const = !writable || !configurable; 13383 + bool mark_const = !writable; 13400 13384 mkprop(js, as_obj, prop_key, value, mark_const); 13401 13385 } 13402 13386 } ··· 19145 19129 if (!map_head) return js_mkerr(js, "out of memory"); 19146 19130 *map_head = NULL; 19147 19131 19148 - jsval_t map_ptr = mkval(T_NUM, (size_t)map_head); 19149 - jsval_t map_key = js_mkstr(js, "__map", 5); 19150 - setprop(js, map_obj, map_key, map_ptr); 19132 + map_registry_entry_t *reg = (map_registry_entry_t *)ANT_GC_MALLOC(sizeof(map_registry_entry_t)); 19133 + if (reg) { 19134 + reg->head = map_head; 19135 + HASH_ADD_PTR(map_registry, head, reg); 19136 + } 19137 + 19138 + jsval_t map_ptr = tov((double)(size_t)map_head); 19139 + set_slot(js, map_obj, SLOT_MAP, map_ptr); 19151 19140 19152 19141 if (nargs == 0 || vtype(args[0]) != T_ARR) return map_obj; 19153 19142 ··· 19192 19181 if (!set_head) return js_mkerr(js, "out of memory"); 19193 19182 *set_head = NULL; 19194 19183 19195 - jsval_t set_ptr = mkval(T_NUM, (size_t)set_head); 19196 - jsval_t set_key = js_mkstr(js, "__set", 5); 19197 - setprop(js, set_obj, set_key, set_ptr); 19184 + set_registry_entry_t *reg = (set_registry_entry_t *)ANT_GC_MALLOC(sizeof(set_registry_entry_t)); 19185 + if (reg) { 19186 + reg->head = set_head; 19187 + HASH_ADD_PTR(set_registry, head, reg); 19188 + } 19189 + 19190 + jsval_t set_ptr = tov((double)(size_t)set_head); 19191 + set_slot(js, set_obj, SLOT_SET, set_ptr); 19198 19192 19199 19193 if (nargs == 0 || vtype(args[0]) != T_ARR) return set_obj; 19200 19194 ··· 19220 19214 } 19221 19215 19222 19216 static map_entry_t** get_map_from_obj(struct js *js, jsval_t obj) { 19223 - jsoff_t map_off = lkp(js, obj, "__map", 5); 19224 - if (map_off == 0) return NULL; 19225 - jsval_t map_val = resolveprop(js, mkval(T_PROP, map_off)); 19226 - if (vtype(map_val) != T_NUM) return NULL; 19227 - return (map_entry_t**)(size_t)vdata(map_val); 19217 + jsval_t map_val = js_get_slot(js, obj, SLOT_MAP); 19218 + if (vtype(map_val) == T_UNDEF) return NULL; 19219 + return (map_entry_t**)(size_t)tod(map_val); 19228 19220 } 19229 19221 19230 19222 static set_entry_t** get_set_from_obj(struct js *js, jsval_t obj) { 19231 - jsoff_t set_off = lkp(js, obj, "__set", 5); 19232 - if (set_off == 0) return NULL; 19233 - jsval_t set_val = resolveprop(js, mkval(T_PROP, set_off)); 19234 - if (vtype(set_val) != T_NUM) return NULL; 19235 - return (set_entry_t**)(size_t)vdata(set_val); 19223 + jsval_t set_val = js_get_slot(js, obj, SLOT_SET); 19224 + if (vtype(set_val) == T_UNDEF) return NULL; 19225 + return (set_entry_t**)(size_t)tod(set_val); 19236 19226 } 19237 19227 19238 19228 static jsval_t map_set(struct js *js, jsval_t *args, int nargs) { ··· 20728 20718 if (css) *css = js->css; 20729 20719 } 20730 20720 20731 - size_t js_getbrk(struct js *js) { return (size_t) js->brk; } 20721 + void js_gc_update_roots(GC_UPDATE_ARGS) { 20722 + if (global_scope_stack) { 20723 + unsigned int len = utarray_len(global_scope_stack); 20724 + for (unsigned int i = 0; i < len; i++) { 20725 + jsoff_t *off = (jsoff_t *)utarray_eltptr(global_scope_stack, i); 20726 + if (off && *off != 0) *off = fwd_off(ctx, *off); 20727 + } 20728 + } 20729 + 20730 + for (int i = 0; i < global_this_stack.depth; i++) { 20731 + global_this_stack.stack[i] = fwd_val(ctx, global_this_stack.stack[i]); 20732 + } 20733 + 20734 + if (propref_stack) { 20735 + unsigned int len = utarray_len(propref_stack); 20736 + for (unsigned int i = 0; i < len; i++) { 20737 + propref_data_t *entry = (propref_data_t *)utarray_eltptr(propref_stack, i); 20738 + if (entry) { 20739 + if (entry->obj_off != 0) entry->obj_off = fwd_off(ctx, entry->obj_off); 20740 + if (entry->key_off != 0) entry->key_off = fwd_off(ctx, entry->key_off); 20741 + } 20742 + } 20743 + } 20744 + 20745 + if (prim_propref_stack) { 20746 + unsigned int len = utarray_len(prim_propref_stack); 20747 + for (unsigned int i = 0; i < len; i++) { 20748 + prim_propref_data_t *entry = (prim_propref_data_t *)utarray_eltptr(prim_propref_stack, i); 20749 + if (entry) { 20750 + entry->prim_val = fwd_val(ctx, entry->prim_val); 20751 + if (entry->key_off != 0) entry->key_off = fwd_off(ctx, entry->key_off); 20752 + } 20753 + } 20754 + } 20755 + 20756 + promise_data_entry_t *pd, *pd_tmp; 20757 + HASH_ITER(hh, promise_registry, pd, pd_tmp) { 20758 + pd->value = fwd_val(ctx, pd->value); 20759 + if (pd->handlers) { 20760 + unsigned int len = utarray_len(pd->handlers); 20761 + for (unsigned int i = 0; i < len; i++) { 20762 + promise_handler_t *h = (promise_handler_t *)utarray_eltptr(pd->handlers, i); 20763 + if (h) { 20764 + h->onFulfilled = fwd_val(ctx, h->onFulfilled); 20765 + h->onRejected = fwd_val(ctx, h->onRejected); 20766 + h->nextPromise = fwd_val(ctx, h->nextPromise); 20767 + } 20768 + } 20769 + } 20770 + } 20771 + 20772 + if (rt && rt->js == js) { 20773 + rt->ant_obj = fwd_val(ctx, rt->ant_obj); 20774 + } 20775 + 20776 + proxy_data_t *proxy, *proxy_tmp; 20777 + proxy_data_t *new_proxy_registry = NULL; 20778 + HASH_ITER(hh, proxy_registry, proxy, proxy_tmp) { 20779 + HASH_DEL(proxy_registry, proxy); 20780 + proxy->obj_offset = fwd_off(ctx, proxy->obj_offset); 20781 + proxy->target = fwd_val(ctx, proxy->target); 20782 + proxy->handler = fwd_val(ctx, proxy->handler); 20783 + HASH_ADD(hh, new_proxy_registry, obj_offset, sizeof(jsoff_t), proxy); 20784 + } 20785 + proxy_registry = new_proxy_registry; 20786 + 20787 + dynamic_accessors_t *acc, *acc_tmp; 20788 + dynamic_accessors_t *new_accessor_registry = NULL; 20789 + HASH_ITER(hh, accessor_registry, acc, acc_tmp) { 20790 + HASH_DEL(accessor_registry, acc); 20791 + acc->obj_offset = fwd_off(ctx, acc->obj_offset); 20792 + HASH_ADD(hh, new_accessor_registry, obj_offset, sizeof(jsoff_t), acc); 20793 + } 20794 + accessor_registry = new_accessor_registry; 20795 + 20796 + descriptor_entry_t *desc, *desc_tmp; 20797 + descriptor_entry_t *new_desc_registry = NULL; 20798 + HASH_ITER(hh, desc_registry, desc, desc_tmp) { 20799 + HASH_DEL(desc_registry, desc); 20800 + 20801 + if (desc->has_getter) desc->getter = fwd_val(ctx, desc->getter); 20802 + if (desc->has_setter) desc->setter = fwd_val(ctx, desc->setter); 20803 + 20804 + jsoff_t old_obj_off = (jsoff_t)(desc->key >> 32); 20805 + uint32_t key_hash = (uint32_t)(desc->key & 0xFFFFFFFF); 20806 + jsoff_t new_obj_off = fwd_off(ctx, old_obj_off); 20807 + desc->key = ((uint64_t)new_obj_off << 32) | key_hash; 20808 + 20809 + HASH_ADD(hh, new_desc_registry, key, sizeof(uint64_t), desc); 20810 + } 20811 + desc_registry = new_desc_registry; 20812 + 20813 + for (coroutine_t *coro = pending_coroutines.head; coro; coro = coro->next) { 20814 + coro->scope = fwd_val(ctx, coro->scope); 20815 + coro->this_val = fwd_val(ctx, coro->this_val); 20816 + coro->awaited_promise = fwd_val(ctx, coro->awaited_promise); 20817 + coro->result = fwd_val(ctx, coro->result); 20818 + coro->async_func = fwd_val(ctx, coro->async_func); 20819 + coro->yield_value = fwd_val(ctx, coro->yield_value); 20820 + 20821 + if (coro->mco) { 20822 + async_exec_context_t *actx = (async_exec_context_t *)mco_get_user_data(coro->mco); 20823 + if (actx) { 20824 + actx->closure_scope = fwd_val(ctx, actx->closure_scope); 20825 + actx->result = fwd_val(ctx, actx->result); 20826 + actx->promise = fwd_val(ctx, actx->promise); 20827 + } 20828 + } 20829 + } 20830 + 20831 + esm_module_t *mod, *mod_tmp; 20832 + HASH_ITER(hh, global_module_cache.modules, mod, mod_tmp) { 20833 + mod->namespace_obj = fwd_val(ctx, mod->namespace_obj); 20834 + mod->default_export = fwd_val(ctx, mod->default_export); 20835 + } 20836 + 20837 + timer_gc_update_roots(fwd_val, ctx); 20838 + ffi_gc_update_roots(fwd_val, ctx); 20839 + fetch_gc_update_roots(fwd_val, ctx); 20840 + fs_gc_update_roots(fwd_val, ctx); 20841 + 20842 + map_registry_entry_t *map_reg, *map_reg_tmp; 20843 + HASH_ITER(hh, map_registry, map_reg, map_reg_tmp) { 20844 + if (map_reg->head && *map_reg->head) { 20845 + map_entry_t *entry, *entry_tmp; 20846 + HASH_ITER(hh, *map_reg->head, entry, entry_tmp) entry->value = fwd_val(ctx, entry->value); 20847 + } 20848 + } 20849 + 20850 + set_registry_entry_t *set_reg, *set_reg_tmp; 20851 + HASH_ITER(hh, set_registry, set_reg, set_reg_tmp) { 20852 + if (set_reg->head && *set_reg->head) { 20853 + set_entry_t *entry, *entry_tmp; 20854 + HASH_ITER(hh, *set_reg->head, entry, entry_tmp) entry->value = fwd_val(ctx, entry->value); 20855 + } 20856 + } 20857 + 20858 + memset(intern_prop_cache, 0, sizeof(intern_prop_cache)); 20859 + } 20732 20860 20733 20861 bool js_chkargs(jsval_t *args, int nargs, const char *spec) { 20734 20862 int i = 0, ok = 1;
+380
src/gc.c
··· 1 + #include "arena.h" 2 + #include "internal.h" 3 + #include "config.h" 4 + 5 + #include <string.h> 6 + #include <stdlib.h> 7 + #include <stdio.h> 8 + 9 + #define MCO_API extern 10 + #include "minicoro.h" 11 + 12 + typedef struct { 13 + jsoff_t old_off; 14 + jsoff_t new_off; 15 + } gc_forward_t; 16 + 17 + typedef struct { 18 + gc_forward_t *entries; 19 + size_t count; 20 + size_t capacity; 21 + } gc_forward_table_t; 22 + 23 + typedef struct { 24 + struct js *js; 25 + uint8_t *new_mem; 26 + jsoff_t new_brk; 27 + jsoff_t new_size; 28 + uint8_t *mark_bits; 29 + gc_forward_table_t fwd; 30 + } gc_ctx_t; 31 + 32 + static jsoff_t gc_copy_entity(gc_ctx_t *ctx, jsoff_t old_off); 33 + static jsval_t gc_update_val(gc_ctx_t *ctx, jsval_t val); 34 + 35 + static inline void mark_set(gc_ctx_t *ctx, jsoff_t off) { 36 + jsoff_t idx = off >> 2; 37 + ctx->mark_bits[idx >> 3] |= (1 << (idx & 7)); 38 + } 39 + 40 + static void fwd_init(gc_forward_table_t *fwd) { 41 + fwd->capacity = 256; 42 + fwd->entries = (gc_forward_t *)malloc(fwd->capacity * sizeof(gc_forward_t)); 43 + fwd->count = 0; 44 + } 45 + 46 + static void fwd_add(gc_forward_table_t *fwd, jsoff_t old_off, jsoff_t new_off) { 47 + if (fwd->count >= fwd->capacity) { 48 + fwd->capacity *= 2; 49 + fwd->entries = (gc_forward_t *)realloc(fwd->entries, fwd->capacity * sizeof(gc_forward_t)); 50 + } 51 + fwd->entries[fwd->count].old_off = old_off; 52 + fwd->entries[fwd->count].new_off = new_off; 53 + fwd->count++; 54 + } 55 + 56 + static jsoff_t fwd_lookup(gc_forward_table_t *fwd, jsoff_t old_off) { 57 + // linear search, could be optimized with hash table for large heaps 58 + for (size_t i = 0; i < fwd->count; i++) { 59 + if (fwd->entries[i].old_off == old_off) return fwd->entries[i].new_off; 60 + } 61 + return (jsoff_t)~0; 62 + } 63 + 64 + static void fwd_free(gc_forward_table_t *fwd) { 65 + free(fwd->entries); 66 + fwd->entries = NULL; 67 + fwd->count = 0; 68 + fwd->capacity = 0; 69 + } 70 + 71 + static inline jsoff_t gc_loadoff(uint8_t *mem, jsoff_t off) { 72 + jsoff_t val; 73 + memcpy(&val, &mem[off], sizeof(val)); return val; 74 + } 75 + 76 + static inline jsval_t gc_loadval(uint8_t *mem, jsoff_t off) { 77 + jsval_t val; 78 + memcpy(&val, &mem[off], sizeof(val)); return val; 79 + } 80 + 81 + static inline void gc_saveoff(uint8_t *mem, jsoff_t off, jsoff_t val) { 82 + memcpy(&mem[off], &val, sizeof(val)); 83 + } 84 + 85 + static inline void gc_saveval(uint8_t *mem, jsoff_t off, jsval_t val) { 86 + memcpy(&mem[off], &val, sizeof(val)); 87 + } 88 + 89 + static jsoff_t gc_esize(jsoff_t w) { 90 + jsoff_t cleaned = w & ~(CONSTMASK | ARRMASK | SLOTMASK); 91 + switch (cleaned & 3U) { 92 + case JS_T_OBJ: return (jsoff_t)(sizeof(jsoff_t) + sizeof(jsoff_t)); 93 + case JS_T_PROP: return (jsoff_t)(sizeof(jsoff_t) + sizeof(jsoff_t) + sizeof(jsval_t)); 94 + case JS_T_STR: return (jsoff_t)(sizeof(jsoff_t) + ((cleaned >> 2) + 3) / 4 * 4); 95 + default: return (jsoff_t)~0U; 96 + } 97 + } 98 + 99 + static jsoff_t gc_alloc(gc_ctx_t *ctx, size_t size) { 100 + size = (size + 3) / 4 * 4; 101 + if (ctx->new_brk + size > ctx->new_size) { 102 + return (jsoff_t)~0; 103 + } 104 + jsoff_t off = ctx->new_brk; 105 + ctx->new_brk += (jsoff_t)size; 106 + return off; 107 + } 108 + 109 + static inline bool gc_is_tagged(jsval_t v) { 110 + return (v >> 53) == NANBOX_PREFIX_CHK; 111 + } 112 + 113 + static inline uint8_t gc_vtype(jsval_t v) { 114 + return gc_is_tagged(v) ? ((v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK) : 255; 115 + } 116 + 117 + static inline size_t gc_vdata(jsval_t v) { 118 + return (size_t)(v & NANBOX_DATA_MASK); 119 + } 120 + 121 + static inline jsval_t gc_mkval(uint8_t type, uint64_t data) { 122 + return NANBOX_PREFIX | ((jsval_t)(type & NANBOX_TYPE_MASK) << NANBOX_TYPE_SHIFT) | (data & NANBOX_DATA_MASK); 123 + } 124 + 125 + static jsoff_t gc_copy_string(gc_ctx_t *ctx, jsoff_t old_off) { 126 + if (old_off >= ctx->js->brk) return old_off; 127 + 128 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 129 + if (new_off != (jsoff_t)~0) return new_off; 130 + 131 + jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 132 + if ((header & 3) != JS_T_STR) return old_off; 133 + 134 + jsoff_t size = gc_esize(header); 135 + if (size == (jsoff_t)~0) return old_off; 136 + 137 + new_off = gc_alloc(ctx, size); 138 + if (new_off == (jsoff_t)~0) return old_off; 139 + 140 + memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 141 + 142 + fwd_add(&ctx->fwd, old_off, new_off); 143 + mark_set(ctx, old_off); 144 + 145 + return new_off; 146 + } 147 + 148 + static jsoff_t gc_copy_prop(gc_ctx_t *ctx, jsoff_t old_off) { 149 + if (old_off >= ctx->js->brk) return old_off; 150 + 151 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 152 + if (new_off != (jsoff_t)~0) return new_off; 153 + 154 + jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 155 + if ((header & 3) != JS_T_PROP) { 156 + return old_off; 157 + } 158 + 159 + jsoff_t size = gc_esize(header); 160 + if (size == (jsoff_t)~0) return old_off; 161 + 162 + new_off = gc_alloc(ctx, size); 163 + if (new_off == (jsoff_t)~0) return old_off; 164 + 165 + memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 166 + 167 + fwd_add(&ctx->fwd, old_off, new_off); 168 + mark_set(ctx, old_off); 169 + 170 + jsoff_t next_prop = header & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 171 + if (next_prop != 0 && next_prop < ctx->js->brk) { 172 + jsoff_t new_next = gc_copy_prop(ctx, next_prop); 173 + jsoff_t new_header = (new_next & ~3U) | (header & (3U | CONSTMASK | ARRMASK | SLOTMASK)); 174 + gc_saveoff(ctx->new_mem, new_off, new_header); 175 + } 176 + 177 + bool is_slot = (header & SLOTMASK) != 0; 178 + 179 + if (!is_slot) { 180 + jsoff_t key_off = gc_loadoff(ctx->js->mem, old_off + sizeof(jsoff_t)); 181 + if (key_off < ctx->js->brk) { 182 + jsoff_t new_key = gc_copy_string(ctx, key_off); 183 + gc_saveoff(ctx->new_mem, new_off + sizeof(jsoff_t), new_key); 184 + } 185 + 186 + jsval_t val = gc_loadval(ctx->js->mem, old_off + sizeof(jsoff_t) + sizeof(jsoff_t)); 187 + jsval_t new_val = gc_update_val(ctx, val); 188 + gc_saveval(ctx->new_mem, new_off + sizeof(jsoff_t) + sizeof(jsoff_t), new_val); 189 + } else { 190 + jsval_t val = gc_loadval(ctx->js->mem, old_off + sizeof(jsoff_t) + sizeof(jsoff_t)); 191 + jsval_t new_val = gc_update_val(ctx, val); 192 + gc_saveval(ctx->new_mem, new_off + sizeof(jsoff_t) + sizeof(jsoff_t), new_val); 193 + } 194 + 195 + return new_off; 196 + } 197 + 198 + static jsoff_t gc_copy_object(gc_ctx_t *ctx, jsoff_t old_off) { 199 + if (old_off >= ctx->js->brk) return old_off; 200 + 201 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 202 + if (new_off != (jsoff_t)~0) return new_off; 203 + 204 + jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 205 + if ((header & 3) != JS_T_OBJ) return old_off; 206 + 207 + jsoff_t size = gc_esize(header); 208 + if (size == (jsoff_t)~0) return old_off; 209 + 210 + new_off = gc_alloc(ctx, size); 211 + if (new_off == (jsoff_t)~0) return old_off; 212 + 213 + memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 214 + 215 + fwd_add(&ctx->fwd, old_off, new_off); 216 + mark_set(ctx, old_off); 217 + 218 + jsoff_t first_prop = header & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 219 + if (first_prop != 0 && first_prop < ctx->js->brk) { 220 + jsoff_t new_first = gc_copy_prop(ctx, first_prop); 221 + jsoff_t new_header = (new_first & ~3U) | (header & (3U | CONSTMASK | ARRMASK | SLOTMASK)); 222 + gc_saveoff(ctx->new_mem, new_off, new_header); 223 + } 224 + 225 + jsoff_t parent_off = gc_loadoff(ctx->js->mem, old_off + sizeof(jsoff_t)); 226 + if (parent_off != 0 && parent_off < ctx->js->brk) { 227 + jsoff_t new_parent = gc_copy_object(ctx, parent_off); 228 + gc_saveoff(ctx->new_mem, new_off + sizeof(jsoff_t), new_parent); 229 + } 230 + 231 + return new_off; 232 + } 233 + 234 + static jsoff_t gc_copy_entity(gc_ctx_t *ctx, jsoff_t old_off) { 235 + if (old_off >= ctx->js->brk) return old_off; 236 + 237 + jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 238 + switch (header & 3) { 239 + case JS_T_OBJ: return gc_copy_object(ctx, old_off); 240 + case JS_T_PROP: return gc_copy_prop(ctx, old_off); 241 + case JS_T_STR: return gc_copy_string(ctx, old_off); 242 + default: return old_off; 243 + } 244 + } 245 + 246 + static jsval_t gc_update_val(gc_ctx_t *ctx, jsval_t val) { 247 + if (!gc_is_tagged(val)) return val; 248 + 249 + uint8_t type = gc_vtype(val); 250 + jsoff_t old_off = (jsoff_t)gc_vdata(val); 251 + 252 + switch (type) { 253 + case JS_V_OBJ: 254 + case JS_V_FUNC: 255 + case JS_V_ARR: 256 + case JS_V_PROMISE: 257 + case JS_V_GENERATOR: { 258 + if (old_off >= ctx->js->brk) return val; 259 + jsoff_t new_off = gc_copy_object(ctx, old_off); 260 + if (new_off != (jsoff_t)~0) return gc_mkval(type, new_off); 261 + break; 262 + } 263 + case JS_V_STR: { 264 + if (old_off >= ctx->js->brk) return val; 265 + jsoff_t new_off = gc_copy_string(ctx, old_off); 266 + if (new_off != (jsoff_t)~0) { 267 + return gc_mkval(type, new_off); 268 + } 269 + break; 270 + } 271 + case JS_V_PROP: { 272 + if (old_off >= ctx->js->brk) return val; 273 + jsoff_t new_off = gc_copy_prop(ctx, old_off); 274 + if (new_off != (jsoff_t)~0) { 275 + return gc_mkval(type, new_off); 276 + } 277 + break; 278 + } 279 + case JS_V_BIGINT: { 280 + if (old_off >= ctx->js->brk) return val; 281 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 282 + if (new_off != (jsoff_t)~0) { 283 + return gc_mkval(type, new_off); 284 + } 285 + jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 286 + size_t total = (header >> 4) + sizeof(jsoff_t); 287 + total = (total + 3) / 4 * 4; 288 + new_off = gc_alloc(ctx, total); 289 + if (new_off != (jsoff_t)~0) { 290 + memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], total); 291 + fwd_add(&ctx->fwd, old_off, new_off); 292 + mark_set(ctx, old_off); 293 + return gc_mkval(type, new_off); 294 + } 295 + break; 296 + } 297 + default: break; 298 + } 299 + 300 + return val; 301 + } 302 + 303 + static jsoff_t gc_fwd_off_callback(void *ctx_ptr, jsoff_t old_off) { 304 + gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; 305 + if (old_off == 0) return 0; 306 + if (old_off >= ctx->js->brk) return old_off; 307 + 308 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 309 + if (new_off != (jsoff_t)~0) return new_off; 310 + 311 + new_off = gc_copy_object(ctx, old_off); 312 + return (new_off != (jsoff_t)~0) ? new_off : old_off; 313 + } 314 + 315 + static jsval_t gc_fwd_val_callback(void *ctx_ptr, jsval_t val) { 316 + gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; 317 + return gc_update_val(ctx, val); 318 + } 319 + 320 + size_t js_gc_compact(struct js *js) { 321 + if (!js || js->brk == 0) return 0; 322 + 323 + mco_coro *running = mco_running(); 324 + int in_coroutine = (running != NULL && running->stack_base != NULL); 325 + if (in_coroutine || js_has_pending_coroutines()) { 326 + ANT_GC_COLLECT(); 327 + return 0; 328 + } 329 + 330 + size_t old_brk = js->brk; 331 + size_t new_size = js->size; 332 + 333 + uint8_t *new_mem = (uint8_t *)ANT_GC_MALLOC(new_size); 334 + if (!new_mem) return 0; 335 + memset(new_mem, 0, new_size); 336 + 337 + size_t bitmap_size = (js->brk / 4 + 7) / 8 + 1; 338 + uint8_t *mark_bits = (uint8_t *)calloc(1, bitmap_size); 339 + if (!mark_bits) return 0; 340 + 341 + gc_ctx_t ctx; 342 + ctx.js = js; 343 + ctx.new_mem = new_mem; 344 + ctx.new_brk = 0; 345 + ctx.new_size = (jsoff_t)new_size; 346 + ctx.mark_bits = mark_bits; 347 + fwd_init(&ctx.fwd); 348 + 349 + if (js->brk > 0) { 350 + jsoff_t header_at_0 = gc_loadoff(js->mem, 0); 351 + if ((header_at_0 & 3) == JS_T_OBJ) gc_copy_object(&ctx, 0); 352 + } 353 + 354 + jsoff_t scope_off = (jsoff_t)gc_vdata(js->scope); 355 + if (scope_off < js->brk) { 356 + jsoff_t new_scope = gc_copy_object(&ctx, scope_off); 357 + js->scope = gc_mkval(JS_V_OBJ, new_scope); 358 + } 359 + 360 + js->this_val = gc_update_val(&ctx, js->this_val); 361 + js->module_ns = gc_update_val(&ctx, js->module_ns); 362 + js->current_func = gc_update_val(&ctx, js->current_func); 363 + js->thrown_value = gc_update_val(&ctx, js->thrown_value); 364 + js->tval = gc_update_val(&ctx, js->tval); 365 + js_gc_update_roots(js, gc_fwd_off_callback, gc_fwd_val_callback, &ctx); 366 + 367 + js->mem = new_mem; 368 + js->brk = ctx.new_brk; 369 + 370 + jsoff_t free_space = js->size - js->brk; 371 + if (free_space < js->lwm || js->lwm == 0) { 372 + js->lwm = free_space; 373 + } 374 + 375 + free(mark_bits); 376 + fwd_free(&ctx.fwd); 377 + ANT_GC_COLLECT(); 378 + 379 + return (old_brk > ctx.new_brk ? old_brk - ctx.new_brk : 0); 380 + }
+10 -2
src/modules/builtin.c
··· 4 4 #include <string.h> 5 5 6 6 #include "ant.h" 7 - #include "arena.h" 7 + #include "gc.h" 8 8 #include "runtime.h" 9 9 #include "modules/builtin.h" 10 10 ··· 68 68 69 69 size_t heap_before = GC_get_heap_size(); 70 70 size_t used_before = GC_get_heap_size() - GC_get_free_bytes(); 71 + size_t arena_before = js_getbrk(js); 71 72 72 - ANT_GC_COLLECT(); 73 + size_t arena_freed = js_gc_compact(js); 74 + size_t arena_after = js_getbrk(js); 73 75 74 76 size_t heap_after = GC_get_heap_size(); 75 77 size_t used_after = GC_get_heap_size() - GC_get_free_bytes(); ··· 81 83 js_set(js, result, "usedBefore", js_mknum((double)used_before)); 82 84 js_set(js, result, "usedAfter", js_mknum((double)used_after)); 83 85 js_set(js, result, "freed", js_mknum((double)freed)); 86 + js_set(js, result, "arenaBefore", js_mknum((double)arena_before)); 87 + js_set(js, result, "arenaAfter", js_mknum((double)arena_after)); 88 + js_set(js, result, "arenaFreed", js_mknum((double)arena_freed)); 84 89 85 90 return result; 86 91 } ··· 90 95 (void) args; (void) nargs; 91 96 92 97 jsval_t result = js_mkobj(js); 98 + size_t arena_size = js_getbrk(js); 99 + 100 + js_set(js, result, "arenaSize", js_mknum((double)arena_size)); 93 101 js_set(js, result, "heapSize", js_mknum((double)GC_get_heap_size())); 94 102 js_set(js, result, "freeBytes", js_mknum((double)GC_get_free_bytes())); 95 103 js_set(js, result, "usedBytes", js_mknum((double)(GC_get_heap_size() - GC_get_free_bytes())));
+12
src/modules/fetch.c
··· 321 321 if (pending_requests && utarray_len(pending_requests) > 0) usleep(1000); 322 322 } 323 323 } 324 + 325 + void fetch_gc_update_roots(GC_FWD_ARGS) { 326 + if (!pending_requests) return; 327 + unsigned int len = utarray_len(pending_requests); 328 + for (unsigned int i = 0; i < len; i++) { 329 + fetch_request_t **reqp = (fetch_request_t **)utarray_eltptr(pending_requests, i); 330 + if (reqp && *reqp) { 331 + (*reqp)->promise = fwd_val(ctx, (*reqp)->promise); 332 + (*reqp)->headers_obj = fwd_val(ctx, (*reqp)->headers_obj); 333 + } 334 + } 335 + }
+9
src/modules/ffi.c
··· 979 979 ret_undef: 980 980 return js_mkundef(); 981 981 } 982 + 983 + void ffi_gc_update_roots(GC_FWD_ARGS) { 984 + pthread_mutex_lock(&ffi_callbacks_mutex); 985 + ffi_callback_t *cb, *tmp; 986 + HASH_ITER(hh, ffi_callbacks, cb, tmp) { 987 + cb->js_func = fwd_val(ctx, cb->js_func); 988 + } 989 + pthread_mutex_unlock(&ffi_callbacks_mutex); 990 + }
+11
src/modules/fs.c
··· 1158 1158 if (pending_requests && utarray_len(pending_requests) > 0) usleep(1000); 1159 1159 } 1160 1160 } 1161 + 1162 + void fs_gc_update_roots(GC_FWD_ARGS) { 1163 + if (!pending_requests) return; 1164 + unsigned int len = utarray_len(pending_requests); 1165 + for (unsigned int i = 0; i < len; i++) { 1166 + fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); 1167 + if (reqp && *reqp) { 1168 + (*reqp)->promise = fwd_val(ctx, (*reqp)->promise); 1169 + } 1170 + } 1171 + }
+12
src/modules/timer.c
··· 318 318 js_set(js, global, "clearImmediate", js_mkfun(js_clear_immediate)); 319 319 js_set(js, global, "queueMicrotask", js_mkfun(js_queue_microtask)); 320 320 } 321 + 322 + void timer_gc_update_roots(GC_FWD_ARGS) { 323 + for (timer_entry_t *t = timer_state.timers; t; t = t->next) { 324 + t->callback = fwd_val(ctx, t->callback); 325 + } 326 + for (microtask_entry_t *m = timer_state.microtasks; m; m = m->next) { 327 + m->callback = fwd_val(ctx, m->callback); 328 + } 329 + for (immediate_entry_t *i = timer_state.immediates; i; i = i->next) { 330 + i->callback = fwd_val(ctx, i->callback); 331 + } 332 + }