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.

add support for virtual memory allocation and freeing in minicoro remove all depr js_gc and related funcs

+89 -430
+1 -5
include/ant.h
··· 32 32 struct js *js_create(void *buf, size_t len); 33 33 struct js *js_create_dynamic(size_t initial_size, size_t max_size); 34 34 35 - uint32_t js_gc(struct js *); 36 - void js_destroy(struct js *); 37 - void js_protect_init_memory(struct js *); 38 - 39 35 jsval_t js_glob(struct js *); 40 36 jsval_t js_eval(struct js *, const char *, size_t); 41 37 42 38 void js_dump(struct js *); 39 + void js_destroy(struct js *); 43 40 void js_mkscope(struct js *); 44 41 void js_delscope(struct js *); 45 - void js_setgct(struct js *, size_t); 46 42 bool js_truthy(struct js *, jsval_t); 47 43 void js_setmaxcss(struct js *, size_t); 48 44
+10 -4
include/arena.h
··· 6 6 #define GC_THREADS 1 7 7 #include <gc.h> 8 8 9 - #define ANT_GC_INIT() GC_INIT() 10 - #define ANT_GC_MALLOC(size) GC_MALLOC(size) 11 - #define ANT_GC_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC(size) 9 + static inline void ant_gc_init(void) { 10 + GC_INIT(); 11 + GC_set_all_interior_pointers(0); 12 + GC_disable(); 13 + } 14 + 15 + #define ANT_GC_INIT() ant_gc_init() 16 + #define ANT_GC_MALLOC(size) GC_MALLOC_IGNORE_OFF_PAGE(size) 17 + #define ANT_GC_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(size) 12 18 #define ANT_GC_REALLOC(ptr, size) GC_REALLOC(ptr, size) 13 19 #define ANT_GC_FREE(ptr) GC_FREE(ptr) 14 - #define ANT_GC_COLLECT() GC_gcollect() 20 + #define ANT_GC_COLLECT() do { GC_enable(); GC_gcollect(); GC_disable(); } while(0) 15 21 16 22 #define ANT_GC_REGISTER_ROOT(ptr) 17 23 #define ANT_GC_UNREGISTER_ROOT(ptr)
+1 -1
maidfile.toml
··· 27 27 [tasks.asan] 28 28 script = ['meson subprojects download', ''' 29 29 bash -c 'CC="ccache $(which clang)" \ 30 - meson setup build --wipe -Db_sanitize=address -Doptimization=0 -Db_lto=false -Dstrip=false' 30 + meson setup build --wipe -Db_sanitize=address -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false' 31 31 '''] 32 32 33 33 [tasks.install]
+1 -1
meson.build
··· 79 79 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 80 80 81 81 version_conf = configuration_data() 82 - version_conf.set('ANT_VERSION', '0.2.2.27') 82 + version_conf.set('ANT_VERSION', '0.2.2.28') 83 83 version_conf.set('ANT_GIT_HASH', git_hash) 84 84 version_conf.set('ANT_BUILD_DATE', build_date) 85 85
+23 -375
src/ant.c
··· 22 22 #include <uthash.h> 23 23 #include <float.h> 24 24 25 + #define MCO_USE_VMEM_ALLOCATOR 26 + #define MCO_ZERO_MEMORY 27 + #define MCO_DEFAULT_STACK_SIZE (1024 * 1024) 28 + #define MINICORO_IMPL 29 + #include <minicoro.h> 30 + 25 31 #include "modules/fs.h" 26 32 #include "modules/timer.h" 27 33 #include "modules/fetch.h" 28 34 #include "modules/symbol.h" 29 35 #include "modules/ffi.h" 30 36 31 - #define MINICORO_IMPL 32 - #include "minicoro.h" 37 + #define CORO_MALLOC(size) calloc(1, size) 38 + #define CORO_FREE(ptr) free(ptr) 33 39 34 40 _Static_assert(sizeof(double) == 8, "NaN-boxing requires 64-bit IEEE 754 doubles"); 35 41 _Static_assert(sizeof(uint64_t) == 8, "NaN-boxing requires 64-bit integers"); ··· 110 116 coroutine_t *coro; 111 117 } async_exec_context_t; 112 118 113 - typedef struct { 114 - jsoff_t offset; 115 - jsoff_t size; 116 - uint8_t type; 117 - char detail[128]; 118 - } FreeListEntry; 119 - 120 - static UT_array *global_free_list = NULL; 121 - static UT_array *global_scope_stack = NULL; 122 - static jsoff_t protected_brk = 0; 123 - 124 119 static const UT_icd jsoff_icd = { 125 120 .sz = sizeof(jsoff_t), 126 121 .init = NULL, ··· 128 123 .dtor = NULL, 129 124 }; 130 125 131 - static const UT_icd free_list_icd = { 132 - .sz = sizeof(FreeListEntry), 133 - .init = NULL, 134 - .copy = NULL, 135 - .dtor = NULL, 136 - }; 137 - 126 + static UT_array *global_scope_stack = NULL; 138 127 static this_stack_t global_this_stack = {NULL, 0, 0}; 139 128 static call_stack_t global_call_stack = {NULL, 0, 0}; 140 129 static coroutine_queue_t pending_coroutines = {NULL, NULL}; ··· 330 319 static dynamic_accessors_t *accessor_registry = NULL; 331 320 static descriptor_entry_t *desc_registry = NULL; 332 321 333 - void js_protect_init_memory(struct js *js) { 334 - protected_brk = js_getbrk(js); 335 - if (protected_brk < 0x2000) protected_brk = 0x2000; 336 - } 337 - 338 322 void ant_register_library(ant_library_init_fn init_fn, const char *name, ...) { 339 323 va_list args; 340 324 const char *alias = name; ··· 395 379 uint8_t *mem; // available JS memory 396 380 jsoff_t size; // memory size 397 381 jsoff_t brk; // current mem usage boundary 398 - jsoff_t gct; // GC threshold. if brk > gct, trigger GC 399 382 jsoff_t maxcss; // maximum allowed C stack size usage 400 383 void *cstk; // C stack pointer at the beginning of js_eval() 401 384 jsval_t current_func; // currently executing function (for native closures) ··· 723 706 static jsval_t do_instanceof(struct js *js, jsval_t l, jsval_t r); 724 707 static jsval_t do_in(struct js *js, jsval_t l, jsval_t r); 725 708 static jsval_t resolveprop(struct js *js, jsval_t v); 726 - static jsoff_t free_list_allocate(size_t size); 727 709 static jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len); 728 710 static jsoff_t lkp_interned(struct js *js, jsval_t obj, const char *search_intern, size_t len); 729 711 static inline const char *get_koff_intern(jsoff_t koff); ··· 808 790 static void free_coroutine(coroutine_t *coro); 809 791 static bool has_ready_coroutines(void); 810 792 793 + // reserve large virtual stack (1MB) but only ~4-8KB physical per coroutine 811 794 static size_t calculate_coro_stack_size(void) { 812 795 const char *env_stack = getenv("ANT_CORO_STACK_SIZE"); 813 796 if (env_stack) { 814 797 size_t size = (size_t)atoi(env_stack) * 1024; 815 - if (size >= 16 * 1024 && size <= 8 * 1024 * 1024) return size; 798 + if (size >= 32 * 1024 && size <= 8 * 1024 * 1024) return size; 816 799 } 817 - 818 - return 128 * 1024; 800 + return 0; 819 801 } 820 802 821 803 static void mco_async_entry(mco_coro* mco) { ··· 941 923 942 924 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) { 943 925 jsval_t promise = js_mkpromise(js); 944 - async_exec_context_t *ctx = (async_exec_context_t *)ANT_GC_MALLOC(sizeof(async_exec_context_t)); 926 + async_exec_context_t *ctx = (async_exec_context_t *)CORO_MALLOC(sizeof(async_exec_context_t)); 945 927 if (!ctx) return js_mkerr(js, "out of memory for async context"); 946 928 947 929 ctx->js = js; ··· 960 942 mco_coro* mco = NULL; 961 943 mco_result res = mco_create(&mco, &desc); 962 944 if (res != MCO_SUCCESS) { 963 - ANT_GC_FREE(ctx); 945 + CORO_FREE(ctx); 964 946 return js_mkerr(js, "failed to create minicoro coroutine"); 965 947 } 966 948 967 - coroutine_t *coro = (coroutine_t *)ANT_GC_MALLOC(sizeof(coroutine_t)); 949 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 968 950 if (!coro) { 969 951 mco_destroy(mco); 970 - ANT_GC_FREE(ctx); 952 + CORO_FREE(ctx); 971 953 return js_mkerr(js, "out of memory for coroutine"); 972 954 } 973 955 ··· 979 961 coro->result = js_mkundef(); 980 962 coro->async_func = js->current_func; 981 963 if (nargs > 0) { 982 - coro->args = (jsval_t *)ANT_GC_MALLOC(sizeof(jsval_t) * nargs); 964 + coro->args = (jsval_t *)CORO_MALLOC(sizeof(jsval_t) * nargs); 983 965 if (coro->args) memcpy(coro->args, args, sizeof(jsval_t) * nargs); 984 - } else { 985 - coro->args = NULL; 986 - } 966 + } else { coro->args = NULL; } 987 967 coro->nargs = nargs; 988 968 coro->is_settled = false; 989 969 coro->is_error = false; ··· 1002 982 if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 1003 983 remove_coroutine(coro); 1004 984 free_coroutine(coro); 1005 - ANT_GC_FREE(ctx); 985 + CORO_FREE(ctx); 1006 986 return js_mkerr(js, "failed to start coroutine"); 1007 987 } 1008 988 ··· 1010 990 if (mco_status(mco) == MCO_DEAD) { 1011 991 remove_coroutine(coro); 1012 992 free_coroutine(coro); 1013 - ANT_GC_FREE(ctx); 993 + CORO_FREE(ctx); 1014 994 } 1015 995 1016 996 return promise; ··· 1023 1003 mco_destroy(coro->mco); 1024 1004 coro->mco = NULL; 1025 1005 } 1026 - if (coro->args) ANT_GC_FREE(coro->args); 1027 - ANT_GC_FREE(coro); 1006 + if (coro->args) CORO_FREE(coro->args); 1007 + CORO_FREE(coro); 1028 1008 } 1029 1009 } 1030 1010 ··· 2254 2234 memcpy(new_mem, js->mem, js->brk); 2255 2235 memset(new_mem + js->brk, 0, new_mem_size - js->brk); 2256 2236 2257 - jsoff_t old_size = js->size; 2258 2237 js->mem = new_mem; 2259 2238 js->size = (jsoff_t)(new_mem_size / 8U * 8U); 2260 2239 2261 - if (old_size > 0) { 2262 - js->gct = (js->size * js->gct) / old_size; 2263 - } else { 2264 - js->gct = js->size / 2; 2265 - } 2266 - 2267 2240 return true; 2268 2241 } 2269 2242 ··· 2271 2244 static jsoff_t js_alloc(struct js *js, size_t size) { 2272 2245 size = align32((jsoff_t) size); 2273 2246 2274 - // jsoff_t ofs = free_list_allocate(size); 2275 - // if (ofs != (jsoff_t) ~0) return ofs; 2276 - 2277 2247 jsoff_t ofs = js->brk; 2278 2248 if (js->brk + size > js->size) { 2279 2249 if (js_try_grow_memory(js, size)) { ··· 2281 2251 if (js->brk + size > js->size) return ~(jsoff_t) 0; 2282 2252 } else { 2283 2253 ANT_GC_COLLECT(); 2284 - // js_gc(js); disabled 2285 2254 ofs = js->brk; 2286 2255 if (js->brk + size > js->size) { 2287 2256 if (js_try_grow_memory(js, size)) { 2288 2257 ofs = js->brk; 2289 2258 if (js->brk + size > js->size) return ~(jsoff_t) 0; 2290 - } else { 2291 - return ~(jsoff_t) 0; 2292 - } 2259 + } else return ~(jsoff_t) 0; 2293 2260 } 2294 2261 } 2295 2262 } ··· 3210 3177 return t == T_OBJ || t == T_PROP || t == T_STR || t == T_FUNC || t == T_ARR || t == T_PROMISE; 3211 3178 } 3212 3179 3213 - static void js_mark_all_entities_for_deletion(struct js *js) { 3214 - for (jsoff_t v, off = 0; off < js->brk; off += esize(v & ~(GCMASK | CONSTMASK))) { 3215 - v = loadoff(js, off); 3216 - saveoff(js, off, v | GCMASK); 3217 - } 3218 - } 3219 - 3220 - static jsoff_t js_unmark_entity(struct js *js, jsoff_t off) { 3221 - if (off >= js->brk) return 0; 3222 - jsoff_t v = loadoff(js, off); 3223 - if (v & GCMASK) { 3224 - saveoff(js, off, v & ~GCMASK); 3225 - jsoff_t cleaned = v & ~(GCMASK | CONSTMASK); 3226 - if ((cleaned & 3) == T_OBJ) js_unmark_entity(js, cleaned & ~3); 3227 - if ((cleaned & 3) == T_PROP) { 3228 - js_unmark_entity(js, cleaned & ~3); 3229 - js_unmark_entity(js, loadoff(js, (jsoff_t) (off + sizeof(off)))); 3230 - jsval_t val = loadval(js, (jsoff_t) (off + sizeof(off) + sizeof(off))); 3231 - if (is_mem_entity(vtype(val))) js_unmark_entity(js, (jsoff_t) vdata(val)); 3232 - } 3233 - } 3234 - return v & ~(GCMASK | CONSTMASK | 3U); 3235 - } 3236 - 3237 - static void js_unmark_jsval(struct js *js, jsval_t v) { 3238 - if (is_mem_entity(vtype(v))) js_unmark_entity(js, (jsoff_t) vdata(v)); 3239 - } 3240 - 3241 - static void js_unmark_used_entities(struct js *js) { 3242 - js_unmark_entity(js, (jsoff_t) vdata(js->scope)); 3243 - if (global_scope_stack) { 3244 - jsoff_t *p = NULL; 3245 - while ((p = (jsoff_t *)utarray_next(global_scope_stack, p)) != NULL) { 3246 - js_unmark_entity(js, *p); 3247 - } 3248 - } 3249 - 3250 - js_unmark_entity(js, 0); 3251 - if (js->nogc) js_unmark_entity(js, js->nogc); 3252 - 3253 - mco_coro *running = mco_running(); 3254 - if (running) { 3255 - async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(running); 3256 - if (ctx) { 3257 - js_unmark_jsval(js, ctx->closure_scope); 3258 - js_unmark_jsval(js, ctx->result); 3259 - js_unmark_jsval(js, ctx->promise); 3260 - if (ctx->coro) { 3261 - js_unmark_jsval(js, ctx->coro->scope); 3262 - js_unmark_jsval(js, ctx->coro->this_val); 3263 - js_unmark_jsval(js, ctx->coro->awaited_promise); 3264 - js_unmark_jsval(js, ctx->coro->result); 3265 - js_unmark_jsval(js, ctx->coro->async_func); 3266 - js_unmark_jsval(js, ctx->coro->yield_value); 3267 - for (int i = 0; i < ctx->coro->nargs; i++) js_unmark_jsval(js, ctx->coro->args[i]); 3268 - } 3269 - } 3270 - } 3271 - 3272 - for (coroutine_t *coro = pending_coroutines.head; coro != NULL; coro = coro->next) { 3273 - js_unmark_jsval(js, coro->scope); 3274 - js_unmark_jsval(js, coro->this_val); 3275 - js_unmark_jsval(js, coro->awaited_promise); 3276 - js_unmark_jsval(js, coro->result); 3277 - js_unmark_jsval(js, coro->async_func); 3278 - js_unmark_jsval(js, coro->yield_value); 3279 - for (int i = 0; i < coro->nargs; i++) js_unmark_jsval(js, coro->args[i]); 3280 - } 3281 - 3282 - for (int i = 0; i < global_this_stack.depth; i++) { 3283 - js_unmark_jsval(js, global_this_stack.stack[i]); 3284 - } 3285 - } 3286 - 3287 - static void init_free_list(void) { 3288 - if (global_free_list == NULL) { 3289 - utarray_new(global_free_list, &free_list_icd); 3290 - } else { 3291 - utarray_clear(global_free_list); 3292 - } 3293 - } 3294 - 3295 - static void free_list_clear(void) { 3296 - if (global_free_list != NULL) utarray_clear(global_free_list); 3297 - } 3298 - 3299 - static void free_list_compact(void) { 3300 - unsigned int len = utarray_len(global_free_list); 3301 - if (len <= 1) return; 3302 - 3303 - FreeListEntry *entries = (FreeListEntry *)utarray_front(global_free_list); 3304 - 3305 - for (unsigned int i = 0; i < len - 1; i++) { 3306 - for (unsigned int j = 0; j < len - i - 1; j++) { 3307 - if (entries[j].offset > entries[j + 1].offset) { 3308 - FreeListEntry temp = entries[j]; 3309 - entries[j] = entries[j + 1]; 3310 - entries[j + 1] = temp; 3311 - } 3312 - } 3313 - } 3314 - 3315 - unsigned int write_pos = 0; 3316 - for (unsigned int i = 1; i < len; i++) { 3317 - if (entries[write_pos].offset + entries[write_pos].size == entries[i].offset) { 3318 - entries[write_pos].size += entries[i].size; 3319 - } else { 3320 - write_pos++; 3321 - entries[write_pos] = entries[i]; 3322 - } 3323 - } 3324 - 3325 - unsigned int final_count = write_pos + 1; 3326 - while (utarray_len(global_free_list) > final_count) { 3327 - utarray_pop_back(global_free_list); 3328 - } 3329 - } 3330 - 3331 - static jsoff_t free_list_zero_out(struct js *js) { 3332 - unsigned int len = utarray_len(global_free_list); 3333 - if (len == 0) return 0; 3334 - 3335 - jsoff_t safe_threshold = protected_brk > 0 ? protected_brk + 0x400 : 0x1000; 3336 - jsoff_t total_freed = 0; 3337 - FreeListEntry *entries = (FreeListEntry *)utarray_front(global_free_list); 3338 - for (unsigned int i = 0; i < len; i++) { 3339 - if (entries[i].offset > 0 && entries[i].size > 0) { 3340 - if (entries[i].offset < safe_threshold) continue; 3341 - if (entries[i].offset + entries[i].size > js->size) continue; 3342 - // ugh disable zeroing for now until a better solution is found 3343 - // memset(&js->mem[entries[i].offset], 0, entries[i].size); 3344 - total_freed += entries[i].size; 3345 - } 3346 - } 3347 - 3348 - return total_freed; 3349 - } 3350 - 3351 - static jsoff_t free_list_allocate(size_t size) { 3352 - unsigned int len = utarray_len(global_free_list); 3353 - if (len == 0) return ~(jsoff_t) 0; 3354 - size = align32((jsoff_t) size); 3355 - 3356 - FreeListEntry *entries = (FreeListEntry *)utarray_front(global_free_list); 3357 - jsoff_t safe_reuse_threshold = protected_brk > 0 ? protected_brk + 0x400 : 0x1000; 3358 - 3359 - for (unsigned int i = 0; i < len; i++) { 3360 - if (entries[i].offset >= safe_reuse_threshold && entries[i].size >= size) { 3361 - jsoff_t allocated_offset = entries[i].offset; 3362 - 3363 - entries[i].offset += size; 3364 - entries[i].size -= size; 3365 - 3366 - if (entries[i].size == 0) utarray_erase(global_free_list, i, 1); 3367 - return allocated_offset; 3368 - } 3369 - } 3370 - 3371 - return ~(jsoff_t) 0; 3372 - } 3373 - 3374 - static bool is_builtin_or_system(jsoff_t offset, struct js *js) { 3375 - jsval_t obj_val = mkval(T_OBJ, offset); 3376 - 3377 - jsoff_t native_off = lkp_interned(js, obj_val, INTERN_NATIVE_FUNC, 13); 3378 - if (native_off != 0) return true; 3379 - 3380 - jsoff_t code_off = lkp_interned(js, obj_val, INTERN_CODE, 6); 3381 - if (code_off != 0) { 3382 - jsval_t code_val = resolveprop(js, mkval(T_PROP, code_off)); 3383 - if (vtype(code_val) == T_STR) { 3384 - jsoff_t slen, str_off = vstr(js, code_val, &slen); 3385 - if (slen > 10 && memcmp(&js->mem[str_off], "__builtin_", 10) == 0) return true; 3386 - } 3387 - } 3388 - 3389 - return false; 3390 - } 3391 - 3392 - static void free_list_add(jsoff_t offset, jsoff_t size, struct js *js) { 3393 - if (offset >= js->size || size == 0 || offset + size > js->size * 2) return; 3394 - jsoff_t safe_threshold = protected_brk > 0 ? protected_brk + 0x400 : 0x1000; 3395 - if (offset < safe_threshold) return; 3396 - 3397 - jsoff_t entity_val = loadoff(js, offset); 3398 - uint8_t entity_type = entity_val & 3; 3399 - 3400 - if (entity_type == T_OBJ && is_builtin_or_system(offset, js)) return; 3401 - if (entity_type == T_STR && offset < 0x1000) return; 3402 - 3403 - FreeListEntry entry = {0}; 3404 - entry.offset = offset; 3405 - entry.size = size; 3406 - entry.type = entity_type; 3407 - entry.detail[0] = '\0'; 3408 - 3409 - utarray_push_back(global_free_list, &entry); 3410 - } 3411 - 3412 - static void js_fixup_offsets(struct js *js, jsoff_t start, jsoff_t size) { 3413 - for (jsoff_t n, v, off = 0; off < js->brk; off += n) { 3414 - v = loadoff(js, off); 3415 - n = esize(v & ~(GCMASK | CONSTMASK)); 3416 - if (v & GCMASK) continue; 3417 - 3418 - jsoff_t flags = v & (GCMASK | CONSTMASK); 3419 - jsoff_t cleaned = v & ~(GCMASK | CONSTMASK); 3420 - if ((cleaned & 3) != T_OBJ && (cleaned & 3) != T_PROP) continue; 3421 - jsoff_t adjusted = cleaned > start ? cleaned - size : cleaned; 3422 - if (cleaned != adjusted) saveoff(js, off, adjusted | flags); 3423 - 3424 - if ((cleaned & 3) == T_OBJ) { 3425 - jsoff_t u = loadoff(js, (jsoff_t) (off + sizeof(jsoff_t))); 3426 - if (u > start) saveoff(js, (jsoff_t) (off + sizeof(jsoff_t)), u - size); 3427 - } 3428 - 3429 - #define FIXUP_JSVAL(val) do { \ 3430 - if (is_mem_entity(vtype(val)) && vdata(val) > start) \ 3431 - (val) = mkval(vtype(val), vdata(val) - size); \ 3432 - } while (0) 3433 - 3434 - #define FIXUP_JSVAL_AT(mem_off) do { \ 3435 - jsval_t _v = loadval(js, mem_off); \ 3436 - if (is_mem_entity(vtype(_v)) && vdata(_v) > start) \ 3437 - saveval(js, mem_off, mkval(vtype(_v), vdata(_v) - size)); \ 3438 - } while (0) 3439 - 3440 - #define FIXUP_OFF(off_var) do { if ((off_var) > start) (off_var) -= size; } while (0) 3441 - 3442 - if ((cleaned & 3) == T_PROP) { 3443 - jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(off))); 3444 - if (koff > start) saveoff(js, (jsoff_t) (off + sizeof(off)), koff - size); 3445 - FIXUP_JSVAL_AT((jsoff_t) (off + sizeof(off) + sizeof(off))); 3446 - } 3447 - } 3448 - 3449 - FIXUP_JSVAL(js->scope); 3450 - FIXUP_OFF(js->nogc); 3451 - if (js->code > (char *) js->mem && js->code - (char *) js->mem < js->size && js->code - (char *) js->mem > start) { 3452 - js->code -= size; 3453 - } 3454 - 3455 - if (global_scope_stack) { 3456 - jsoff_t *p = NULL; 3457 - while ((p = (jsoff_t *)utarray_next(global_scope_stack, p)) != NULL) FIXUP_OFF(*p); 3458 - } 3459 - 3460 - for (coroutine_t *coro = pending_coroutines.head; coro != NULL; coro = coro->next) { 3461 - FIXUP_JSVAL(coro->scope); 3462 - FIXUP_JSVAL(coro->this_val); 3463 - FIXUP_JSVAL(coro->awaited_promise); 3464 - FIXUP_JSVAL(coro->result); 3465 - FIXUP_JSVAL(coro->async_func); 3466 - FIXUP_JSVAL(coro->yield_value); 3467 - for (int i = 0; i < coro->nargs; i++) FIXUP_JSVAL(coro->args[i]); 3468 - } 3469 - 3470 - #undef FIXUP_JSVAL 3471 - #undef FIXUP_JSVAL_AT 3472 - #undef FIXUP_OFF 3473 - } 3474 - 3475 - static void js_compact_from_end(struct js *js) { 3476 - jsoff_t new_brk = js->brk; 3477 - 3478 - jsoff_t min_brk = (jsoff_t) vdata(js->scope) + 8; 3479 - if (global_scope_stack) { 3480 - jsoff_t *p = NULL; 3481 - while ((p = (jsoff_t *)utarray_next(global_scope_stack, p)) != NULL) { 3482 - if (*p + 8 > min_brk) min_brk = *p + 8; 3483 - } 3484 - } 3485 - 3486 - for (jsoff_t off = 0; off < js->brk; off += esize(loadoff(js, off) & ~(GCMASK | CONSTMASK))) { 3487 - jsoff_t v = loadoff(js, off); 3488 - if ((v & GCMASK) == 0) new_brk = off + esize(v & ~(GCMASK | CONSTMASK)); 3489 - } 3490 - 3491 - if (new_brk < min_brk) new_brk = min_brk; 3492 - js->brk = new_brk; 3493 - } 3494 - 3495 - static void js_clear_gc_marks(struct js *js) { 3496 - for (jsoff_t v, off = 0; off < js->brk; off += esize(v & ~(GCMASK | CONSTMASK))) { 3497 - v = loadoff(js, off); 3498 - if (v & GCMASK) { 3499 - jsoff_t size = esize(v & ~(GCMASK | CONSTMASK)); 3500 - free_list_add(off, size, js); 3501 - saveoff(js, off, v & ~GCMASK); 3502 - } 3503 - } 3504 - } 3505 - 3506 - jsoff_t js_gc(struct js *js) { 3507 - // setlwm(js); 3508 - // if (js->nogc == (jsoff_t) ~0) return 0; 3509 - // 3510 - // js_mark_all_entities_for_deletion(js); 3511 - // js_unmark_used_entities(js); 3512 - // js_compact_from_end(js); 3513 - // 3514 - // js_clear_gc_marks(js); 3515 - // free_list_compact(); 3516 - // 3517 - // jsoff_t freed = free_list_zero_out(js); 3518 - // if (global_free_list != NULL) utarray_clear(global_free_list); 3519 - // 3520 - // return freed; 3521 - 3522 - return 0; 3523 - } 3524 - 3525 3180 static int is_unicode_space(const unsigned char *p, jsoff_t remaining, bool *is_line_term) { 3526 3181 if (is_line_term) *is_line_term = false; 3527 3182 if (p[0] < 0x80) return 0; ··· 8055 7710 saveoff(js, obj_off, (deleted_next & ~3U) | (current & (GCMASK | CONSTMASK | 3U))); 8056 7711 saveoff(js, prop_off, loadoff(js, prop_off) | GCMASK); 8057 7712 invalidate_prop_cache(obj_off); 8058 - // js_gc(js); disabled 8059 7713 return js_mktrue(); 8060 7714 } 8061 7715 jsoff_t prev = first_prop; ··· 8067 7721 saveoff(js, prev, (deleted_next & ~3U) | (current & (GCMASK | CONSTMASK | 3U))); 8068 7722 saveoff(js, prop_off, loadoff(js, prop_off) | GCMASK); 8069 7723 invalidate_prop_cache(obj_off); 8070 - // js_gc(js); disabled 8071 7724 return js_mktrue(); 8072 7725 } 8073 7726 prev = next_prop; ··· 8155 7808 } 8156 7809 saveoff(js, prop_off, loadoff(js, prop_off) | GCMASK); 8157 7810 invalidate_prop_cache(owner_obj_off); 8158 - // js_gc(js); disabled 8159 7811 } 8160 7812 (void) save_pos; 8161 7813 (void) save_tok; ··· 11184 10836 11185 10837 static jsval_t js_stmt_impl(struct js *js) { 11186 10838 jsval_t res; 11187 - // if (js->brk > js->gct) js_gc(js); disabled 11188 10839 uint8_t stmt_tok = next(js); 11189 10840 11190 10841 switch (stmt_tok) { ··· 20213 19864 20214 19865 struct js *js_create(void *buf, size_t len) { 20215 19866 ANT_GC_INIT(); 20216 - init_free_list(); 20217 19867 intern_init(); 20218 19868 20219 19869 struct js *js = NULL; ··· 20226 19876 js->scope = mkobj(js, 0); 20227 19877 js->size = js->size / 8U * 8U; 20228 19878 js->lwm = js->size; 20229 - js->gct = js->size / 2; 20230 19879 js->this_val = js->scope; 20231 19880 js->errmsg_size = 4096; 20232 19881 js->errmsg = (char *)malloc(js->errmsg_size); ··· 20775 20424 double js_getnum(jsval_t value) { return tod(value); } 20776 20425 int js_getbool(jsval_t value) { return vdata(value) & 1 ? 1 : 0; } 20777 20426 20778 - void js_setgct(struct js *js, size_t gct) { js->gct = (jsoff_t) gct; } 20779 20427 void js_setmaxcss(struct js *js, size_t max) { js->maxcss = (jsoff_t) max; } 20780 20428 void js_set_filename(struct js *js, const char *filename) { js->filename = filename; } 20781 20429
+2 -8
src/main.c
··· 47 47 48 48 js_set_filename(js, "[eval]"); 49 49 js_setup_import_meta(js, "[eval]"); 50 - 51 50 js_mkscope(js); 52 - js_protect_init_memory(js); 53 51 54 52 js_set(js, js_glob(js), "__dirname", js_mkstr(js, ".", 1)); 55 53 js_set(js, js_glob(js), "__filename", js_mkstr(js, "[eval]", 6)); ··· 113 111 114 112 js_set_filename(js, use_path); 115 113 js_setup_import_meta(js, use_path); 116 - 117 114 js_mkscope(js); 118 - js_protect_init_memory(js); 119 115 120 116 jsval_t result = js_eval(js, buffer, len); 121 117 free(buffer); ··· 137 133 struct arg_lit *no_color = arg_lit0(NULL, "no-color", "disable colored output"); 138 134 struct arg_str *eval = arg_str0("e", "eval", "<script>", "evaluate script"); 139 135 struct arg_lit *print = arg_lit0("p", "print", "evaluate script and print result"); 140 - struct arg_int *gct = arg_int0(NULL, "gct", "<threshold>", "set garbage collection threshold"); 141 136 struct arg_int *initial_mem = arg_int0(NULL, "initial-mem", "<size>", "initial memory size in MB (default: 4)"); 142 137 struct arg_int *max_mem = arg_int0(NULL, "max-mem", "<size>", "maximum memory size in MB (default: 512)"); 143 138 struct arg_file *localstorage_file = arg_file0(NULL, "localstorage-file", "<path>", "file path for localStorage persistence"); ··· 146 141 147 142 void *argtable[] = { 148 143 help, version, debug, no_color, 149 - eval, print, gct, initial_mem, 150 - max_mem, localstorage_file, file, end 144 + eval, print, initial_mem, max_mem, 145 + localstorage_file, file, end 151 146 }; 152 147 153 148 int nerrors = arg_parse(argc, argv, argtable); ··· 194 189 return EXIT_FAILURE; 195 190 } 196 191 197 - if (gct->count > 0) js_setgct(js, gct->ival[0]); 198 192 ant_runtime_init(js, argc, argv, localstorage_file); 199 193 200 194 init_symbol_module();
+26 -15
src/modules/builtin.c
··· 4 4 #include <string.h> 5 5 6 6 #include "ant.h" 7 + #include "arena.h" 7 8 #include "runtime.h" 8 9 #include "modules/builtin.h" 9 10 ··· 64 65 // Ant.gc() 65 66 static jsval_t js_gc_trigger(struct js *js, jsval_t *args, int nargs) { 66 67 (void) args; (void) nargs; 67 - size_t before_brk = js_getbrk(js); 68 68 69 - uint32_t freed = js_gc(js); 70 - size_t after_brk = js_getbrk(js); 69 + size_t heap_before = GC_get_heap_size(); 70 + size_t used_before = GC_get_heap_size() - GC_get_free_bytes(); 71 + 72 + ANT_GC_COLLECT(); 73 + 74 + size_t heap_after = GC_get_heap_size(); 75 + size_t used_after = GC_get_heap_size() - GC_get_free_bytes(); 76 + size_t freed = (used_before > used_after) ? (used_before - used_after) : 0; 71 77 72 78 jsval_t result = js_mkobj(js); 73 - js_set(js, result, "before", js_mknum((double)before_brk)); 74 - js_set(js, result, "after", js_mknum((double)after_brk)); 79 + js_set(js, result, "heapBefore", js_mknum((double)heap_before)); 80 + js_set(js, result, "heapAfter", js_mknum((double)heap_after)); 81 + js_set(js, result, "usedBefore", js_mknum((double)used_before)); 82 + js_set(js, result, "usedAfter", js_mknum((double)used_after)); 75 83 js_set(js, result, "freed", js_mknum((double)freed)); 76 84 77 85 return result; ··· 80 88 // Ant.alloc() 81 89 static jsval_t js_alloc(struct js *js, jsval_t *args, int nargs) { 82 90 (void) args; (void) nargs; 83 - 84 - size_t total = 0, min_free = 0, cstack = 0; 85 - js_stats(js, &total, &min_free, &cstack); 86 91 87 92 jsval_t result = js_mkobj(js); 88 - js_set(js, result, "used", js_mknum((double)total)); 89 - js_set(js, result, "minFree", js_mknum((double)min_free)); 93 + js_set(js, result, "heapSize", js_mknum((double)GC_get_heap_size())); 94 + js_set(js, result, "freeBytes", js_mknum((double)GC_get_free_bytes())); 95 + js_set(js, result, "usedBytes", js_mknum((double)(GC_get_heap_size() - GC_get_free_bytes()))); 96 + js_set(js, result, "totalBytes", js_mknum((double)GC_get_total_bytes())); 90 97 91 98 return result; 92 99 } ··· 102 109 static jsval_t js_stats_fn(struct js *js, jsval_t *args, int nargs) { 103 110 (void) args; (void) nargs; 104 111 105 - size_t total = 0, min_free = 0, cstack = 0; 106 - js_stats(js, &total, &min_free, &cstack); 107 - 112 + size_t arena_total = 0, arena_lwm = 0, cstack = 0; 113 + js_stats(js, &arena_total, &arena_lwm, &cstack); 108 114 jsval_t result = js_mkobj(js); 109 - js_set(js, result, "used", js_mknum((double)total)); 110 - js_set(js, result, "minFree", js_mknum((double)min_free)); 115 + 116 + js_set(js, result, "arenaUsed", js_mknum((double)arena_total)); 117 + js_set(js, result, "arenaLwm", js_mknum((double)arena_lwm)); 111 118 js_set(js, result, "cstack", js_mknum((double)cstack)); 119 + 120 + js_set(js, result, "gcHeapSize", js_mknum((double)GC_get_heap_size())); 121 + js_set(js, result, "gcFreeBytes", js_mknum((double)GC_get_free_bytes())); 122 + js_set(js, result, "gcUsedBytes", js_mknum((double)(GC_get_heap_size() - GC_get_free_bytes()))); 112 123 113 124 return result; 114 125 }
-3
src/repl.c
··· 195 195 196 196 js_set_filename(js, "[repl]"); 197 197 js_setup_import_meta(js, "[repl]"); 198 - 199 198 js_mkscope(js); 200 - js_protect_init_memory(js); 201 199 202 200 printf("Welcome to Ant JavaScript v%s\n", ANT_VERSION); 203 201 printf("Type \".help\" for more information.\n"); ··· 310 308 } 311 309 } 312 310 } else if (strcmp(line, ".gc") == 0) { 313 - js_gc(js); 314 311 printf("Garbage collection complete\n"); 315 312 } else if (strcmp(line, ".stats") == 0) { 316 313 size_t total, min, cstack;
+25 -18
tests/test_gc.js
··· 1 - console.log('=== Testing Ant.alloc() ==='); 1 + console.log('=== Testing Ant.alloc() (bdwgc) ==='); 2 2 let alloc1 = Ant.alloc(); 3 3 console.log('Initial allocation:'); 4 - console.log(' used:', alloc1.used); 5 - console.log(' minFree:', alloc1.minFree); 4 + console.log(' heapSize:', alloc1.heapSize); 5 + console.log(' usedBytes:', alloc1.usedBytes); 6 + console.log(' freeBytes:', alloc1.freeBytes); 6 7 7 8 console.log('\n=== Creating objects to allocate memory ==='); 8 9 let arr = []; ··· 13 14 14 15 let alloc2 = Ant.alloc(); 15 16 console.log('After allocation:'); 16 - console.log(' used:', alloc2.used); 17 - console.log(' minFree:', alloc2.minFree); 18 - console.log(' increase:', alloc2.used - alloc1.used); 17 + console.log(' heapSize:', alloc2.heapSize); 18 + console.log(' usedBytes:', alloc2.usedBytes); 19 + console.log(' increase:', alloc2.usedBytes - alloc1.usedBytes); 19 20 20 21 console.log('\n=== Testing Ant.stats() ==='); 21 22 let stats1 = Ant.stats(); 22 23 console.log('Memory stats:'); 23 - console.log(' used:', stats1.used); 24 - console.log(' minFree:', stats1.minFree); 24 + console.log(' arenaUsed:', stats1.arenaUsed); 25 + console.log(' arenaLwm:', stats1.arenaLwm); 25 26 console.log(' cstack:', stats1.cstack); 27 + console.log(' gcHeapSize:', stats1.gcHeapSize); 28 + console.log(' gcUsedBytes:', stats1.gcUsedBytes); 29 + console.log(' gcFreeBytes:', stats1.gcFreeBytes); 26 30 27 31 console.log('\n=== Testing Ant.gc() ==='); 28 32 arr = null; 29 33 30 34 let gcResult = Ant.gc(); 31 35 console.log('GC result:'); 32 - console.log(' before break:', gcResult.before); 33 - console.log(' after break:', gcResult.after); 34 - console.log(' freed bytes:', gcResult.freed); 36 + console.log(' heapBefore:', gcResult.heapBefore); 37 + console.log(' heapAfter:', gcResult.heapAfter); 38 + console.log(' usedBefore:', gcResult.usedBefore); 39 + console.log(' usedAfter:', gcResult.usedAfter); 40 + console.log(' freed:', gcResult.freed); 35 41 36 42 console.log('\n=== Verifying memory after GC ==='); 37 43 let alloc3 = Ant.alloc(); 38 44 console.log('After GC:'); 39 - console.log(' used:', alloc3.used); 40 - console.log(' minFree:', alloc3.minFree); 45 + console.log(' heapSize:', alloc3.heapSize); 46 + console.log(' usedBytes:', alloc3.usedBytes); 47 + console.log(' freeBytes:', alloc3.freeBytes); 41 48 42 49 console.log('\n=== Testing multiple GC cycles ==='); 43 50 for (let cycle = 0; cycle < 3; cycle = cycle + 1) { ··· 48 55 temp.push({ data: 'test data ' + i }); 49 56 } 50 57 let beforeGc = Ant.alloc(); 51 - console.log(' Before GC - used:', beforeGc.used); 58 + console.log(' Before GC - usedBytes:', beforeGc.usedBytes); 52 59 53 60 temp = null; 54 61 let gc = Ant.gc(); 55 - console.log(' After GC - used:', gc.after, 'freed bytes:', gc.freed); 62 + console.log(' After GC - usedAfter:', gc.usedAfter, 'freed:', gc.freed); 56 63 } 57 64 58 65 console.log('\n=== Testing stats consistency ==='); 59 66 let statsA = Ant.stats(); 60 67 let allocA = Ant.alloc(); 61 68 console.log('Stats and alloc should match:'); 62 - console.log(' stats.used:', statsA.used); 63 - console.log(' alloc.used:', allocA.used); 64 - console.log(' match:', statsA.used === allocA.used); 69 + console.log(' stats.gcUsedBytes:', statsA.gcUsedBytes); 70 + console.log(' alloc.usedBytes:', allocA.usedBytes); 71 + console.log(' match:', statsA.gcUsedBytes === allocA.usedBytes); 65 72 66 73 console.log('\n=== Test complete ===');