Page MenuHomePhorge

collections.c
No OneTemporary

Size
56 KB
Referenced Files
None
Subscribers
None

collections.c

#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));
}

File Metadata

Mime Type
text/x-c
Expires
Sat, May 2, 2:13 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543265
Default Alt Text
collections.c (56 KB)

Event Timeline