MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <string.h>
3#include <stdio.h>
4#include <math.h>
5
6#include "ant.h"
7#include "gc.h"
8#include "errors.h"
9#include "runtime.h"
10#include "internal.h"
11#include "silver/engine.h"
12#include "descriptors.h"
13
14#include "modules/bigint.h"
15#include "modules/collections.h"
16#include "modules/symbol.h"
17
18ant_value_t g_map_iter_proto = 0;
19ant_value_t g_set_iter_proto = 0;
20
21typedef struct {
22 unsigned char stack[32];
23 unsigned char *bytes;
24 size_t len;
25} collection_key_t;
26
27static ant_value_t normalize_map_key(ant_value_t key) {
28 if (vtype(key) == T_NUM) {
29 double d = tod(key);
30 if (d == 0.0 && signbit(d)) return js_mknum(0.0);
31 }
32 return key;
33}
34
35static void collection_key_reset(collection_key_t *key) {
36 key->bytes = key->stack;
37 key->len = 0;
38}
39
40static void collection_key_free(collection_key_t *key) {
41 if (key->bytes != key->stack) free(key->bytes);
42 collection_key_reset(key);
43}
44
45static bool collection_key_reserve(collection_key_t *key, size_t len) {
46 if (len <= sizeof(key->stack)) return true;
47 unsigned char *heap = malloc(len);
48 if (!heap) return false;
49 key->bytes = heap;
50 return true;
51}
52
53static bool collection_key_init(ant_t *js, ant_value_t input, collection_key_t *out) {
54 collection_key_reset(out);
55
56 ant_value_t key = normalize_map_key(input);
57 uint8_t tag = (uint8_t)vtype(key);
58
59 if (vtype(key) == T_STR) {
60 size_t str_len = 0;
61 const char *str = js_getstr(js, key, &str_len);
62 out->len = 1 + str_len;
63 if (!collection_key_reserve(out, out->len)) return false;
64 out->bytes[0] = tag;
65 if (str_len > 0) memcpy(out->bytes + 1, str, str_len);
66 return true;
67 }
68
69 if (vtype(key) == T_BIGINT) {
70 size_t str_len = bigint_digits_len(js, key) + (bigint_is_negative(js, key) ? 1 : 0);
71 out->len = 1 + str_len;
72 if (!collection_key_reserve(out, out->len)) return false;
73 out->bytes[0] = tag;
74 if (str_len > 0) strbigint(js, key, (char *)(out->bytes + 1), str_len + 1);
75 return true;
76 }
77
78 out->len = 1 + sizeof(ant_value_t);
79 if (!collection_key_reserve(out, out->len)) return false;
80 out->bytes[0] = tag;
81 memcpy(out->bytes + 1, &key, sizeof(ant_value_t));
82
83 return true;
84}
85
86static map_entry_t *map_find_entry(ant_t *js, map_entry_t **map_ptr, ant_value_t key_val) {
87 collection_key_t key;
88 if (!collection_key_init(js, key_val, &key)) return NULL;
89
90 map_entry_t *entry = NULL;
91 HASH_FIND(hh, *map_ptr, key.bytes, key.len, entry);
92 collection_key_free(&key);
93
94 return entry;
95}
96
97static set_entry_t *set_find_entry(ant_t *js, set_entry_t **set_ptr, ant_value_t value) {
98 collection_key_t key;
99 if (!collection_key_init(js, value, &key)) return NULL;
100
101 set_entry_t *entry = NULL;
102 HASH_FIND(hh, *set_ptr, key.bytes, key.len, entry);
103 collection_key_free(&key);
104
105 return entry;
106}
107
108static bool map_store_entry(
109 ant_t *js,
110 map_entry_t **map_ptr,
111 ant_value_t raw_key,
112 ant_value_t key_val,
113 ant_value_t value
114) {
115 collection_key_t key;
116 if (!collection_key_init(js, raw_key, &key)) return false;
117
118 map_entry_t *entry = NULL;
119 HASH_FIND(hh, *map_ptr, key.bytes, key.len, entry);
120 if (entry) {
121 entry->key_val = key_val;
122 entry->value = value;
123 collection_key_free(&key);
124 return true;
125 }
126
127 entry = ant_calloc(sizeof(map_entry_t));
128 if (!entry) {
129 collection_key_free(&key);
130 return false;
131 }
132
133 entry->key = malloc(key.len);
134 if (!entry->key) {
135 collection_key_free(&key);
136 free(entry);
137 return false;
138 }
139
140 memcpy(entry->key, key.bytes, key.len);
141 entry->key_len = key.len;
142 entry->key_val = key_val;
143 entry->value = value;
144
145 HASH_ADD_KEYPTR(hh, *map_ptr, entry->key, entry->key_len, entry);
146 collection_key_free(&key);
147
148 return true;
149}
150
151static bool set_store_entry(ant_t *js, set_entry_t **set_ptr, ant_value_t value) {
152 collection_key_t key;
153 if (!collection_key_init(js, value, &key)) return false;
154
155 set_entry_t *entry = NULL;
156 HASH_FIND(hh, *set_ptr, key.bytes, key.len, entry);
157 if (entry) {
158 collection_key_free(&key);
159 return true;
160 }
161
162 entry = ant_calloc(sizeof(set_entry_t));
163 if (!entry) {
164 collection_key_free(&key);
165 return false;
166 }
167
168 entry->key = malloc(key.len);
169 if (!entry->key) {
170 collection_key_free(&key);
171 free(entry);
172 return false;
173 }
174
175 memcpy(entry->key, key.bytes, key.len);
176 entry->key_len = key.len;
177 entry->value = value;
178
179 HASH_ADD_KEYPTR(hh, *set_ptr, entry->key, entry->key_len, entry);
180 collection_key_free(&key);
181
182 return true;
183}
184
185static ant_value_t map_init_from_iterable(ant_t *js, map_entry_t **map_head, ant_value_t iterable) {
186 js_iter_t it;
187 if (!js_iter_open(js, iterable, &it)) {
188 return js_mkerr_typed(js, JS_ERR_TYPE, "Map constructor argument is not iterable");
189 }
190
191 ant_value_t entry;
192 while (js_iter_next(js, &it, &entry)) {
193 uint8_t entry_t = vtype(entry);
194 if (entry_t != T_ARR && entry_t != T_OBJ) {
195 js_iter_close(js, &it);
196 return js_mkerr_typed(js, JS_ERR_TYPE, "Map iterable entries must be pair sequences");
197 }
198
199 ant_offset_t entry_len = js_arr_len(js, entry);
200 if (entry_len < 2) {
201 js_iter_close(js, &it);
202 return js_mkerr_typed(js, JS_ERR_TYPE, "Map iterable entries must have at least 2 items");
203 }
204
205 ant_value_t key = normalize_map_key(js_arr_get(js, entry, 0));
206 ant_value_t value = js_arr_get(js, entry, 1);
207 if (!map_store_entry(js, map_head, key, key, value)) {
208 js_iter_close(js, &it);
209 return js_mkerr(js, "out of memory");
210 }}
211
212 return js_mkundef();
213}
214
215static ant_value_t set_init_from_iterable(ant_t *js, set_entry_t **set_head, ant_value_t iterable) {
216 js_iter_t it;
217 if (!js_iter_open(js, iterable, &it)) {
218 return js_mkerr_typed(js, JS_ERR_TYPE, "Set constructor argument is not iterable");
219 }
220
221 ant_value_t value;
222 while (js_iter_next(js, &it, &value)) if (!set_store_entry(js, set_head, value)) {
223 js_iter_close(js, &it);
224 return js_mkerr(js, "out of memory");
225 }
226
227 return js_mkundef();
228}
229
230static ant_value_t weakmap_init_from_iterable(ant_t *js, weakmap_entry_t **wm_head, ant_value_t iterable) {
231 js_iter_t it;
232 if (!js_iter_open(js, iterable, &it)) {
233 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap constructor argument is not iterable");
234 }
235
236 ant_value_t entry;
237 while (js_iter_next(js, &it, &entry)) {
238 uint8_t entry_t = vtype(entry);
239 if (entry_t != T_ARR && entry_t != T_OBJ) {
240 js_iter_close(js, &it);
241 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap iterable entries must be pair sequences");
242 }
243
244 ant_offset_t entry_len = js_arr_len(js, entry);
245 if (entry_len < 2) {
246 js_iter_close(js, &it);
247 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap iterable entries must have at least 2 items");
248 }
249
250 ant_value_t key = js_arr_get(js, entry, 0);
251 ant_value_t value = js_arr_get(js, entry, 1);
252 if (!is_object_type(key)) {
253 js_iter_close(js, &it);
254 return js_mkerr(js, "WeakMap key must be an object");
255 }
256
257 weakmap_entry_t *wm_entry;
258 HASH_FIND(hh, *wm_head, &key, sizeof(ant_value_t), wm_entry);
259 if (wm_entry) {
260 wm_entry->value = value;
261 continue;
262 }
263
264 wm_entry = ant_calloc(sizeof(weakmap_entry_t));
265 if (!wm_entry) {
266 js_iter_close(js, &it);
267 return js_mkerr(js, "out of memory");
268 }
269
270 wm_entry->key_obj = key;
271 wm_entry->value = value;
272 HASH_ADD(hh, *wm_head, key_obj, sizeof(ant_value_t), wm_entry);
273 }
274
275 return js_mkundef();
276}
277
278static ant_value_t weakset_init_from_iterable(ant_t *js, weakset_entry_t **ws_head, ant_value_t iterable) {
279 js_iter_t it;
280 if (!js_iter_open(js, iterable, &it)) {
281 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakSet constructor argument is not iterable");
282 }
283
284 ant_value_t value;
285 while (js_iter_next(js, &it, &value)) {
286 if (!is_object_type(value)) {
287 js_iter_close(js, &it);
288 return js_mkerr(js, "WeakSet value must be an object");
289 }
290
291 weakset_entry_t *entry;
292 HASH_FIND(hh, *ws_head, &value, sizeof(ant_value_t), entry);
293 if (entry) continue;
294
295 entry = ant_calloc(sizeof(weakset_entry_t));
296 if (!entry) {
297 js_iter_close(js, &it);
298 return js_mkerr(js, "out of memory");
299 }
300
301 entry->value_obj = value;
302 HASH_ADD(hh, *ws_head, value_obj, sizeof(ant_value_t), entry);
303 }
304
305 return js_mkundef();
306}
307
308map_entry_t **get_map_from_obj(ant_value_t obj) {
309 ant_object_t *ptr = js_obj_ptr(obj);
310 if (!ptr || ptr->type_tag != T_MAP) return NULL;
311 return (map_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);
312}
313
314set_entry_t **get_set_from_obj(ant_value_t obj) {
315 ant_object_t *ptr = js_obj_ptr(obj);
316 if (!ptr || ptr->type_tag != T_SET) return NULL;
317 return (set_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);
318}
319
320static weakmap_entry_t **get_weakmap_from_obj(ant_value_t obj) {
321 ant_object_t *ptr = js_obj_ptr(obj);
322 if (!ptr || ptr->type_tag != T_WEAKMAP) return NULL;
323 return (weakmap_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);
324}
325
326static weakset_entry_t **get_weakset_from_obj(ant_value_t obj) {
327 ant_object_t *ptr = js_obj_ptr(obj);
328 if (!ptr || ptr->type_tag != T_WEAKSET) return NULL;
329 return (weakset_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value);
330}
331
332static map_iterator_state_t *get_map_iter_state(ant_value_t obj) {
333 ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE);
334 if (vtype(state_val) == T_UNDEF) return NULL;
335 return (map_iterator_state_t *)(uintptr_t)js_getnum(state_val);
336}
337
338static set_iterator_state_t *get_set_iter_state(ant_value_t obj) {
339 ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE);
340 if (vtype(state_val) == T_UNDEF) return NULL;
341 return (set_iterator_state_t *)(uintptr_t)js_getnum(state_val);
342}
343
344static ant_value_t map_set(ant_t *js, ant_value_t *args, int nargs) {
345 if (nargs < 2) return js_mkerr(js, "Map.set() requires 2 arguments");
346
347 ant_value_t this_val = js->this_val;
348 map_entry_t **map_ptr = get_map_from_obj(this_val);
349 if (!map_ptr) return js_mkerr(js, "Invalid Map object");
350
351 ant_value_t key_val = normalize_map_key(args[0]);
352 if (!map_store_entry(js, map_ptr, args[0], key_val, args[1]))
353 return js_mkerr(js, "out of memory");
354
355 ant_object_t *map_obj = js_obj_ptr(this_val);
356 if (map_obj) {
357 gc_write_barrier(js, map_obj, key_val);
358 gc_write_barrier(js, map_obj, args[1]);
359 }
360
361 return this_val;
362}
363
364static ant_value_t map_get(ant_t *js, ant_value_t *args, int nargs) {
365 if (nargs < 1) return js_mkerr(js, "Map.get() requires 1 argument");
366
367 ant_value_t this_val = js->this_val;
368 map_entry_t **map_ptr = get_map_from_obj(this_val);
369 if (!map_ptr) return js_mkundef();
370
371 map_entry_t *entry = map_find_entry(js, map_ptr, args[0]);
372 return entry ? entry->value : js_mkundef();
373}
374
375static ant_value_t map_has(ant_t *js, ant_value_t *args, int nargs) {
376 if (nargs < 1) return js_mkerr(js, "Map.has() requires 1 argument");
377
378 ant_value_t this_val = js->this_val;
379 map_entry_t **map_ptr = get_map_from_obj(this_val);
380
381 if (!map_ptr) return js_false;
382 map_entry_t *entry = map_find_entry(js, map_ptr, args[0]);
383 return js_bool(entry != NULL);
384}
385
386static ant_value_t map_upsert(ant_t *js, ant_value_t *args, int nargs) {
387 if (nargs < 3) return js_mkerr(js, "Map.upsert() requires 3 arguments");
388
389 ant_value_t this_val = js->this_val;
390 map_entry_t **map_ptr = get_map_from_obj(this_val);
391 if (!map_ptr) return js_mkerr(js, "Invalid Map object");
392
393 ant_value_t update_fn = args[1];
394 ant_value_t insert_fn = args[2];
395 if (!is_callable(update_fn))
396 return js_mkerr_typed(js, JS_ERR_TYPE, "Map.upsert update callback must be callable");
397 if (!is_callable(insert_fn))
398 return js_mkerr_typed(js, JS_ERR_TYPE, "Map.upsert insert callback must be callable");
399
400 map_entry_t *entry = map_find_entry(js, map_ptr, args[0]);
401 ant_value_t value;
402
403 if (entry) {
404 ant_value_t call_args[3] = { entry->value, args[0], this_val };
405 value = sv_vm_call(js->vm, js, update_fn, js_mkundef(), call_args, 3, NULL, false);
406 } else {
407 ant_value_t call_args[2] = { args[0], this_val };
408 value = sv_vm_call(js->vm, js, insert_fn, js_mkundef(), call_args, 2, NULL, false);
409 }
410
411 if (is_err(value)) return value;
412
413 ant_value_t key_val = normalize_map_key(args[0]);
414 if (!map_store_entry(js, map_ptr, args[0], key_val, value))
415 return js_mkerr(js, "out of memory");
416
417 ant_object_t *map_obj = js_obj_ptr(this_val);
418 if (map_obj) {
419 gc_write_barrier(js, map_obj, key_val);
420 gc_write_barrier(js, map_obj, value);
421 }
422
423 return value;
424}
425
426static ant_value_t map_delete(ant_t *js, ant_value_t *args, int nargs) {
427 if (nargs < 1) return js_mkerr(js, "Map.delete() requires 1 argument");
428
429 ant_value_t this_val = js->this_val;
430 map_entry_t **map_ptr = get_map_from_obj(this_val);
431
432 if (!map_ptr) return js_false;
433 map_entry_t *entry = map_find_entry(js, map_ptr, args[0]);
434 if (entry) {
435 HASH_DEL(*map_ptr, entry);
436 free(entry->key);
437 free(entry);
438 return js_true;
439 }
440 return js_false;
441}
442
443static ant_value_t map_clear(ant_t *js, ant_value_t *args, int nargs) {
444 ant_value_t this_val = js->this_val;
445 map_entry_t **map_ptr = get_map_from_obj(this_val);
446 if (!map_ptr) return js_mkundef();
447
448 map_entry_t *entry, *tmp;
449 HASH_ITER(hh, *map_ptr, entry, tmp) {
450 HASH_DEL(*map_ptr, entry);
451 free(entry->key);
452 free(entry);
453 }
454 *map_ptr = NULL;
455
456 return js_mkundef();
457}
458
459static ant_value_t map_size(ant_t *js, ant_value_t *args, int nargs) {
460 ant_value_t this_val = js->this_val;
461 map_entry_t **map_ptr = get_map_from_obj(this_val);
462 if (!map_ptr) return js_mknum(0);
463
464 return js_mknum((double)HASH_COUNT(*map_ptr));
465}
466
467static ant_value_t map_forEach(ant_t *js, ant_value_t *args, int nargs) {
468 ant_value_t this_val = js->this_val;
469 map_entry_t **map_ptr = get_map_from_obj(this_val);
470
471 if (nargs < 1 || vtype(args[0]) != T_FUNC)
472 return js_mkerr(js, "forEach requires a callback function");
473
474 ant_value_t callback = args[0];
475
476 if (map_ptr && *map_ptr) {
477 map_entry_t *entry, *tmp;
478 HASH_ITER(hh, *map_ptr, entry, tmp) {
479 ant_value_t k = entry->key_val;
480 ant_value_t call_args[3] = { entry->value, k, this_val };
481 ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false);
482 if (is_err(result)) return result;
483 }}
484
485 return js_mkundef();
486}
487
488bool advance_map(ant_t *js, js_iter_t *it, ant_value_t *out) {
489 map_iterator_state_t *state = get_map_iter_state(it->iterator);
490 if (!state || !state->current) return false;
491
492 map_entry_t *entry = state->current;
493 switch (state->type) {
494 case ITER_TYPE_MAP_VALUES:
495 *out = entry->value;
496 break;
497 case ITER_TYPE_MAP_KEYS:
498 *out = entry->key_val;
499 break;
500 case ITER_TYPE_MAP_ENTRIES: {
501 ant_value_t pair = js_mkarr(js);
502 js_arr_push(js, pair, entry->key_val);
503 js_arr_push(js, pair, entry->value);
504 *out = pair;
505 break;
506 }
507 default: *out = js_mkundef();
508 }
509
510 state->current = entry->hh.next;
511 return true;
512}
513
514static ant_value_t map_iter_next(ant_t *js, ant_value_t *args, int nargs) {
515 js_iter_t it = { .iterator = js->this_val };
516 ant_value_t value;
517 return js_iter_result(js, advance_map(js, &it, &value), value);
518}
519
520static ant_value_t create_map_iterator(ant_t *js, ant_value_t map_obj, iter_type_t type) {
521 map_entry_t **map_ptr = get_map_from_obj(map_obj);
522
523 map_iterator_state_t *state = ant_calloc(sizeof(map_iterator_state_t));
524 if (!state) return js_mkerr(js, "out of memory");
525
526 state->head = map_ptr;
527 state->current = map_ptr ? *map_ptr : NULL;
528 state->type = type;
529
530 ant_value_t iter = js_mkobj(js);
531 js_set_proto_init(iter, g_map_iter_proto);
532 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state));
533
534 return iter;
535}
536
537static ant_value_t map_values(ant_t *js, ant_value_t *args, int nargs) {
538 (void)args; (void)nargs;
539 return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_VALUES);
540}
541
542static ant_value_t map_keys(ant_t *js, ant_value_t *args, int nargs) {
543 (void)args; (void)nargs;
544 return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_KEYS);
545}
546
547static ant_value_t map_entries(ant_t *js, ant_value_t *args, int nargs) {
548 (void)args; (void)nargs;
549 return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_ENTRIES);
550}
551
552bool advance_set(ant_t *js, js_iter_t *it, ant_value_t *out) {
553 set_iterator_state_t *state = get_set_iter_state(it->iterator);
554 if (!state || !state->current) return false;
555
556 set_entry_t *entry = state->current;
557 if (state->type == ITER_TYPE_SET_ENTRIES) {
558 ant_value_t pair = js_mkarr(js);
559 js_arr_push(js, pair, entry->value);
560 js_arr_push(js, pair, entry->value);
561 *out = pair;
562 } else *out = entry->value;
563
564 state->current = entry->hh.next;
565 return true;
566}
567
568static ant_value_t set_iter_next(ant_t *js, ant_value_t *args, int nargs) {
569 js_iter_t it = { .iterator = js->this_val };
570 ant_value_t value;
571 return js_iter_result(js, advance_set(js, &it, &value), value);
572}
573
574static ant_value_t create_set_iterator(ant_t *js, ant_value_t set_obj, iter_type_t type) {
575 set_entry_t **set_ptr = get_set_from_obj(set_obj);
576
577 set_iterator_state_t *state = ant_calloc(sizeof(set_iterator_state_t));
578 if (!state) return js_mkerr(js, "out of memory");
579
580 state->head = set_ptr;
581 state->current = set_ptr ? *set_ptr : NULL;
582 state->type = type;
583
584 ant_value_t iter = js_mkobj(js);
585 js_set_proto_init(iter, g_set_iter_proto);
586 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state));
587
588 return iter;
589}
590
591static ant_value_t set_add(ant_t *js, ant_value_t *args, int nargs) {
592 if (nargs < 1) return js_mkerr(js, "Set.add() requires 1 argument");
593
594 ant_value_t this_val = js->this_val;
595 set_entry_t **set_ptr = get_set_from_obj(this_val);
596 if (!set_ptr) return js_mkerr(js, "Invalid Set object");
597
598 if (!set_store_entry(js, set_ptr, args[0]))
599 return js_mkerr(js, "out of memory");
600
601 ant_object_t *set_obj = js_obj_ptr(this_val);
602 if (set_obj) gc_write_barrier(js, set_obj, args[0]);
603
604 return this_val;
605}
606
607static ant_value_t set_has(ant_t *js, ant_value_t *args, int nargs) {
608 if (nargs < 1) return js_mkerr(js, "Set.has() requires 1 argument");
609
610 ant_value_t this_val = js->this_val;
611 set_entry_t **set_ptr = get_set_from_obj(this_val);
612 if (!set_ptr) return js_false;
613
614 set_entry_t *entry = set_find_entry(js, set_ptr, args[0]);
615 return js_bool(entry != NULL);
616}
617
618static ant_value_t set_delete(ant_t *js, ant_value_t *args, int nargs) {
619 if (nargs < 1) return js_mkerr(js, "Set.delete() requires 1 argument");
620
621 ant_value_t this_val = js->this_val;
622 set_entry_t **set_ptr = get_set_from_obj(this_val);
623 if (!set_ptr) return js_false;
624
625 set_entry_t *entry = set_find_entry(js, set_ptr, args[0]);
626
627 if (entry) {
628 HASH_DEL(*set_ptr, entry);
629 free(entry->key);
630 free(entry);
631 return js_true;
632 }
633 return js_false;
634}
635
636static ant_value_t set_clear(ant_t *js, ant_value_t *args, int nargs) {
637 (void)args; (void)nargs;
638 ant_value_t this_val = js->this_val;
639 set_entry_t **set_ptr = get_set_from_obj(this_val);
640 if (!set_ptr) return js_mkundef();
641
642 set_entry_t *entry, *tmp;
643 HASH_ITER(hh, *set_ptr, entry, tmp) {
644 HASH_DEL(*set_ptr, entry);
645 free(entry->key);
646 free(entry);
647 }
648 *set_ptr = NULL;
649
650 return js_mkundef();
651}
652
653static ant_value_t set_size(ant_t *js, ant_value_t *args, int nargs) {
654 (void)args; (void)nargs;
655 ant_value_t this_val = js->this_val;
656 set_entry_t **set_ptr = get_set_from_obj(this_val);
657 if (!set_ptr) return js_mknum(0);
658
659 return js_mknum((double)HASH_COUNT(*set_ptr));
660}
661
662static ant_value_t set_values(ant_t *js, ant_value_t *args, int nargs) {
663 (void)args; (void)nargs;
664 return create_set_iterator(js, js->this_val, ITER_TYPE_SET_VALUES);
665}
666
667static ant_value_t set_entries(ant_t *js, ant_value_t *args, int nargs) {
668 (void)args; (void)nargs;
669 return create_set_iterator(js, js->this_val, ITER_TYPE_SET_ENTRIES);
670}
671
672static ant_value_t set_forEach(ant_t *js, ant_value_t *args, int nargs) {
673 ant_value_t this_val = js->this_val;
674 set_entry_t **set_ptr = get_set_from_obj(this_val);
675
676 if (nargs < 1 || vtype(args[0]) != T_FUNC)
677 return js_mkerr(js, "forEach requires a callback function");
678
679 ant_value_t callback = args[0];
680 if (set_ptr && *set_ptr) {
681 set_entry_t *entry, *tmp;
682
683 HASH_ITER(hh, *set_ptr, entry, tmp) {
684 ant_value_t call_args[3] = { entry->value, entry->value, this_val };
685 ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false);
686 if (is_err(result)) return result;
687 }}
688
689 return js_mkundef();
690}
691
692static ant_value_t make_set_result(ant_t *js, set_entry_t ***out_set) {
693 ant_value_t set_obj = js_mkobj(js);
694 if (is_err(set_obj)) return set_obj;
695 js_obj_ptr(set_obj)->type_tag = T_SET;
696
697 ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3);
698 if (is_special_object(set_proto)) js_set_proto_init(set_obj, set_proto);
699
700 set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *));
701 if (!set_head) return js_mkerr(js, "out of memory");
702 *set_head = NULL;
703
704 js_set_slot(set_obj, SLOT_DATA, ANT_PTR(set_head));
705 if (out_set) *out_set = set_head;
706
707 return set_obj;
708}
709
710static bool set_result_add(ant_t *js, ant_value_t set_obj, set_entry_t **set_ptr, ant_value_t value) {
711 if (!set_store_entry(js, set_ptr, value)) return false;
712 ant_object_t *obj = js_obj_ptr(set_obj);
713 if (obj) gc_write_barrier(js, obj, value);
714 return true;
715}
716
717static void set_result_delete(ant_t *js, set_entry_t **set_ptr, ant_value_t value) {
718 set_entry_t *entry = set_find_entry(js, set_ptr, value);
719 if (!entry) return;
720 HASH_DEL(*set_ptr, entry);
721 free(entry->key);
722 free(entry);
723}
724
725typedef struct {
726 ant_value_t obj;
727 ant_value_t has;
728 ant_value_t keys;
729 double size;
730} set_record_t;
731
732typedef enum {
733 SET_KEY_CONTINUE,
734 SET_KEY_STOP,
735} set_key_status_t;
736
737typedef set_key_status_t (*set_key_cb)(
738 ant_t *js,
739 ant_value_t value,
740 ant_value_t *result,
741 void *ctx
742);
743
744static ant_value_t get_set_record(ant_t *js, ant_value_t value, const char *method, set_record_t *out) {
745 if (!is_object_type(value))
746 return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a set-like object", method);
747
748 ant_value_t size = js_getprop_fallback(js, value, "size");
749 if (is_err(size)) return size;
750
751 if (vtype(size) == T_BIGINT || vtype(size) == T_SYMBOL)
752 return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a numeric size", method);
753 double num_size = js_to_number(js, size);
754 if (isnan(num_size))
755 return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a numeric size", method);
756 double int_size = (num_size == 0.0 || !isfinite(num_size))
757 ? num_size
758 : (num_size < 0 ? -floor(-num_size) : floor(num_size));
759 if (int_size < 0)
760 return js_mkerr_typed(js, JS_ERR_RANGE, "Set.%s() requires a non-negative size", method);
761
762 ant_value_t has = js_getprop_fallback(js, value, "has");
763 if (is_err(has)) return has;
764 if (!is_callable(has))
765 return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a callable has method", method);
766
767 ant_value_t keys = js_getprop_fallback(js, value, "keys");
768 if (is_err(keys)) return keys;
769 if (!is_callable(keys))
770 return js_mkerr_typed(js, JS_ERR_TYPE, "Set.%s() requires a callable keys method", method);
771
772 out->obj = value;
773 out->has = has;
774 out->keys = keys;
775 out->size = int_size;
776
777 return js_mkundef();
778}
779
780static ant_value_t set_record_has(ant_t *js, set_record_t *record, ant_value_t value, bool *out) {
781 ant_value_t result = sv_vm_call(js->vm, js, record->has, record->obj, &value, 1, NULL, false);
782 if (is_err(result)) return result;
783 *out = js_truthy(js, result);
784 return js_mkundef();
785}
786
787static ant_value_t set_record_close_keys_iterator(ant_t *js, ant_value_t iterator) {
788 ant_value_t return_fn = js_getprop_fallback(js, iterator, "return");
789 if (is_err(return_fn)) return return_fn;
790 if (!is_callable(return_fn)) return js_mkundef();
791 return sv_vm_call(js->vm, js, return_fn, iterator, NULL, 0, NULL, false);
792}
793
794static ant_value_t set_record_for_each_key(ant_t *js, set_record_t *record, set_key_cb cb, void *ctx) {
795 ant_value_t iterator = sv_vm_call(js->vm, js, record->keys, record->obj, NULL, 0, NULL, false);
796 if (is_err(iterator)) return iterator;
797 if (!is_object_type(iterator))
798 return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() result is not an iterator");
799
800 ant_value_t next_fn = js_getprop_fallback(js, iterator, "next");
801 if (is_err(next_fn)) return next_fn;
802 if (!is_callable(next_fn))
803 return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() iterator has no callable next method");
804
805 while (true) {
806 ant_value_t next = sv_vm_call(js->vm, js, next_fn, iterator, NULL, 0, NULL, false);
807 if (is_err(next)) return next;
808 if (!is_object_type(next))
809 return js_mkerr_typed(js, JS_ERR_TYPE, "Set keys() iterator result is not an object");
810
811 ant_value_t done = js_getprop_fallback(js, next, "done");
812 if (is_err(done)) return done;
813 if (js_truthy(js, done)) return js_mkundef();
814
815 ant_value_t value = js_getprop_fallback(js, next, "value");
816 if (is_err(value)) return value;
817
818 ant_value_t result = js_mkundef();
819 set_key_status_t status = cb(js, value, &result, ctx);
820
821 if (is_err(result)) {
822 ant_value_t close_result = set_record_close_keys_iterator(js, iterator);
823 return is_err(close_result) ? close_result : result;
824 }
825
826 if (status == SET_KEY_STOP) {
827 ant_value_t close_result = set_record_close_keys_iterator(js, iterator);
828 return is_err(close_result) ? close_result : js_mkundef();
829 }
830 }
831}
832
833typedef struct {
834 ant_value_t out;
835 set_entry_t **out_set;
836} set_build_ctx_t;
837
838static set_key_status_t set_add_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
839 set_build_ctx_t *build = (set_build_ctx_t *)ctx;
840 if (!set_result_add(js, build->out, build->out_set, value))
841 *result = js_mkerr(js, "out of memory");
842 return SET_KEY_CONTINUE;
843}
844
845static ant_value_t set_union(ant_t *js, ant_value_t *args, int nargs) {
846 set_entry_t **this_set = get_set_from_obj(js->this_val);
847 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
848 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.union() requires a set-like object");
849
850 set_record_t other;
851 ant_value_t rec = get_set_record(js, args[0], "union", &other);
852 if (is_err(rec)) return rec;
853
854 set_entry_t **out_set = NULL;
855 ant_value_t out = make_set_result(js, &out_set);
856 if (is_err(out)) return out;
857 set_build_ctx_t build = { out, out_set };
858
859 set_entry_t *entry, *tmp;
860 HASH_ITER(hh, *this_set, entry, tmp)
861 if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory");
862 ant_value_t result = set_record_for_each_key(js, &other, set_add_key_cb, &build);
863
864 return is_err(result) ? result : out;
865}
866
867typedef struct {
868 ant_value_t out;
869 set_entry_t **out_set;
870 set_entry_t **this_set;
871} set_compare_build_ctx_t;
872
873static set_key_status_t set_intersection_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
874 set_compare_build_ctx_t *build = (set_compare_build_ctx_t *)ctx;
875 if (
876 set_find_entry(js, build->this_set, value) &&
877 !set_result_add(js, build->out, build->out_set, value)
878 ) *result = js_mkerr(js, "out of memory");
879 return SET_KEY_CONTINUE;
880}
881
882static ant_value_t set_intersection(ant_t *js, ant_value_t *args, int nargs) {
883 set_entry_t **this_set = get_set_from_obj(js->this_val);
884 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
885 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.intersection() requires a set-like object");
886
887 set_record_t other;
888 ant_value_t rec = get_set_record(js, args[0], "intersection", &other);
889 if (is_err(rec)) return rec;
890
891 set_entry_t **out_set = NULL;
892 ant_value_t out = make_set_result(js, &out_set);
893 if (is_err(out)) return out;
894
895 double this_size = (double)HASH_COUNT(*this_set);
896 if (this_size <= other.size) {
897 set_entry_t *entry, *tmp;
898 HASH_ITER(hh, *this_set, entry, tmp) {
899 bool has = false;
900 ant_value_t result = set_record_has(js, &other, entry->value, &has);
901 if (is_err(result)) return result;
902 if (has && !set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory");
903 }
904 return out;
905 }
906
907 set_compare_build_ctx_t build = { out, out_set, this_set };
908 ant_value_t result = set_record_for_each_key(js, &other, set_intersection_key_cb, &build);
909
910 return is_err(result) ? result : out;
911}
912
913typedef struct {
914 set_record_t *other;
915 ant_value_t out;
916 set_entry_t **out_set;
917} set_difference_ctx_t;
918
919static ant_value_t set_difference_key_cb(ant_t *js, ant_value_t value, void *ctx) {
920 set_difference_ctx_t *build = (set_difference_ctx_t *)ctx;
921 bool has = false;
922 ant_value_t result = set_record_has(js, build->other, value, &has);
923 if (is_err(result)) return result;
924 if (!has && !set_result_add(js, build->out, build->out_set, value)) return js_mkerr(js, "out of memory");
925 return js_mkundef();
926}
927
928static set_key_status_t set_delete_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
929 set_build_ctx_t *build = (set_build_ctx_t *)ctx;
930 set_result_delete(js, build->out_set, value);
931 return SET_KEY_CONTINUE;
932}
933
934static ant_value_t set_difference(ant_t *js, ant_value_t *args, int nargs) {
935 set_entry_t **this_set = get_set_from_obj(js->this_val);
936 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
937 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.difference() requires a set-like object");
938
939 set_record_t other;
940 ant_value_t rec = get_set_record(js, args[0], "difference", &other);
941 if (is_err(rec)) return rec;
942
943 set_entry_t **out_set = NULL;
944 ant_value_t out = make_set_result(js, &out_set);
945 if (is_err(out)) return out;
946
947 set_entry_t *entry, *tmp;
948 if ((double)HASH_COUNT(*this_set) <= other.size) {
949 set_difference_ctx_t diff = { &other, out, out_set };
950 HASH_ITER(hh, *this_set, entry, tmp) {
951 ant_value_t result = set_difference_key_cb(js, entry->value, &diff);
952 if (is_err(result)) return result;
953 }
954 return out;
955 }
956
957 HASH_ITER(hh, *this_set, entry, tmp) {
958 if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory");
959 }
960
961 set_build_ctx_t build = { out, out_set };
962 ant_value_t result = set_record_for_each_key(js, &other, set_delete_key_cb, &build);
963
964 return is_err(result) ? result : out;
965}
966
967typedef struct {
968 ant_value_t out;
969 set_entry_t **out_set;
970 set_entry_t **this_set;
971} set_symdiff_ctx_t;
972
973static set_key_status_t set_symmetric_difference_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
974 set_symdiff_ctx_t *build = (set_symdiff_ctx_t *)ctx;
975 if (set_find_entry(js, build->this_set, value)) {
976 set_result_delete(js, build->out_set, value);
977 } else if (!set_find_entry(js, build->out_set, value))
978 if (!set_result_add(js, build->out, build->out_set, value)) *result = js_mkerr(js, "out of memory");
979 return SET_KEY_CONTINUE;
980}
981
982static ant_value_t set_symmetricDifference(ant_t *js, ant_value_t *args, int nargs) {
983 set_entry_t **this_set = get_set_from_obj(js->this_val);
984 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
985 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.symmetricDifference() requires a set-like object");
986
987 set_record_t other;
988 ant_value_t rec = get_set_record(js, args[0], "symmetricDifference", &other);
989 if (is_err(rec)) return rec;
990
991 set_entry_t **out_set = NULL;
992 ant_value_t out = make_set_result(js, &out_set);
993 if (is_err(out)) return out;
994
995 set_entry_t *entry, *tmp;
996 HASH_ITER(hh, *this_set, entry, tmp) {
997 if (!set_result_add(js, out, out_set, entry->value)) return js_mkerr(js, "out of memory");
998 }
999 set_symdiff_ctx_t build = { out, out_set, this_set };
1000 ant_value_t result = set_record_for_each_key(js, &other, set_symmetric_difference_key_cb, &build);
1001 return is_err(result) ? result : out;
1002}
1003
1004static ant_value_t set_isSubsetOf(ant_t *js, ant_value_t *args, int nargs) {
1005 set_entry_t **this_set = get_set_from_obj(js->this_val);
1006 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
1007 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isSubsetOf() requires a set-like object");
1008
1009 set_record_t other;
1010 ant_value_t rec = get_set_record(js, args[0], "isSubsetOf", &other);
1011
1012 if (is_err(rec)) return rec;
1013 if ((double)HASH_COUNT(*this_set) > other.size) return js_false;
1014
1015 set_entry_t *entry, *tmp;
1016 HASH_ITER(hh, *this_set, entry, tmp) {
1017 bool has = false;
1018 ant_value_t result = set_record_has(js, &other, entry->value, &has);
1019 if (is_err(result)) return result;
1020 if (!has) return js_false;
1021 }
1022 return js_true;
1023}
1024
1025typedef struct {
1026 set_entry_t **this_set;
1027 bool result;
1028} set_predicate_ctx_t;
1029
1030static set_key_status_t set_superset_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
1031 set_predicate_ctx_t *pred = (set_predicate_ctx_t *)ctx;
1032 if (!set_find_entry(js, pred->this_set, value)) {
1033 pred->result = false;
1034 return SET_KEY_STOP;
1035 }
1036 return SET_KEY_CONTINUE;
1037}
1038
1039static ant_value_t set_isSupersetOf(ant_t *js, ant_value_t *args, int nargs) {
1040 set_entry_t **this_set = get_set_from_obj(js->this_val);
1041 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
1042 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isSupersetOf() requires a set-like object");
1043
1044 set_record_t other;
1045 ant_value_t rec = get_set_record(js, args[0], "isSupersetOf", &other);
1046 if (is_err(rec)) return rec;
1047 if ((double)HASH_COUNT(*this_set) < other.size) return js_false;
1048
1049 set_predicate_ctx_t pred = { this_set, true };
1050 ant_value_t result = set_record_for_each_key(js, &other, set_superset_key_cb, &pred);
1051 if (is_err(result)) return result;
1052 return js_bool(pred.result);
1053}
1054
1055static set_key_status_t set_disjoint_key_cb(ant_t *js, ant_value_t value, ant_value_t *result, void *ctx) {
1056 set_predicate_ctx_t *pred = (set_predicate_ctx_t *)ctx;
1057 if (set_find_entry(js, pred->this_set, value)) {
1058 pred->result = false;
1059 return SET_KEY_STOP;
1060 }
1061 return SET_KEY_CONTINUE;
1062}
1063
1064static ant_value_t set_isDisjointFrom(ant_t *js, ant_value_t *args, int nargs) {
1065 set_entry_t **this_set = get_set_from_obj(js->this_val);
1066 if (!this_set) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid Set object");
1067 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Set.isDisjointFrom() requires a set-like object");
1068
1069 set_record_t other;
1070 ant_value_t rec = get_set_record(js, args[0], "isDisjointFrom", &other);
1071 if (is_err(rec)) return rec;
1072
1073 if ((double)HASH_COUNT(*this_set) <= other.size) {
1074 set_entry_t *entry, *tmp;
1075 HASH_ITER(hh, *this_set, entry, tmp) {
1076 bool has = false;
1077 ant_value_t result = set_record_has(js, &other, entry->value, &has);
1078 if (is_err(result)) return result;
1079 if (has) return js_false;
1080 }
1081 return js_true;
1082 }
1083
1084 set_predicate_ctx_t pred = { this_set, true };
1085 ant_value_t result = set_record_for_each_key(js, &other, set_disjoint_key_cb, &pred);
1086 if (is_err(result)) return result;
1087
1088 return js_bool(pred.result);
1089}
1090
1091static ant_value_t weakmap_set(ant_t *js, ant_value_t *args, int nargs) {
1092 if (nargs < 2) return js_mkerr(js, "WeakMap.set() requires 2 arguments");
1093
1094 ant_value_t this_val = js->this_val;
1095 weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val);
1096 if (!wm_ptr) return js_mkerr(js, "Invalid WeakMap object");
1097
1098 if (!is_object_type(args[0]))
1099 return js_mkerr(js, "WeakMap key must be an object");
1100
1101 ant_value_t key_obj = args[0];
1102 weakmap_entry_t *entry;
1103 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1104
1105 if (entry) entry->value = args[1]; else {
1106 entry = ant_calloc(sizeof(weakmap_entry_t));
1107 if (!entry) return js_mkerr(js, "out of memory");
1108 entry->key_obj = key_obj;
1109 entry->value = args[1];
1110 HASH_ADD(hh, *wm_ptr, key_obj, sizeof(ant_value_t), entry);
1111 }
1112
1113 ant_object_t *wm_obj = js_obj_ptr(this_val);
1114 if (wm_obj) {
1115 gc_write_barrier(js, wm_obj, key_obj);
1116 gc_write_barrier(js, wm_obj, args[1]);
1117 }
1118
1119 return this_val;
1120}
1121
1122static ant_value_t weakmap_get(ant_t *js, ant_value_t *args, int nargs) {
1123 if (nargs < 1) return js_mkerr(js, "WeakMap.get() requires 1 argument");
1124
1125 ant_value_t this_val = js->this_val;
1126 weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val);
1127 if (!wm_ptr) return js_mkundef();
1128 if (!is_object_type(args[0])) return js_mkundef();
1129
1130 ant_value_t key_obj = args[0];
1131 weakmap_entry_t *entry;
1132 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1133 return entry ? entry->value : js_mkundef();
1134}
1135
1136static ant_value_t weakmap_has(ant_t *js, ant_value_t *args, int nargs) {
1137 if (nargs < 1) return js_mkerr(js, "WeakMap.has() requires 1 argument");
1138
1139 ant_value_t this_val = js->this_val;
1140 weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val);
1141 if (!wm_ptr) return js_false;
1142 if (!is_object_type(args[0])) return js_false;
1143
1144 ant_value_t key_obj = args[0];
1145 weakmap_entry_t *entry;
1146 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1147 return js_bool(entry != NULL);
1148}
1149
1150static ant_value_t weakmap_upsert(ant_t *js, ant_value_t *args, int nargs) {
1151 if (nargs < 3) return js_mkerr(js, "WeakMap.upsert() requires 3 arguments");
1152
1153 ant_value_t this_val = js->this_val;
1154 weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val);
1155 if (!wm_ptr) return js_mkerr(js, "Invalid WeakMap object");
1156
1157 if (!is_object_type(args[0]))
1158 return js_mkerr(js, "WeakMap key must be an object");
1159
1160 ant_value_t update_fn = args[1];
1161 ant_value_t insert_fn = args[2];
1162 if (!is_callable(update_fn))
1163 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap.upsert update callback must be callable");
1164 if (!is_callable(insert_fn))
1165 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap.upsert insert callback must be callable");
1166
1167 ant_value_t key_obj = args[0];
1168 weakmap_entry_t *entry;
1169 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1170
1171 ant_value_t value;
1172 if (entry) {
1173 ant_value_t call_args[3] = { entry->value, key_obj, this_val };
1174 value = sv_vm_call(js->vm, js, update_fn, js_mkundef(), call_args, 3, NULL, false);
1175 } else {
1176 ant_value_t call_args[2] = { key_obj, this_val };
1177 value = sv_vm_call(js->vm, js, insert_fn, js_mkundef(), call_args, 2, NULL, false);
1178 }
1179
1180 if (is_err(value)) return value;
1181
1182 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1183 if (entry) entry->value = value; else {
1184 entry = ant_calloc(sizeof(weakmap_entry_t));
1185 if (!entry) return js_mkerr(js, "out of memory");
1186 entry->key_obj = key_obj;
1187 entry->value = value;
1188 HASH_ADD(hh, *wm_ptr, key_obj, sizeof(ant_value_t), entry);
1189 }
1190
1191 ant_object_t *wm_obj = js_obj_ptr(this_val);
1192 if (wm_obj) {
1193 gc_write_barrier(js, wm_obj, key_obj);
1194 gc_write_barrier(js, wm_obj, value);
1195 }
1196
1197 return value;
1198}
1199
1200static ant_value_t weakmap_delete(ant_t *js, ant_value_t *args, int nargs) {
1201 if (nargs < 1) return js_mkerr(js, "WeakMap.delete() requires 1 argument");
1202
1203 ant_value_t this_val = js->this_val;
1204 weakmap_entry_t **wm_ptr = get_weakmap_from_obj(this_val);
1205 if (!wm_ptr) return js_false;
1206 if (!is_object_type(args[0])) return js_false;
1207
1208 ant_value_t key_obj = args[0];
1209 weakmap_entry_t *entry;
1210 HASH_FIND(hh, *wm_ptr, &key_obj, sizeof(ant_value_t), entry);
1211 if (entry) {
1212 HASH_DEL(*wm_ptr, entry);
1213 free(entry);
1214 return js_true;
1215 }
1216 return js_false;
1217}
1218
1219static ant_value_t weakset_add(ant_t *js, ant_value_t *args, int nargs) {
1220 if (nargs < 1) return js_mkerr(js, "WeakSet.add() requires 1 argument");
1221
1222 ant_value_t this_val = js->this_val;
1223 weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val);
1224 if (!ws_ptr) return js_mkerr(js, "Invalid WeakSet object");
1225
1226 if (!is_object_type(args[0]))
1227 return js_mkerr(js, "WeakSet value must be an object");
1228
1229 ant_value_t value_obj = args[0];
1230
1231 weakset_entry_t *entry;
1232 HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry);
1233
1234 if (!entry) {
1235 entry = ant_calloc(sizeof(weakset_entry_t));
1236 if (!entry) return js_mkerr(js, "out of memory");
1237 entry->value_obj = value_obj;
1238 HASH_ADD(hh, *ws_ptr, value_obj, sizeof(ant_value_t), entry);
1239 }
1240
1241 return this_val;
1242}
1243
1244static ant_value_t weakset_has(ant_t *js, ant_value_t *args, int nargs) {
1245 if (nargs < 1) return js_mkerr(js, "WeakSet.has() requires 1 argument");
1246
1247 ant_value_t this_val = js->this_val;
1248 weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val);
1249 if (!ws_ptr) return js_false;
1250 if (!is_object_type(args[0])) return js_false;
1251
1252 ant_value_t value_obj = args[0];
1253 weakset_entry_t *entry;
1254 HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry);
1255 return js_bool(entry != NULL);
1256}
1257
1258static ant_value_t weakset_delete(ant_t *js, ant_value_t *args, int nargs) {
1259 if (nargs < 1) return js_mkerr(js, "WeakSet.delete() requires 1 argument");
1260
1261 ant_value_t this_val = js->this_val;
1262 weakset_entry_t **ws_ptr = get_weakset_from_obj(this_val);
1263 if (!ws_ptr) return js_false;
1264 if (!is_object_type(args[0])) return js_false;
1265
1266 ant_value_t value_obj = args[0];
1267 weakset_entry_t *entry;
1268 HASH_FIND(hh, *ws_ptr, &value_obj, sizeof(ant_value_t), entry);
1269
1270 if (entry) {
1271 HASH_DEL(*ws_ptr, entry);
1272 free(entry);
1273 return js_true;
1274 }
1275 return js_false;
1276}
1277
1278static ant_value_t builtin_WeakRef(ant_t *js, ant_value_t *args, int nargs) {
1279 if (nargs < 1 || !is_object_type(args[0])) {
1280 return js_mkerr(js, "WeakRef target must be an object");
1281 }
1282
1283 ant_value_t wr_obj = js_mkobj(js);
1284 ant_value_t wr_proto = js_get_ctor_proto(js, "WeakRef", 7);
1285 if (is_special_object(wr_proto)) js_set_proto_init(wr_obj, wr_proto);
1286 js_set_slot(wr_obj, SLOT_DATA, args[0]);
1287
1288 return wr_obj;
1289}
1290
1291static ant_value_t weakref_deref(ant_t *js, ant_value_t *args, int nargs) {
1292 (void)args; (void)nargs;
1293 ant_value_t this_val = js->this_val;
1294 if (vtype(this_val) != T_OBJ) return js_mkundef();
1295
1296 ant_value_t target = js_get_slot(this_val, SLOT_DATA);
1297 if (vtype(target) != T_OBJ) return js_mkundef();
1298
1299 return target;
1300}
1301
1302static ant_value_t builtin_FinalizationRegistry(ant_t *js, ant_value_t *args, int nargs) {
1303 if (nargs < 1 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) {
1304 return js_mkerr(js, "FinalizationRegistry callback must be a function");
1305 }
1306
1307 ant_value_t fr_obj = js_mkobj(js);
1308 ant_value_t fr_proto = js_get_ctor_proto(js, "FinalizationRegistry", 20);
1309 if (is_special_object(fr_proto)) js_set_proto_init(fr_obj, fr_proto);
1310
1311 js_set_slot(fr_obj, SLOT_MAP, js_mkarr(js));
1312 js_set_slot(fr_obj, SLOT_DATA, args[0]);
1313
1314 return fr_obj;
1315}
1316
1317static ant_value_t finreg_register(ant_t *js, ant_value_t *args, int nargs) {
1318 ant_value_t this_val = js->this_val;
1319 if (vtype(this_val) != T_OBJ) return js_mkundef();
1320
1321 if (nargs < 1 || vtype(args[0]) != T_OBJ) {
1322 return js_mkerr(js, "FinalizationRegistry.register target must be an object");
1323 }
1324
1325 ant_value_t target = args[0];
1326 ant_value_t held_value = nargs > 1 ? args[1] : js_mkundef();
1327 ant_value_t unregister_token = nargs > 2 ? args[2] : js_mkundef();
1328
1329 if (vdata(target) == vdata(held_value) && vtype(held_value) == T_OBJ) {
1330 return js_mkerr(js, "target and held value must not be the same");
1331 }
1332
1333 ant_value_t registrations = js_get_slot(this_val, SLOT_MAP);
1334 if (vtype(registrations) != T_ARR) return js_mkundef();
1335
1336 ant_value_t entry = js_mkarr(js);
1337 ant_offset_t len = js_arr_len(js, registrations);
1338
1339 char idx[16];
1340 size_t idx_len = uint_to_str(idx, sizeof(idx), 0);
1341 js_setprop(js, entry, js_mkstr(js, idx, idx_len), target);
1342 idx_len = uint_to_str(idx, sizeof(idx), 1);
1343 js_setprop(js, entry, js_mkstr(js, idx, idx_len), held_value);
1344 idx_len = uint_to_str(idx, sizeof(idx), 2);
1345 js_setprop(js, entry, js_mkstr(js, idx, idx_len), unregister_token);
1346 js_setprop(js, entry, js->length_str, tov(3.0));
1347
1348 idx_len = uint_to_str(idx, sizeof(idx), len);
1349 js_setprop(js, registrations, js_mkstr(js, idx, idx_len), entry);
1350 js_setprop(js, registrations, js->length_str, tov((double)(len + 1)));
1351
1352 return js_mkundef();
1353}
1354
1355static ant_value_t finreg_unregister(ant_t *js, ant_value_t *args, int nargs) {
1356 ant_value_t this_val = js->this_val;
1357 if (vtype(this_val) != T_OBJ) return js_false;
1358
1359 if (nargs < 1 || vtype(args[0]) != T_OBJ) {
1360 return js_mkerr(js, "FinalizationRegistry.unregister token must be an object");
1361 }
1362
1363 ant_value_t token = args[0];
1364 ant_value_t registrations = js_get_slot(this_val, SLOT_MAP);
1365 if (vtype(registrations) != T_ARR) return js_false;
1366
1367 ant_offset_t len = js_arr_len(js, registrations);
1368 bool removed = false;
1369
1370 for (ant_offset_t i = 0; i < len; i++) {
1371 ant_value_t entry = js_arr_get(js, registrations, i);
1372 if (vtype(entry) != T_ARR) continue;
1373 ant_value_t entry_token = js_arr_get(js, entry, 2);
1374 if (vtype(entry_token) == T_OBJ && vdata(entry_token) == vdata(token)) {
1375 char idx[16];
1376 size_t idx_len = uint_to_str(idx, sizeof(idx), i);
1377 js_setprop(js, registrations, js_mkstr(js, idx, idx_len), js_mkundef());
1378 removed = true;
1379 }
1380 }
1381
1382 return js_bool(removed);
1383}
1384
1385static ant_value_t map_groupBy(ant_t *js, ant_value_t *args, int nargs) {
1386 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Map.groupBy requires 2 arguments");
1387
1388 ant_value_t items = args[0];
1389 ant_value_t callback = args[1];
1390
1391 if (vtype(callback) != T_FUNC && vtype(callback) != T_CFUNC)
1392 return js_mkerr_typed(js, JS_ERR_TYPE, "callback is not a function");
1393
1394 ant_value_t map_obj = js_mkobj(js);
1395 js_obj_ptr(map_obj)->type_tag = T_MAP;
1396
1397 ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3);
1398 if (is_special_object(map_proto)) js_set_proto_init(map_obj, map_proto);
1399
1400 map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *));
1401 if (!map_head) return js_mkerr(js, "out of memory");
1402 *map_head = NULL;
1403 js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head));
1404
1405 ant_offset_t len = js_arr_len(js, items);
1406 for (ant_offset_t i = 0; i < len; i++) {
1407 ant_value_t val = js_arr_get(js, items, i);
1408 ant_value_t cb_args[2] = { val, tov((double)i) };
1409
1410 ant_value_t key = normalize_map_key(
1411 sv_vm_call(js->vm, js, callback,
1412 js_mkundef(), cb_args, 2, NULL, false)
1413 );
1414
1415 if (is_err(key)) return key;
1416 map_entry_t *entry = map_find_entry(js, map_head, key);
1417 ant_value_t group;
1418
1419 if (entry) group = entry->value; else {
1420 group = js_mkarr(js);
1421 if (!map_store_entry(js, map_head, key, key, group)) return js_mkerr(js, "out of memory");
1422 }
1423
1424 js_arr_push(js, group, val);
1425 }
1426
1427 return map_obj;
1428}
1429
1430static ant_value_t builtin_Map(ant_t *js, ant_value_t *args, int nargs) {
1431 if (vtype(js->new_target) == T_UNDEF) {
1432 return js_mkerr_typed(js, JS_ERR_TYPE, "Map constructor requires 'new'");
1433 }
1434
1435 ant_value_t map_obj = js->this_val;
1436 if (vtype(map_obj) != T_OBJ) map_obj = js_mkobj(js);
1437 if (is_err(map_obj)) return map_obj;
1438 js_obj_ptr(map_obj)->type_tag = T_MAP;
1439
1440 ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3);
1441 ant_value_t instance_proto = js_instance_proto_from_new_target(js, map_proto);
1442
1443 if (is_special_object(instance_proto)) js_set_proto_init(map_obj, instance_proto);
1444
1445 map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *));
1446 if (!map_head) return js_mkerr(js, "out of memory");
1447 *map_head = NULL;
1448
1449 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC)
1450 js_set_slot(map_obj, SLOT_CTOR, js->new_target);
1451 js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head));
1452
1453 if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return map_obj;
1454 ant_value_t init_result = map_init_from_iterable(js, map_head, args[0]);
1455 if (is_err(init_result)) return init_result;
1456
1457 return map_obj;
1458}
1459
1460static ant_value_t builtin_Set(ant_t *js, ant_value_t *args, int nargs) {
1461 if (vtype(js->new_target) == T_UNDEF) {
1462 return js_mkerr_typed(js, JS_ERR_TYPE, "Set constructor requires 'new'");
1463 }
1464
1465 ant_value_t set_obj = js->this_val;
1466 if (vtype(set_obj) != T_OBJ) set_obj = js_mkobj(js);
1467 if (is_err(set_obj)) return set_obj;
1468 js_obj_ptr(set_obj)->type_tag = T_SET;
1469
1470 ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3);
1471 ant_value_t instance_proto = js_instance_proto_from_new_target(js, set_proto);
1472
1473 if (is_special_object(instance_proto)) js_set_proto_init(set_obj, instance_proto);
1474
1475 set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *));
1476 if (!set_head) return js_mkerr(js, "out of memory");
1477 *set_head = NULL;
1478
1479 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC)
1480 js_set_slot(set_obj, SLOT_CTOR, js->new_target);
1481 js_set_slot(set_obj, SLOT_DATA, ANT_PTR(set_head));
1482
1483 if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return set_obj;
1484 ant_value_t init_result = set_init_from_iterable(js, set_head, args[0]);
1485 if (is_err(init_result)) return init_result;
1486
1487 return set_obj;
1488}
1489
1490static ant_value_t builtin_WeakMap(ant_t *js, ant_value_t *args, int nargs) {
1491 if (vtype(js->new_target) == T_UNDEF) {
1492 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap constructor requires 'new'");
1493 }
1494
1495 ant_value_t wm_obj = js->this_val;
1496 if (vtype(wm_obj) != T_OBJ) wm_obj = js_mkobj(js);
1497 if (is_err(wm_obj)) return wm_obj;
1498 js_obj_ptr(wm_obj)->type_tag = T_WEAKMAP;
1499
1500 ant_value_t wm_proto = js_get_ctor_proto(js, "WeakMap", 7);
1501 ant_value_t instance_proto = js_instance_proto_from_new_target(js, wm_proto);
1502
1503 if (is_special_object(instance_proto)) js_set_proto_init(wm_obj, instance_proto);
1504
1505 weakmap_entry_t **wm_head = ant_calloc(sizeof(weakmap_entry_t *));
1506 if (!wm_head) return js_mkerr(js, "out of memory");
1507 *wm_head = NULL;
1508
1509 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC)
1510 js_set_slot(wm_obj, SLOT_CTOR, js->new_target);
1511 js_set_slot(wm_obj, SLOT_DATA, ANT_PTR(wm_head));
1512
1513 if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return wm_obj;
1514 ant_value_t init_result = weakmap_init_from_iterable(js, wm_head, args[0]);
1515 if (is_err(init_result)) return init_result;
1516
1517 return wm_obj;
1518}
1519
1520static ant_value_t builtin_WeakSet(ant_t *js, ant_value_t *args, int nargs) {
1521 if (vtype(js->new_target) == T_UNDEF) {
1522 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakSet constructor requires 'new'");
1523 }
1524
1525 ant_value_t ws_obj = js->this_val;
1526 if (vtype(ws_obj) != T_OBJ) ws_obj = js_mkobj(js);
1527 if (is_err(ws_obj)) return ws_obj;
1528 js_obj_ptr(ws_obj)->type_tag = T_WEAKSET;
1529
1530 ant_value_t ws_proto = js_get_ctor_proto(js, "WeakSet", 7);
1531 ant_value_t instance_proto = js_instance_proto_from_new_target(js, ws_proto);
1532
1533 if (is_special_object(instance_proto)) js_set_proto_init(ws_obj, instance_proto);
1534
1535 weakset_entry_t **ws_head = ant_calloc(sizeof(weakset_entry_t *));
1536 if (!ws_head) return js_mkerr(js, "out of memory");
1537 *ws_head = NULL;
1538
1539 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC)
1540 js_set_slot(ws_obj, SLOT_CTOR, js->new_target);
1541 js_set_slot(ws_obj, SLOT_DATA, ANT_PTR(ws_head));
1542
1543 if (nargs == 0 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) return ws_obj;
1544 ant_value_t init_result = weakset_init_from_iterable(js, ws_head, args[0]);
1545 if (is_err(init_result)) return init_result;
1546
1547 return ws_obj;
1548}
1549
1550void init_collections_module(void) {
1551 ant_t *js = rt->js;
1552
1553 ant_value_t glob = js->global;
1554 ant_value_t object_proto = js->sym.object_proto;
1555
1556 ant_value_t iter_sym = get_iterator_sym();
1557 ant_value_t tag_sym = get_toStringTag_sym();
1558
1559 g_map_iter_proto = js_mkobj(js);
1560 js_set_proto_init(g_map_iter_proto, js->sym.iterator_proto);
1561 js_set(js, g_map_iter_proto, "next", js_mkfun(map_iter_next));
1562 js_iter_register_advance(g_map_iter_proto, advance_map);
1563
1564 g_set_iter_proto = js_mkobj(js);
1565 js_set_proto_init(g_set_iter_proto, js->sym.iterator_proto);
1566 js_set(js, g_set_iter_proto, "next", js_mkfun(set_iter_next));
1567 js_iter_register_advance(g_set_iter_proto, advance_set);
1568
1569 ant_value_t map_proto = js_mkobj(js);
1570 js_set_proto_init(map_proto, object_proto);
1571 js_set(js, map_proto, "set", js_mkfun(map_set));
1572 js_set(js, map_proto, "get", js_mkfun(map_get));
1573 js_set(js, map_proto, "has", js_mkfun(map_has));
1574 js_set(js, map_proto, "upsert", js_mkfun(map_upsert));
1575 js_set(js, map_proto, "delete", js_mkfun(map_delete));
1576 js_set(js, map_proto, "clear", js_mkfun(map_clear));
1577 js_set_getter_desc(js, map_proto, "size", 4, js_mkfun(map_size), JS_DESC_C);
1578 js_set(js, map_proto, "entries", js_mkfun(map_entries));
1579 js_set(js, map_proto, "keys", js_mkfun(map_keys));
1580 js_set(js, map_proto, "values", js_mkfun(map_values));
1581 js_set(js, map_proto, "forEach", js_mkfun(map_forEach));
1582 js_set_sym(js, map_proto, iter_sym, js_get(js, map_proto, "entries"));
1583 js_set_sym(js, map_proto, tag_sym, js_mkstr(js, "Map", 3));
1584
1585 ant_value_t map_ctor = js_mkobj(js);
1586 js_set_slot(map_ctor, SLOT_CFUNC, js_mkfun(builtin_Map));
1587 js_mkprop_fast(js, map_ctor, "prototype", 9, map_proto);
1588 js_mkprop_fast(js, map_ctor, "name", 4, ANT_STRING("Map"));
1589 js_set_descriptor(js, map_ctor, "name", 4, 0);
1590 js_set(js, map_ctor, "groupBy", js_mkfun(map_groupBy));
1591 js_define_species_getter(js, map_ctor);
1592 js_set(js, glob, "Map", js_obj_to_func(map_ctor));
1593
1594 ant_value_t set_proto = js_mkobj(js);
1595 js_set_proto_init(set_proto, object_proto);
1596 js_set(js, set_proto, "add", js_mkfun(set_add));
1597 js_set(js, set_proto, "has", js_mkfun(set_has));
1598 js_set(js, set_proto, "delete", js_mkfun(set_delete));
1599 js_set(js, set_proto, "clear", js_mkfun(set_clear));
1600 js_set_getter_desc(js, set_proto, "size", 4, js_mkfun(set_size), JS_DESC_C);
1601 js_set(js, set_proto, "values", js_mkfun(set_values));
1602 js_set_exact(js, set_proto, "keys", js_get(js, set_proto, "values"));
1603 js_set(js, set_proto, "entries", js_mkfun(set_entries));
1604 js_set(js, set_proto, "forEach", js_mkfun(set_forEach));
1605 js_set(js, set_proto, "union", js_mkfun(set_union));
1606 js_set(js, set_proto, "intersection", js_mkfun(set_intersection));
1607 js_set(js, set_proto, "difference", js_mkfun(set_difference));
1608 js_set(js, set_proto, "symmetricDifference", js_mkfun(set_symmetricDifference));
1609 js_set(js, set_proto, "isSubsetOf", js_mkfun(set_isSubsetOf));
1610 js_set(js, set_proto, "isSupersetOf", js_mkfun(set_isSupersetOf));
1611 js_set(js, set_proto, "isDisjointFrom", js_mkfun(set_isDisjointFrom));
1612 js_set_sym(js, set_proto, iter_sym, js_get(js, set_proto, "values"));
1613 js_set_sym(js, set_proto, tag_sym, js_mkstr(js, "Set", 3));
1614
1615 ant_value_t set_ctor = js_mkobj(js);
1616 js_set_slot(set_ctor, SLOT_CFUNC, js_mkfun(builtin_Set));
1617 js_mkprop_fast(js, set_ctor, "prototype", 9, set_proto);
1618 js_mkprop_fast(js, set_ctor, "name", 4, ANT_STRING("Set"));
1619 js_set_descriptor(js, set_ctor, "name", 4, 0);
1620 js_define_species_getter(js, set_ctor);
1621 js_set(js, glob, "Set", js_obj_to_func(set_ctor));
1622
1623 ant_value_t weakmap_proto = js_mkobj(js);
1624 js_set_proto_init(weakmap_proto, object_proto);
1625 js_set(js, weakmap_proto, "set", js_mkfun(weakmap_set));
1626 js_set(js, weakmap_proto, "get", js_mkfun(weakmap_get));
1627 js_set(js, weakmap_proto, "has", js_mkfun(weakmap_has));
1628 js_set(js, weakmap_proto, "upsert", js_mkfun(weakmap_upsert));
1629 js_set(js, weakmap_proto, "delete", js_mkfun(weakmap_delete));
1630 js_set_sym(js, weakmap_proto, tag_sym, js_mkstr(js, "WeakMap", 7));
1631
1632 ant_value_t weakmap_ctor = js_mkobj(js);
1633 js_set_slot(weakmap_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakMap));
1634 js_mkprop_fast(js, weakmap_ctor, "prototype", 9, weakmap_proto);
1635 js_mkprop_fast(js, weakmap_ctor, "name", 4, ANT_STRING("WeakMap"));
1636 js_set_descriptor(js, weakmap_ctor, "name", 4, 0);
1637 js_set(js, glob, "WeakMap", js_obj_to_func(weakmap_ctor));
1638
1639 ant_value_t weakset_proto = js_mkobj(js);
1640 js_set_proto_init(weakset_proto, object_proto);
1641 js_set(js, weakset_proto, "add", js_mkfun(weakset_add));
1642 js_set(js, weakset_proto, "has", js_mkfun(weakset_has));
1643 js_set(js, weakset_proto, "delete", js_mkfun(weakset_delete));
1644 js_set_sym(js, weakset_proto, tag_sym, js_mkstr(js, "WeakSet", 7));
1645
1646 ant_value_t weakset_ctor = js_mkobj(js);
1647 js_set_slot(weakset_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakSet));
1648 js_mkprop_fast(js, weakset_ctor, "prototype", 9, weakset_proto);
1649 js_mkprop_fast(js, weakset_ctor, "name", 4, ANT_STRING("WeakSet"));
1650 js_set_descriptor(js, weakset_ctor, "name", 4, 0);
1651 js_set(js, glob, "WeakSet", js_obj_to_func(weakset_ctor));
1652
1653 ant_value_t weakref_proto = js_mkobj(js);
1654 js_set_proto_init(weakref_proto, object_proto);
1655 js_set(js, weakref_proto, "deref", js_mkfun(weakref_deref));
1656 js_set_sym(js, weakref_proto, tag_sym, js_mkstr(js, "WeakRef", 7));
1657
1658 ant_value_t weakref_ctor = js_mkobj(js);
1659 js_set_slot(weakref_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakRef));
1660 js_mkprop_fast(js, weakref_ctor, "prototype", 9, weakref_proto);
1661 js_mkprop_fast(js, weakref_ctor, "name", 4, ANT_STRING("WeakRef"));
1662 js_set_descriptor(js, weakref_ctor, "name", 4, 0);
1663 js_set(js, glob, "WeakRef", js_obj_to_func(weakref_ctor));
1664
1665 ant_value_t finreg_proto = js_mkobj(js);
1666 js_set_proto_init(finreg_proto, object_proto);
1667 js_set(js, finreg_proto, "register", js_mkfun(finreg_register));
1668 js_set(js, finreg_proto, "unregister", js_mkfun(finreg_unregister));
1669 js_set_sym(js, finreg_proto, tag_sym, js_mkstr(js, "FinalizationRegistry", 20));
1670
1671 ant_value_t finreg_ctor = js_mkobj(js);
1672 js_set_slot(finreg_ctor, SLOT_CFUNC, js_mkfun(builtin_FinalizationRegistry));
1673 js_mkprop_fast(js, finreg_ctor, "prototype", 9, finreg_proto);
1674 js_mkprop_fast(js, finreg_ctor, "name", 4, ANT_STRING("FinalizationRegistry"));
1675 js_set_descriptor(js, finreg_ctor, "name", 4, 0);
1676 js_set(js, glob, "FinalizationRegistry", js_obj_to_func(finreg_ctor));
1677}