Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678#include <stdlib.h>#include <string.h>#include <stdio.h>#include <math.h>
#include "ant.h"#include "gc.h"#include "errors.h"#include "runtime.h"#include "internal.h"#include "silver/engine.h"#include "descriptors.h"
#include "modules/bigint.h"#include "modules/collections.h"#include "modules/symbol.h"
ant_value_t g_map_iter_proto = 0;ant_value_t g_set_iter_proto = 0;
typedef struct { unsigned char stack[32]; unsigned char *bytes; size_t len;} collection_key_t;
static ant_value_t normalize_map_key(ant_value_t key) { if (vtype(key) == T_NUM) { double d = tod(key); if (d == 0.0 && signbit(d)) return js_mknum(0.0); } return key;}
static void collection_key_reset(collection_key_t *key) { key->bytes = key->stack; key->len = 0;}
static void collection_key_free(collection_key_t *key) { if (key->bytes != key->stack) free(key->bytes); collection_key_reset(key);}
static bool collection_key_reserve(collection_key_t *key, size_t len) { if (len <= sizeof(key->stack)) return true; unsigned char *heap = malloc(len); if (!heap) return false; key->bytes = heap; return true;}
static bool collection_key_init(ant_t *js, ant_value_t input, collection_key_t *out) { collection_key_reset(out);
ant_value_t key = normalize_map_key(input); uint8_t tag = (uint8_t)vtype(key);
if (vtype(key) == T_STR) { size_t str_len = 0; const char *str = js_getstr(js, key, &str_len); out->len = 1 + str_len; if (!collection_key_reserve(out, out->len)) return false; out->bytes[0] = tag; if (str_len > 0) memcpy(out->bytes + 1, str, str_len); return true; }
if (vtype(key) == T_BIGINT) { size_t str_len = bigint_digits_len(js, key) + (bigint_is_negative(js, key) ? 1 : 0); out->len = 1 + str_len; if (!collection_key_reserve(out, out->len)) return false; out->bytes[0] = tag; if (str_len > 0) strbigint(js, key, (char *)(out->bytes + 1), str_len + 1); return true; }
out->len = 1 + sizeof(ant_value_t); if (!collection_key_reserve(out, out->len)) return false; out->bytes[0] = tag; memcpy(out->bytes + 1, &key, sizeof(ant_value_t)); return true;}
static map_entry_t *map_find_entry(ant_t *js, map_entry_t **map_ptr, ant_value_t key_val) { collection_key_t key; if (!collection_key_init(js, key_val, &key)) return NULL; map_entry_t *entry = NULL; HASH_FIND(hh, *map_ptr, key.bytes, key.len, entry); collection_key_free(&key); return entry;}
static set_entry_t *set_find_entry(ant_t *js, set_entry_t **set_ptr, ant_value_t value) { collection_key_t key; if (!collection_key_init(js, value, &key)) return NULL; set_entry_t *entry = NULL; HASH_FIND(hh, *set_ptr, key.bytes, key.len, entry); collection_key_free(&key); return entry;}
static bool map_store_entry( ant_t *js, map_entry_t **map_ptr, ant_value_t raw_key, ant_value_t key_val, ant_value_t value) { collection_key_t key; if (!collection_key_init(js, raw_key, &key)) return false;
map_entry_t *entry = NULL; HASH_FIND(hh, *map_ptr, key.bytes, key.len, entry); if (entry) { entry->key_val = key_val; entry->value = value; collection_key_free(&key); return true; }
entry = ant_calloc(sizeof(map_entry_t)); if (!entry) { collection_key_free(&key); return false; }
entry->key = malloc(key.len); if (!entry->key) { collection_key_free(&key); free(entry); return false; }
memcpy(entry->key, key.bytes, key.len); entry->key_len = key.len; entry->key_val = key_val; entry->value = value; HASH_ADD_KEYPTR(hh, *map_ptr, entry->key, entry->key_len, entry); collection_key_free(&key); return true;}
static bool set_store_entry(ant_t *js, set_entry_t **set_ptr, ant_value_t value) { collection_key_t key; if (!collection_key_init(js, value, &key)) return false;
set_entry_t *entry = NULL; HASH_FIND(hh, *set_ptr, key.bytes, key.len, entry); if (entry) { collection_key_free(&key); return true; }
entry = ant_calloc(sizeof(set_entry_t)); if (!entry) { collection_key_free(&key); return false; }
entry->key = malloc(key.len); if (!entry->key) { collection_key_free(&key); free(entry); return false; }
memcpy(entry->key, key.bytes, key.len); entry->key_len = key.len; entry->value = value; HASH_ADD_KEYPTR(hh, *set_ptr, entry->key, entry->key_len, entry); collection_key_free(&key); return true;}
static ant_value_t map_init_from_iterable(ant_t *js, map_entry_t **map_head, ant_value_t iterable) { js_iter_t it; if (!js_iter_open(js, iterable, &it)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Map constructor argument is not iterable"); }
ant_value_t entry; while (js_iter_next(js, &it, &entry)) { uint8_t entry_t = vtype(entry); if (entry_t != T_ARR && entry_t != T_OBJ) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "Map iterable entries must be pair sequences"); }
ant_offset_t entry_len = js_arr_len(js, entry); if (entry_len < 2) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "Map iterable entries must have at least 2 items"); }
ant_value_t key = normalize_map_key(js_arr_get(js, entry, 0)); ant_value_t value = js_arr_get(js, entry, 1); if (!map_store_entry(js, map_head, key, key, value)) { js_iter_close(js, &it); return js_mkerr(js, "out of memory"); }}
return js_mkundef();}
static ant_value_t set_init_from_iterable(ant_t *js, set_entry_t **set_head, ant_value_t iterable) { js_iter_t it; if (!js_iter_open(js, iterable, &it)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Set constructor argument is not iterable"); }
ant_value_t value; while (js_iter_next(js, &it, &value)) if (!set_store_entry(js, set_head, value)) { js_iter_close(js, &it); return js_mkerr(js, "out of memory"); }
return js_mkundef();}
static ant_value_t weakmap_init_from_iterable(ant_t *js, weakmap_entry_t **wm_head, ant_value_t iterable) { js_iter_t it; if (!js_iter_open(js, iterable, &it)) { return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap constructor argument is not iterable"); }
ant_value_t entry; while (js_iter_next(js, &it, &entry)) { uint8_t entry_t = vtype(entry); if (entry_t != T_ARR && entry_t != T_OBJ) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap iterable entries must be pair sequences"); } ant_offset_t entry_len = js_arr_len(js, entry); if (entry_len < 2) { js_iter_close(js, &it); return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap iterable entries must have at least 2 items"); } ant_value_t key = js_arr_get(js, entry, 0); ant_value_t value = js_arr_get(js, entry, 1); if (!is_object_type(key)) { js_iter_close(js, &it); return js_mkerr(js, "WeakMap key must be an object"); } weakmap_entry_t *wm_entry; HASH_FIND(hh, *wm_head, &key, sizeof(ant_value_t), wm_entry); if (wm_entry) { wm_entry->value = value; continue; } wm_entry = ant_calloc(sizeof(weakmap_entry_t)); if (!wm_entry) { js_iter_close(js, &it); return js_mkerr(js, "out of memory"); } wm_entry->key_obj = key; wm_entry->value = value; HASH_ADD(hh, *wm_head, key_obj, sizeof(ant_value_t), wm_entry); }
return js_mkundef();}
static ant_value_t weakset_init_from_iterable(ant_t *js, weakset_entry_t **ws_head, ant_value_t iterable) { js_iter_t it; if (!js_iter_open(js, iterable, &it)) { return js_mkerr_typed(js, JS_ERR_TYPE, "WeakSet constructor argument is not iterable"); }
ant_value_t value; while (js_iter_next(js, &it, &value)) { if (!is_object_type(value)) { js_iter_close(js, &it); return js_mkerr(js, "WeakSet value must be an object"); } weakset_entry_t *entry; HASH_FIND(hh, *ws_head, &value, sizeof(ant_value_t), entry); if (entry) continue; entry = ant_calloc(sizeof(weakset_entry_t)); if (!entry) { js_iter_close(js, &it); return js_mkerr(js, "out of memory"); } entry->value_obj = value; HASH_ADD(hh, *ws_head, value_obj, sizeof(ant_value_t), entry); }
return js_mkundef();}
map_entry_t **get_map_from_obj(ant_value_t obj) { ant_object_t *ptr = js_obj_ptr(obj); if (!ptr || ptr->type_tag != T_MAP) return NULL; return (map_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);}
set_entry_t **get_set_from_obj(ant_value_t obj) { ant_object_t *ptr = js_obj_ptr(obj); if (!ptr || ptr->type_tag != T_SET) return NULL; return (set_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);}
static weakmap_entry_t **get_weakmap_from_obj(ant_value_t obj) { ant_object_t *ptr = js_obj_ptr(obj); if (!ptr || ptr->type_tag != T_WEAKMAP) return NULL; return (weakmap_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);}
static weakset_entry_t **get_weakset_from_obj(ant_value_t obj) { ant_object_t *ptr = js_obj_ptr(obj); if (!ptr || ptr->type_tag != T_WEAKSET) return NULL; return (weakset_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);}
static map_iterator_state_t *get_map_iter_state(ant_value_t obj) { ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return NULL; return (map_iterator_state_t *)(uintptr_t)js_getnum(state_val);}
static set_iterator_state_t *get_set_iter_state(ant_value_t obj) { ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return NULL; return (set_iterator_state_t *)(uintptr_t)js_getnum(state_val);}
static ant_value_t map_set(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "Map.set() requires 2 arguments"); ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_mkerr(js, "Invalid Map object"); ant_value_t key_val = normalize_map_key(args[0]); if (!map_store_entry(js, map_ptr, args[0], key_val, args[1])) return js_mkerr(js, "out of memory");
ant_object_t *map_obj = js_obj_ptr(this_val); if (map_obj) { gc_write_barrier(js, map_obj, key_val); gc_write_barrier(js, map_obj, args[1]); }
return this_val;}
static ant_value_t map_get(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Map.get() requires 1 argument"); ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_mkundef();
map_entry_t *entry = map_find_entry(js, map_ptr, args[0]); return entry ? entry->value : js_mkundef();}
static ant_value_t map_has(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Map.has() requires 1 argument"); ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_false; map_entry_t *entry = map_find_entry(js, map_ptr, args[0]); return js_bool(entry != NULL);}
static ant_value_t map_upsert(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 3) return js_mkerr(js, "Map.upsert() requires 3 arguments");
ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_mkerr(js, "Invalid Map object");
ant_value_t update_fn = args[1]; ant_value_t insert_fn = args[2]; if (!is_callable(update_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "Map.upsert update callback must be callable"); if (!is_callable(insert_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "Map.upsert insert callback must be callable");
map_entry_t *entry = map_find_entry(js, map_ptr, args[0]); ant_value_t value;
if (entry) { ant_value_t call_args[3] = { entry->value, args[0], this_val }; value = sv_vm_call(js->vm, js, update_fn, js_mkundef(), call_args, 3, NULL, false); } else { ant_value_t call_args[2] = { args[0], this_val }; value = sv_vm_call(js->vm, js, insert_fn, js_mkundef(), call_args, 2, NULL, false); }
if (is_err(value)) return value;
ant_value_t key_val = normalize_map_key(args[0]); if (!map_store_entry(js, map_ptr, args[0], key_val, value)) return js_mkerr(js, "out of memory");
ant_object_t *map_obj = js_obj_ptr(this_val); if (map_obj) { gc_write_barrier(js, map_obj, key_val); gc_write_barrier(js, map_obj, value); }
return value;}
static ant_value_t map_delete(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Map.delete() requires 1 argument"); ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_false; map_entry_t *entry = map_find_entry(js, map_ptr, args[0]); if (entry) { HASH_DEL(*map_ptr, entry); free(entry->key); free(entry); return js_true; } return js_false;}
static ant_value_t map_clear(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_mkundef(); map_entry_t *entry, *tmp; HASH_ITER(hh, *map_ptr, entry, tmp) { HASH_DEL(*map_ptr, entry); free(entry->key); free(entry); } *map_ptr = NULL; return js_mkundef();}
static ant_value_t map_size(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (!map_ptr) return js_mknum(0); return js_mknum((double)HASH_COUNT(*map_ptr));}
static ant_value_t map_forEach(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; map_entry_t **map_ptr = get_map_from_obj(this_val); if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_mkerr(js, "forEach requires a callback function"); ant_value_t callback = args[0]; if (map_ptr && *map_ptr) { map_entry_t *entry, *tmp; HASH_ITER(hh, *map_ptr, entry, tmp) { ant_value_t k = entry->key_val; ant_value_t call_args[3] = { entry->value, k, this_val }; ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false); if (is_err(result)) return result; }} return js_mkundef();}
bool advance_map(ant_t *js, js_iter_t *it, ant_value_t *out) { map_iterator_state_t *state = get_map_iter_state(it->iterator); if (!state || !state->current) return false;
map_entry_t *entry = state->current; switch (state->type) { case ITER_TYPE_MAP_VALUES: *out = entry->value; break; case ITER_TYPE_MAP_KEYS: *out = entry->key_val; break; case ITER_TYPE_MAP_ENTRIES: { ant_value_t pair = js_mkarr(js); js_arr_push(js, pair, entry->key_val); js_arr_push(js, pair, entry->value); *out = pair; break; } default: *out = js_mkundef(); } state->current = entry->hh.next; return true;}
static ant_value_t map_iter_next(ant_t *js, ant_value_t *args, int nargs) { js_iter_t it = { .iterator = js->this_val }; ant_value_t value; return js_iter_result(js, advance_map(js, &it, &value), value);}
static ant_value_t create_map_iterator(ant_t *js, ant_value_t map_obj, iter_type_t type) { map_entry_t **map_ptr = get_map_from_obj(map_obj); map_iterator_state_t *state = ant_calloc(sizeof(map_iterator_state_t)); if (!state) return js_mkerr(js, "out of memory"); state->head = map_ptr; state->current = map_ptr ? *map_ptr : NULL; state->type = type; ant_value_t iter = js_mkobj(js); js_set_proto_init(iter, g_map_iter_proto); js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); return iter;}
static ant_value_t map_values(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_VALUES);}
static ant_value_t map_keys(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_KEYS);}
static ant_value_t map_entries(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_ENTRIES);}
bool advance_set(ant_t *js, js_iter_t *it, ant_value_t *out) { set_iterator_state_t *state = get_set_iter_state(it->iterator); if (!state || !state->current) return false;
set_entry_t *entry = state->current; if (state->type == ITER_TYPE_SET_ENTRIES) { ant_value_t pair = js_mkarr(js); js_arr_push(js, pair, entry->value); js_arr_push(js, pair, entry->value); *out = pair; } else *out = entry->value; state->current = entry->hh.next; return true;}
static ant_value_t set_iter_next(ant_t *js, ant_value_t *args, int nargs) { js_iter_t it = { .iterator = js->this_val }; ant_value_t value; return js_iter_result(js, advance_set(js, &it, &value), value);}
static ant_value_t create_set_iterator(ant_t *js, ant_value_t set_obj, iter_type_t type) { set_entry_t **set_ptr = get_set_from_obj(set_obj); set_iterator_state_t *state = ant_calloc(sizeof(set_iterator_state_t)); if (!state) return js_mkerr(js, "out of memory"); state->head = set_ptr; state->current = set_ptr ? *set_ptr : NULL; state->type = type; ant_value_t iter = js_mkobj(js); js_set_proto_init(iter, g_set_iter_proto); js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); return iter;}
static ant_value_t set_add(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Set.add() requires 1 argument"); ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (!set_ptr) return js_mkerr(js, "Invalid Set object"); if (!set_store_entry(js, set_ptr, args[0])) return js_mkerr(js, "out of memory");
ant_object_t *set_obj = js_obj_ptr(this_val); if (set_obj) gc_write_barrier(js, set_obj, args[0]);
return this_val;}
static ant_value_t set_has(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Set.has() requires 1 argument"); ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (!set_ptr) return js_false;
set_entry_t *entry = set_find_entry(js, set_ptr, args[0]); return js_bool(entry != NULL);}
static ant_value_t set_delete(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Set.delete() requires 1 argument"); ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (!set_ptr) return js_false;
set_entry_t *entry = set_find_entry(js, set_ptr, args[0]); if (entry) { HASH_DEL(*set_ptr, entry); free(entry->key); free(entry); return js_true; } return js_false;}
static ant_value_t set_clear(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (!set_ptr) return js_mkundef(); set_entry_t *entry, *tmp; HASH_ITER(hh, *set_ptr, entry, tmp) { HASH_DEL(*set_ptr, entry); free(entry->key); free(entry); } *set_ptr = NULL; return js_mkundef();}
static ant_value_t set_size(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (!set_ptr) return js_mknum(0); return js_mknum((double)HASH_COUNT(*set_ptr));}
static ant_value_t set_values(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; return create_set_iterator(js, js->this_val, ITER_TYPE_SET_VALUES);}
static ant_value_t set_entries(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; return create_set_iterator(js, js->this_val, ITER_TYPE_SET_ENTRIES);}
static ant_value_t set_forEach(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(this_val); if (nargs < 1 || vtype(args[0]) != T_FUNC) return js_mkerr(js, "forEach requires a callback function"); ant_value_t callback = args[0]; if (set_ptr && *set_ptr) { set_entry_t *entry, *tmp; HASH_ITER(hh, *set_ptr, entry, tmp) { ant_value_t call_args[3] = { entry->value, entry->value, this_val }; ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false); if (is_err(result)) return result; }} return js_mkundef();}
static ant_value_t make_set_result(ant_t *js, set_entry_t ***out_set) { ant_value_t set_obj = js_mkobj(js); if (is_err(set_obj)) return set_obj; js_obj_ptr(set_obj)->type_tag = T_SET;
ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3); if (is_special_object(set_proto)) js_set_proto_init(set_obj, set_proto);
set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *)); if (!set_head) return js_mkerr(js, "out of memory"); *set_head = NULL; js_set_slot(set_obj, SLOT_DATA, ANT_PTR(set_head)); if (out_set) *out_set = set_head; return set_obj;}
static bool set_result_add(ant_t *js, ant_value_t set_obj, set_entry_t **set_ptr, ant_value_t value) { if (!set_store_entry(js, set_ptr, value)) return false; ant_object_t *obj = js_obj_ptr(set_obj); if (obj) gc_write_barrier(js, obj, value); return true;}
static void set_result_delete(ant_t *js, set_entry_t **set_ptr, ant_value_t value) { set_entry_t *entry = set_find_entry(js, set_ptr, value); if (!entry) return; HASH_DEL(*set_ptr, entry); free(entry->key); free(entry);}
typedef struct { ant_value_t obj; ant_value_t has; ant_value_t keys; double size;} set_record_t;
typedef enum { SET_KEY_CONTINUE, SET_KEY_STOP,} set_key_status_t;
typedef set_key_status_t (*set_key_cb)( ant_t *js, ant_value_t value, ant_value_t *result, void *ctx);
static ant_value_t get_set_record(ant_t *js, ant_value_t value, const char *method, set_record_t *out) { if (!is_object_type(value)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a set-like object", method);
ant_value_t size = js_getprop_fallback(js, value, "size"); if (is_err(size)) return size; if (vtype(size) == T_BIGINT || vtype(size) == T_SYMBOL) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a numeric size", method); double num_size = js_to_number(js, size); if (isnan(num_size)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a numeric size", method); double int_size = (num_size == 0.0 || !isfinite(num_size)) ? num_size : (num_size < 0 ? -floor(-num_size) : floor(num_size)); if (int_size < 0) return js_mkerr_typed(js, JS_ERR_RANGE, "Set.%s() requires a non-negative size", method);
ant_value_t has = js_getprop_fallback(js, value, "has"); if (is_err(has)) return has; if (!is_callable(has)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a callable has method", method);
ant_value_t keys = js_getprop_fallback(js, value, "keys"); if (is_err(keys)) return keys; if (!is_callable(keys)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a callable keys method", method);
out->obj = value; out->has = has; out->keys = keys; out->size = int_size; return js_mkundef();}
static ant_value_t set_record_has(ant_t *js, set_record_t *record, ant_value_t value, bool *out) { ant_value_t result = sv_vm_call(js->vm, js, record->has, record->obj, &value, 1, NULL, false); if (is_err(result)) return result; *out = js_truthy(js, result); return js_mkundef();}
static ant_value_t set_record_close_keys_iterator(ant_t *js, ant_value_t iterator) { ant_value_t return_fn = js_getprop_fallback(js, iterator, "return"); if (is_err(return_fn)) return return_fn; if (!is_callable(return_fn)) return js_mkundef(); return sv_vm_call(js->vm, js, return_fn, iterator, NULL, 0, NULL, false);}
static ant_value_t set_record_for_each_key(ant_t *js, set_record_t *record, set_key_cb cb, void *ctx) { ant_value_t iterator = sv_vm_call(js->vm, js, record->keys, record->obj, NULL, 0, NULL, false); if (is_err(iterator)) return iterator; if (!is_object_type(iterator)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() result is not an iterator");
ant_value_t next_fn = js_getprop_fallback(js, iterator, "next"); if (is_err(next_fn)) return next_fn; if (!is_callable(next_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() iterator has no callable next method");
while (true) { ant_value_t next = sv_vm_call(js->vm, js, next_fn, iterator, NULL, 0, NULL, false); if (is_err(next)) return next; if (!is_object_type(next)) return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() iterator result is not an object"); ant_value_t done = js_getprop_fallback(js, next, "done"); if (is_err(done)) return done; if (js_truthy(js, done)) return js_mkundef(); ant_value_t value = js_getprop_fallback(js, next, "value"); if (is_err(value)) return value; ant_value_t result = js_mkundef(); set_key_status_t status = cb(js, value, &result, ctx); if (is_err(result)) { ant_value_t close_result = set_record_close_keys_iterator(js, iterator); return is_err(close_result) ? close_result : result; } if (status == SET_KEY_STOP) { ant_value_t close_result = set_record_close_keys_iterator(js, iterator); return is_err(close_result) ? close_result : js_mkundef(); } }}
typedef struct { ant_value_t out; set_entry_t **out_set;} set_build_ctx_t;
static set_key_status_t set_add_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_build_ctx_t *build = (set_build_ctx_t *)ctx; if (!set_result_add(js, build->out, build->out_set, value)) *result = js_mkerr(js, "out of memory"); return SET_KEY_CONTINUE;}
static ant_value_t set_union(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.union() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "union", &other); if (is_err(rec)) return rec;
set_entry_t **out_set = NULL; ant_value_t out = make_set_result(js, &out_set); if (is_err(out)) return out; set_build_ctx_t build = { out, out_set };
set_entry_t *entry, *tmp; HASH_ITER(hh, *this_set, entry, tmp) if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory"); ant_value_t result = set_record_for_each_key(js, &other, set_add_key_cb, &build); return is_err(result) ? result : out;}
typedef struct { ant_value_t out; set_entry_t **out_set; set_entry_t **this_set;} set_compare_build_ctx_t;
static set_key_status_t set_intersection_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_compare_build_ctx_t *build = (set_compare_build_ctx_t *)ctx; if ( set_find_entry(js, build->this_set, value) && !set_result_add(js, build->out, build->out_set, value) ) *result = js_mkerr(js, "out of memory"); return SET_KEY_CONTINUE;}
static ant_value_t set_intersection(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.intersection() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "intersection", &other); if (is_err(rec)) return rec;
set_entry_t **out_set = NULL; ant_value_t out = make_set_result(js, &out_set); if (is_err(out)) return out;
double this_size = (double)HASH_COUNT(*this_set); if (this_size <= other.size) { set_entry_t *entry, *tmp; HASH_ITER(hh, *this_set, entry, tmp) { bool has = false; ant_value_t result = set_record_has(js, &other, entry->value, &has); if (is_err(result)) return result; if (has && !set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory"); } return out; }
set_compare_build_ctx_t build = { out, out_set, this_set }; ant_value_t result = set_record_for_each_key(js, &other, set_intersection_key_cb, &build); return is_err(result) ? result : out;}
typedef struct { set_record_t *other; ant_value_t out; set_entry_t **out_set;} set_difference_ctx_t;
static ant_value_t set_difference_key_cb(ant_t *js, ant_value_t value, void *ctx) { set_difference_ctx_t *build = (set_difference_ctx_t *)ctx; bool has = false; ant_value_t result = set_record_has(js, build->other, value, &has); if (is_err(result)) return result; if (!has && !set_result_add(js, build->out, build->out_set, value)) return js_mkerr(js, "out of memory"); return js_mkundef();}
static set_key_status_t set_delete_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_build_ctx_t *build = (set_build_ctx_t *)ctx; set_result_delete(js, build->out_set, value); return SET_KEY_CONTINUE;}
static ant_value_t set_difference(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.difference() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "difference", &other); if (is_err(rec)) return rec;
set_entry_t **out_set = NULL; ant_value_t out = make_set_result(js, &out_set); if (is_err(out)) return out;
set_entry_t *entry, *tmp; if ((double)HASH_COUNT(*this_set) <= other.size) { set_difference_ctx_t diff = { &other, out, out_set }; HASH_ITER(hh, *this_set, entry, tmp) { ant_value_t result = set_difference_key_cb(js, entry->value, &diff); if (is_err(result)) return result; } return out; }
HASH_ITER(hh, *this_set, entry, tmp) { if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory"); }
set_build_ctx_t build = { out, out_set }; ant_value_t result = set_record_for_each_key(js, &other, set_delete_key_cb, &build); return is_err(result) ? result : out;}
typedef struct { ant_value_t out; set_entry_t **out_set; set_entry_t **this_set;} set_symdiff_ctx_t;
static set_key_status_t set_symmetric_difference_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_symdiff_ctx_t *build = (set_symdiff_ctx_t *)ctx; if (set_find_entry(js, build->this_set, value)) { set_result_delete(js, build->out_set, value); } else if (!set_find_entry(js, build->out_set, value)) if (!set_result_add(js, build->out, build->out_set, value)) *result = js_mkerr(js, "out of memory"); return SET_KEY_CONTINUE;}
static ant_value_t set_symmetricDifference(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.symmetricDifference() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "symmetricDifference", &other); if (is_err(rec)) return rec;
set_entry_t **out_set = NULL; ant_value_t out = make_set_result(js, &out_set); if (is_err(out)) return out;
set_entry_t *entry, *tmp; HASH_ITER(hh, *this_set, entry, tmp) { if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory"); } set_symdiff_ctx_t build = { out, out_set, this_set }; ant_value_t result = set_record_for_each_key(js, &other, set_symmetric_difference_key_cb, &build); return is_err(result) ? result : out;}
static ant_value_t set_isSubsetOf(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isSubsetOf() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "isSubsetOf", &other); if (is_err(rec)) return rec; if ((double)HASH_COUNT(*this_set) > other.size) return js_false;
set_entry_t *entry, *tmp; HASH_ITER(hh, *this_set, entry, tmp) { bool has = false; ant_value_t result = set_record_has(js, &other, entry->value, &has); if (is_err(result)) return result; if (!has) return js_false; } return js_true;}
typedef struct { set_entry_t **this_set; bool result;} set_predicate_ctx_t;
static set_key_status_t set_superset_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_predicate_ctx_t *pred = (set_predicate_ctx_t *)ctx; if (!set_find_entry(js, pred->this_set, value)) { pred->result = false; return SET_KEY_STOP; } return SET_KEY_CONTINUE;}
static ant_value_t set_isSupersetOf(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isSupersetOf() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "isSupersetOf", &other); if (is_err(rec)) return rec; if ((double)HASH_COUNT(*this_set) < other.size) return js_false;
set_predicate_ctx_t pred = { this_set, true }; ant_value_t result = set_record_for_each_key(js, &other, set_superset_key_cb, &pred); if (is_err(result)) return result; return js_bool(pred.result);}
static set_key_status_t set_disjoint_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) { set_predicate_ctx_t *pred = (set_predicate_ctx_t *)ctx; if (set_find_entry(js, pred->this_set, value)) { pred->result = false; return SET_KEY_STOP; } return SET_KEY_CONTINUE;}
static ant_value_t set_isDisjointFrom(ant_t *js, ant_value_t *args, int nargs) { set_entry_t **this_set = get_set_from_obj(js->this_val); if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object"); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isDisjointFrom() requires a set-like object"); set_record_t other; ant_value_t rec = get_set_record(js, args[0], "isDisjointFrom", &other); if (is_err(rec)) return rec;
if ((double)HASH_COUNT(*this_set) <= other.size) { set_entry_t *entry, *tmp; HASH_ITER(hh, *this_set, entry, tmp) { bool has = false; ant_value_t result = set_record_has(js, &other, entry->value, &has); if (is_err(result)) return result; if (has) return js_false; } return js_true; }
set_predicate_ctx_t pred = { this_set, true }; ant_value_t result = set_record_for_each_key(js, &other, set_disjoint_key_cb, &pred); if (is_err(result)) return result; return js_bool(pred.result);}
static ant_value_t weakmap_set(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "WeakMap.set() requires 2 arguments"); ant_value_t this_val = js->this_val; weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val); if (!wm_ptr) return js_mkerr(js, "Invalid WeakMap object"); if (!is_object_type(args[0])) return js_mkerr(js, "WeakMap key must be an object"); ant_value_t key_obj = args[0]; weakmap_entry_t *entry; HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry); if (entry) entry->value = args[1]; else { entry = ant_calloc(sizeof(weakmap_entry_t)); if (!entry) return js_mkerr(js, "out of memory"); entry->key_obj = key_obj; entry->value = args[1]; HASH_ADD(hh, *wm_ptr, key_obj, sizeof(ant_value_t), entry); } ant_object_t *wm_obj = js_obj_ptr(this_val); if (wm_obj) { gc_write_barrier(js, wm_obj, key_obj); gc_write_barrier(js, wm_obj, args[1]); } return this_val;}
static ant_value_t weakmap_get(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakMap.get() requires 1 argument"); ant_value_t this_val = js->this_val; weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val); if (!wm_ptr) return js_mkundef(); if (!is_object_type(args[0])) return js_mkundef(); ant_value_t key_obj = args[0]; weakmap_entry_t *entry; HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry); return entry ? entry->value : js_mkundef();}
static ant_value_t weakmap_has(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakMap.has() requires 1 argument"); ant_value_t this_val = js->this_val; weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val); if (!wm_ptr) return js_false; if (!is_object_type(args[0])) return js_false; ant_value_t key_obj = args[0]; weakmap_entry_t *entry; HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry); return js_bool(entry != NULL);}
static ant_value_t weakmap_upsert(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 3) return js_mkerr(js, "WeakMap.upsert() requires 3 arguments");
ant_value_t this_val = js->this_val; weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val); if (!wm_ptr) return js_mkerr(js, "Invalid WeakMap object");
if (!is_object_type(args[0])) return js_mkerr(js, "WeakMap key must be an object");
ant_value_t update_fn = args[1]; ant_value_t insert_fn = args[2]; if (!is_callable(update_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap.upsert update callback must be callable"); if (!is_callable(insert_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap.upsert insert callback must be callable");
ant_value_t key_obj = args[0]; weakmap_entry_t *entry; HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
ant_value_t value; if (entry) { ant_value_t call_args[3] = { entry->value, key_obj, this_val }; value = sv_vm_call(js->vm, js, update_fn, js_mkundef(), call_args, 3, NULL, false); } else { ant_value_t call_args[2] = { key_obj, this_val }; value = sv_vm_call(js->vm, js, insert_fn, js_mkundef(), call_args, 2, NULL, false); }
if (is_err(value)) return value;
HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry); if (entry) entry->value = value; else { entry = ant_calloc(sizeof(weakmap_entry_t)); if (!entry) return js_mkerr(js, "out of memory"); entry->key_obj = key_obj; entry->value = value; HASH_ADD(hh, *wm_ptr, key_obj, sizeof(ant_value_t), entry); }
ant_object_t *wm_obj = js_obj_ptr(this_val); if (wm_obj) { gc_write_barrier(js, wm_obj, key_obj); gc_write_barrier(js, wm_obj, value); }
return value;}
static ant_value_t weakmap_delete(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakMap.delete() requires 1 argument"); ant_value_t this_val = js->this_val; weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val); if (!wm_ptr) return js_false; if (!is_object_type(args[0])) return js_false; ant_value_t key_obj = args[0]; weakmap_entry_t *entry; HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry); if (entry) { HASH_DEL(*wm_ptr, entry); free(entry); return js_true; } return js_false;}
static ant_value_t weakset_add(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakSet.add() requires 1 argument"); ant_value_t this_val = js->this_val; weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val); if (!ws_ptr) return js_mkerr(js, "Invalid WeakSet object"); if (!is_object_type(args[0])) return js_mkerr(js, "WeakSet value must be an object"); ant_value_t value_obj = args[0]; weakset_entry_t *entry; HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry); if (!entry) { entry = ant_calloc(sizeof(weakset_entry_t)); if (!entry) return js_mkerr(js, "out of memory"); entry->value_obj = value_obj; HASH_ADD(hh, *ws_ptr, value_obj, sizeof(ant_value_t), entry); } return this_val;}
static ant_value_t weakset_has(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakSet.has() requires 1 argument"); ant_value_t this_val = js->this_val; weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val); if (!ws_ptr) return js_false; if (!is_object_type(args[0])) return js_false; ant_value_t value_obj = args[0]; weakset_entry_t *entry; HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry); return js_bool(entry != NULL);}
static ant_value_t weakset_delete(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "WeakSet.delete() requires 1 argument"); ant_value_t this_val = js->this_val; weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val); if (!ws_ptr) return js_false; if (!is_object_type(args[0])) return js_false; ant_value_t value_obj = args[0]; weakset_entry_t *entry; HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry); if (entry) { HASH_DEL(*ws_ptr, entry); free(entry); return js_true; } return js_false;}
static ant_value_t builtin_WeakRef(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || !is_object_type(args[0])) { return js_mkerr(js, "WeakRef target must be an object"); } ant_value_t wr_obj = js_mkobj(js); ant_value_t wr_proto = js_get_ctor_proto(js, "WeakRef", 7); if (is_special_object(wr_proto)) js_set_proto_init(wr_obj, wr_proto); js_set_slot(wr_obj, SLOT_DATA, args[0]); return wr_obj;}
static ant_value_t weakref_deref(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_val = js->this_val; if (vtype(this_val) != T_OBJ) return js_mkundef(); ant_value_t target = js_get_slot(this_val, SLOT_DATA); if (vtype(target) != T_OBJ) return js_mkundef(); return target;}
static ant_value_t builtin_FinalizationRegistry(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) { return js_mkerr(js, "FinalizationRegistry callback must be a function"); } ant_value_t fr_obj = js_mkobj(js); ant_value_t fr_proto = js_get_ctor_proto(js, "FinalizationRegistry", 20); if (is_special_object(fr_proto)) js_set_proto_init(fr_obj, fr_proto); js_set_slot(fr_obj, SLOT_MAP, js_mkarr(js)); js_set_slot(fr_obj, SLOT_DATA, args[0]); return fr_obj;}
static ant_value_t finreg_register(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; if (vtype(this_val) != T_OBJ) return js_mkundef(); if (nargs < 1 || vtype(args[0]) != T_OBJ) { return js_mkerr(js, "FinalizationRegistry.register target must be an object"); } ant_value_t target = args[0]; ant_value_t held_value = nargs > 1 ? args[1] : js_mkundef(); ant_value_t unregister_token = nargs > 2 ? args[2] : js_mkundef(); if (vdata(target) == vdata(held_value) && vtype(held_value) == T_OBJ) { return js_mkerr(js, "target and held value must not be the same"); } ant_value_t registrations = js_get_slot(this_val, SLOT_MAP); if (vtype(registrations) != T_ARR) return js_mkundef(); ant_value_t entry = js_mkarr(js); ant_offset_t len = js_arr_len(js, registrations); char idx[16]; size_t idx_len = uint_to_str(idx, sizeof(idx), 0); js_setprop(js, entry, js_mkstr(js, idx, idx_len), target); idx_len = uint_to_str(idx, sizeof(idx), 1); js_setprop(js, entry, js_mkstr(js, idx, idx_len), held_value); idx_len = uint_to_str(idx, sizeof(idx), 2); js_setprop(js, entry, js_mkstr(js, idx, idx_len), unregister_token); js_setprop(js, entry, js->length_str, tov(3.0)); idx_len = uint_to_str(idx, sizeof(idx), len); js_setprop(js, registrations, js_mkstr(js, idx, idx_len), entry); js_setprop(js, registrations, js->length_str, tov((double)(len + 1))); return js_mkundef();}
static ant_value_t finreg_unregister(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; if (vtype(this_val) != T_OBJ) return js_false; if (nargs < 1 || vtype(args[0]) != T_OBJ) { return js_mkerr(js, "FinalizationRegistry.unregister token must be an object"); } ant_value_t token = args[0]; ant_value_t registrations = js_get_slot(this_val, SLOT_MAP); if (vtype(registrations) != T_ARR) return js_false; ant_offset_t len = js_arr_len(js, registrations); bool removed = false; for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, registrations, i); if (vtype(entry) != T_ARR) continue; ant_value_t entry_token = js_arr_get(js, entry, 2); if (vtype(entry_token) == T_OBJ && vdata(entry_token) == vdata(token)) { char idx[16]; size_t idx_len = uint_to_str(idx, sizeof(idx), i); js_setprop(js, registrations, js_mkstr(js, idx, idx_len), js_mkundef()); removed = true; } } return js_bool(removed);}
static ant_value_t map_groupBy(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Map.groupBy requires 2 arguments"); ant_value_t items = args[0]; ant_value_t callback = args[1]; if (vtype(callback) != T_FUNC && vtype(callback) != T_CFUNC) return js_mkerr_typed(js, JS_ERR_TYPE, "callback is not a function"); ant_value_t map_obj = js_mkobj(js); js_obj_ptr(map_obj)->type_tag = T_MAP; ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3); if (is_special_object(map_proto)) js_set_proto_init(map_obj, map_proto); map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *)); if (!map_head) return js_mkerr(js, "out of memory"); *map_head = NULL; js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head)); ant_offset_t len = js_arr_len(js, items); for (ant_offset_t i = 0; i < len; i++) { ant_value_t val = js_arr_get(js, items, i); ant_value_t cb_args[2] = { val, tov((double)i) }; ant_value_t key = normalize_map_key( sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 2, NULL, false) ); if (is_err(key)) return key; map_entry_t *entry = map_find_entry(js, map_head, key); ant_value_t group;
if (entry) group = entry->value; else { group = js_mkarr(js); if (!map_store_entry(js, map_head, key, key, group)) return js_mkerr(js, "out of memory"); } js_arr_push(js, group, val); } return map_obj;}
static ant_value_t builtin_Map(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) { return js_mkerr_typed(js, JS_ERR_TYPE, "Map constructor requires 'new'"); } ant_value_t map_obj = js->this_val; if (vtype(map_obj) != T_OBJ) map_obj = js_mkobj(js); if (is_err(map_obj)) return map_obj; js_obj_ptr(map_obj)->type_tag = T_MAP; ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3); ant_value_t instance_proto = js_instance_proto_from_new_target(js, map_proto); if (is_special_object(instance_proto)) js_set_proto_init(map_obj, instance_proto); map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *)); if (!map_head) return js_mkerr(js, "out of memory"); *map_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) js_set_slot(map_obj, SLOT_CTOR, js->new_target); js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head)); if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return map_obj; ant_value_t init_result = map_init_from_iterable(js, map_head, args[0]); if (is_err(init_result)) return init_result; return map_obj;}
static ant_value_t builtin_Set(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) { return js_mkerr_typed(js, JS_ERR_TYPE, "Set constructor requires 'new'"); } ant_value_t set_obj = js->this_val; if (vtype(set_obj) != T_OBJ) set_obj = js_mkobj(js); if (is_err(set_obj)) return set_obj; js_obj_ptr(set_obj)->type_tag = T_SET; ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3); ant_value_t instance_proto = js_instance_proto_from_new_target(js, set_proto);
if (is_special_object(instance_proto)) js_set_proto_init(set_obj, instance_proto); set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *)); if (!set_head) return js_mkerr(js, "out of memory"); *set_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) js_set_slot(set_obj, SLOT_CTOR, js->new_target); js_set_slot(set_obj, SLOT_DATA, ANT_PTR(set_head)); if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return set_obj; ant_value_t init_result = set_init_from_iterable(js, set_head, args[0]); if (is_err(init_result)) return init_result; return set_obj;}
static ant_value_t builtin_WeakMap(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) { return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap constructor requires 'new'"); } ant_value_t wm_obj = js->this_val; if (vtype(wm_obj) != T_OBJ) wm_obj = js_mkobj(js); if (is_err(wm_obj)) return wm_obj; js_obj_ptr(wm_obj)->type_tag = T_WEAKMAP; ant_value_t wm_proto = js_get_ctor_proto(js, "WeakMap", 7); ant_value_t instance_proto = js_instance_proto_from_new_target(js, wm_proto);
if (is_special_object(instance_proto)) js_set_proto_init(wm_obj, instance_proto); weakmap_entry_t **wm_head = ant_calloc(sizeof(weakmap_entry_t *)); if (!wm_head) return js_mkerr(js, "out of memory"); *wm_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) js_set_slot(wm_obj, SLOT_CTOR, js->new_target); js_set_slot(wm_obj, SLOT_DATA, ANT_PTR(wm_head)); if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return wm_obj; ant_value_t init_result = weakmap_init_from_iterable(js, wm_head, args[0]); if (is_err(init_result)) return init_result; return wm_obj;}
static ant_value_t builtin_WeakSet(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) { return js_mkerr_typed(js, JS_ERR_TYPE, "WeakSet constructor requires 'new'"); } ant_value_t ws_obj = js->this_val; if (vtype(ws_obj) != T_OBJ) ws_obj = js_mkobj(js); if (is_err(ws_obj)) return ws_obj; js_obj_ptr(ws_obj)->type_tag = T_WEAKSET; ant_value_t ws_proto = js_get_ctor_proto(js, "WeakSet", 7); ant_value_t instance_proto = js_instance_proto_from_new_target(js, ws_proto);
if (is_special_object(instance_proto)) js_set_proto_init(ws_obj, instance_proto); weakset_entry_t **ws_head = ant_calloc(sizeof(weakset_entry_t *)); if (!ws_head) return js_mkerr(js, "out of memory"); *ws_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) js_set_slot(ws_obj, SLOT_CTOR, js->new_target); js_set_slot(ws_obj, SLOT_DATA, ANT_PTR(ws_head)); if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return ws_obj; ant_value_t init_result = weakset_init_from_iterable(js, ws_head, args[0]); if (is_err(init_result)) return init_result; return ws_obj;}
void init_collections_module(void) { ant_t *js = rt->js; ant_value_t glob = js->global; ant_value_t object_proto = js->sym.object_proto; ant_value_t iter_sym = get_iterator_sym(); ant_value_t tag_sym = get_toStringTag_sym(); g_map_iter_proto = js_mkobj(js); js_set_proto_init(g_map_iter_proto, js->sym.iterator_proto); js_set(js, g_map_iter_proto, "next", js_mkfun(map_iter_next)); js_iter_register_advance(g_map_iter_proto, advance_map); g_set_iter_proto = js_mkobj(js); js_set_proto_init(g_set_iter_proto, js->sym.iterator_proto); js_set(js, g_set_iter_proto, "next", js_mkfun(set_iter_next)); js_iter_register_advance(g_set_iter_proto, advance_set); ant_value_t map_proto = js_mkobj(js); js_set_proto_init(map_proto, object_proto); js_set(js, map_proto, "set", js_mkfun(map_set)); js_set(js, map_proto, "get", js_mkfun(map_get)); js_set(js, map_proto, "has", js_mkfun(map_has)); js_set(js, map_proto, "upsert", js_mkfun(map_upsert)); js_set(js, map_proto, "delete", js_mkfun(map_delete)); js_set(js, map_proto, "clear", js_mkfun(map_clear)); js_set_getter_desc(js, map_proto, "size", 4, js_mkfun(map_size), JS_DESC_C); js_set(js, map_proto, "entries", js_mkfun(map_entries)); js_set(js, map_proto, "keys", js_mkfun(map_keys)); js_set(js, map_proto, "values", js_mkfun(map_values)); js_set(js, map_proto, "forEach", js_mkfun(map_forEach)); js_set_sym(js, map_proto, iter_sym, js_get(js, map_proto, "entries")); js_set_sym(js, map_proto, tag_sym, js_mkstr(js, "Map", 3)); ant_value_t map_ctor = js_mkobj(js); js_set_slot(map_ctor, SLOT_CFUNC, js_mkfun(builtin_Map)); js_mkprop_fast(js, map_ctor, "prototype", 9, map_proto); js_mkprop_fast(js, map_ctor, "name", 4, ANT_STRING("Map")); js_set_descriptor(js, map_ctor, "name", 4, 0); js_set(js, map_ctor, "groupBy", js_mkfun(map_groupBy)); js_define_species_getter(js, map_ctor); js_set(js, glob, "Map", js_obj_to_func(map_ctor)); ant_value_t set_proto = js_mkobj(js); js_set_proto_init(set_proto, object_proto); js_set(js, set_proto, "add", js_mkfun(set_add)); js_set(js, set_proto, "has", js_mkfun(set_has)); js_set(js, set_proto, "delete", js_mkfun(set_delete)); js_set(js, set_proto, "clear", js_mkfun(set_clear)); js_set_getter_desc(js, set_proto, "size", 4, js_mkfun(set_size), JS_DESC_C); js_set(js, set_proto, "values", js_mkfun(set_values)); js_set_exact(js, set_proto, "keys", js_get(js, set_proto, "values")); js_set(js, set_proto, "entries", js_mkfun(set_entries)); js_set(js, set_proto, "forEach", js_mkfun(set_forEach)); js_set(js, set_proto, "union", js_mkfun(set_union)); js_set(js, set_proto, "intersection", js_mkfun(set_intersection)); js_set(js, set_proto, "difference", js_mkfun(set_difference)); js_set(js, set_proto, "symmetricDifference", js_mkfun(set_symmetricDifference)); js_set(js, set_proto, "isSubsetOf", js_mkfun(set_isSubsetOf)); js_set(js, set_proto, "isSupersetOf", js_mkfun(set_isSupersetOf)); js_set(js, set_proto, "isDisjointFrom", js_mkfun(set_isDisjointFrom)); js_set_sym(js, set_proto, iter_sym, js_get(js, set_proto, "values")); js_set_sym(js, set_proto, tag_sym, js_mkstr(js, "Set", 3)); ant_value_t set_ctor = js_mkobj(js); js_set_slot(set_ctor, SLOT_CFUNC, js_mkfun(builtin_Set)); js_mkprop_fast(js, set_ctor, "prototype", 9, set_proto); js_mkprop_fast(js, set_ctor, "name", 4, ANT_STRING("Set")); js_set_descriptor(js, set_ctor, "name", 4, 0); js_define_species_getter(js, set_ctor); js_set(js, glob, "Set", js_obj_to_func(set_ctor)); ant_value_t weakmap_proto = js_mkobj(js); js_set_proto_init(weakmap_proto, object_proto); js_set(js, weakmap_proto, "set", js_mkfun(weakmap_set)); js_set(js, weakmap_proto, "get", js_mkfun(weakmap_get)); js_set(js, weakmap_proto, "has", js_mkfun(weakmap_has)); js_set(js, weakmap_proto, "upsert", js_mkfun(weakmap_upsert)); js_set(js, weakmap_proto, "delete", js_mkfun(weakmap_delete)); js_set_sym(js, weakmap_proto, tag_sym, js_mkstr(js, "WeakMap", 7)); ant_value_t weakmap_ctor = js_mkobj(js); js_set_slot(weakmap_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakMap)); js_mkprop_fast(js, weakmap_ctor, "prototype", 9, weakmap_proto); js_mkprop_fast(js, weakmap_ctor, "name", 4, ANT_STRING("WeakMap")); js_set_descriptor(js, weakmap_ctor, "name", 4, 0); js_set(js, glob, "WeakMap", js_obj_to_func(weakmap_ctor)); ant_value_t weakset_proto = js_mkobj(js); js_set_proto_init(weakset_proto, object_proto); js_set(js, weakset_proto, "add", js_mkfun(weakset_add)); js_set(js, weakset_proto, "has", js_mkfun(weakset_has)); js_set(js, weakset_proto, "delete", js_mkfun(weakset_delete)); js_set_sym(js, weakset_proto, tag_sym, js_mkstr(js, "WeakSet", 7)); ant_value_t weakset_ctor = js_mkobj(js); js_set_slot(weakset_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakSet)); js_mkprop_fast(js, weakset_ctor, "prototype", 9, weakset_proto); js_mkprop_fast(js, weakset_ctor, "name", 4, ANT_STRING("WeakSet")); js_set_descriptor(js, weakset_ctor, "name", 4, 0); js_set(js, glob, "WeakSet", js_obj_to_func(weakset_ctor)); ant_value_t weakref_proto = js_mkobj(js); js_set_proto_init(weakref_proto, object_proto); js_set(js, weakref_proto, "deref", js_mkfun(weakref_deref)); js_set_sym(js, weakref_proto, tag_sym, js_mkstr(js, "WeakRef", 7)); ant_value_t weakref_ctor = js_mkobj(js); js_set_slot(weakref_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakRef)); js_mkprop_fast(js, weakref_ctor, "prototype", 9, weakref_proto); js_mkprop_fast(js, weakref_ctor, "name", 4, ANT_STRING("WeakRef")); js_set_descriptor(js, weakref_ctor, "name", 4, 0); js_set(js, glob, "WeakRef", js_obj_to_func(weakref_ctor)); ant_value_t finreg_proto = js_mkobj(js); js_set_proto_init(finreg_proto, object_proto); js_set(js, finreg_proto, "register", js_mkfun(finreg_register)); js_set(js, finreg_proto, "unregister", js_mkfun(finreg_unregister)); js_set_sym(js, finreg_proto, tag_sym, js_mkstr(js, "FinalizationRegistry", 20)); ant_value_t finreg_ctor = js_mkobj(js); js_set_slot(finreg_ctor, SLOT_CFUNC, js_mkfun(builtin_FinalizationRegistry)); js_mkprop_fast(js, finreg_ctor, "prototype", 9, finreg_proto); js_mkprop_fast(js, finreg_ctor, "name", 4, ANT_STRING("FinalizationRegistry")); js_set_descriptor(js, finreg_ctor, "name", 4, 0); js_set(js, glob, "FinalizationRegistry", js_obj_to_func(finreg_ctor));}