MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#if defined(__GNUC__) && !defined(__clang__)
2 #pragma GCC optimize("O3,inline")
3#endif
4
5#include <compat.h> // IWYU pragma: keep
6
7#include "ant.h"
8#include "utf8.h"
9#include "ptr.h"
10#include "debug.h"
11#include "tokens.h"
12#include "common.h"
13#include "utils.h"
14#include "sugar.h"
15#include "base64.h"
16#include "runtime.h"
17#include "internal.h"
18#include "errors.h"
19#include "descriptors.h"
20#include "shapes.h"
21#include "numbers.h"
22
23#include "gc.h"
24#include "gc/objects.h"
25#include "gc/roots.h"
26
27#include "esm/remote.h"
28#include "esm/loader.h"
29#include "esm/exports.h"
30#include "esm/builtin_bundle.h"
31
32#include "silver/lexer.h"
33#include "silver/compiler.h"
34#include "silver/engine.h"
35#include "silver/ops/using.h"
36
37#include <uv.h>
38#include <assert.h>
39#include <pcre2.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <time.h>
45#include <utarray.h>
46#include <uthash.h>
47#include <float.h>
48#include <tlsuv/tlsuv.h>
49#include <tlsuv/http.h>
50#include <minicoro.h>
51
52#ifdef _WIN32
53#include <sys/stat.h>
54#else
55#include <sys/time.h>
56#include <sys/stat.h>
57#include <sys/resource.h>
58#endif
59
60#include "modules/atomics.h"
61#include "modules/bigint.h"
62#include "modules/timer.h"
63#include "modules/symbol.h"
64#include "modules/date.h"
65#include "modules/buffer.h"
66#include "modules/blob.h"
67#include "modules/collections.h"
68#include "modules/lmdb.h"
69#include "modules/regex.h"
70#include "modules/globals.h"
71
72#define D(x) ((double)(x))
73
74_Static_assert(sizeof(double) == 8, "NaN-boxing requires 64-bit IEEE 754 doubles");
75_Static_assert(sizeof(uint64_t) == 8, "NaN-boxing requires 64-bit integers");
76_Static_assert(sizeof(double) == sizeof(uint64_t), "double and uint64_t must have same size");
77
78#if defined(__STDC_IEC_559__) || defined(__GCC_IEC_559)
79#elif defined(__FAST_MATH__)
80 #error "NaN-boxing is incompatible with -ffast-math"
81#elif DBL_MANT_DIG != 53 || DBL_MAX_EXP != 1024
82 #error "NaN-boxing requires IEEE 754 binary64 doubles"
83#endif
84
85typedef struct interned_string {
86 uint64_t hash;
87 char *str;
88 size_t len;
89 struct interned_string *next;
90} interned_string_t;
91
92typedef struct {
93 uint32_t id;
94 uint32_t flags;
95 const char *key;
96 uint32_t desc_len;
97 char desc[];
98} ant_symbol_heap_t;
99
100static size_t intern_count = 0;
101static size_t intern_bytes = 0;
102
103static interned_string_t **intern_buckets = NULL;
104static size_t intern_bucket_count = 0;
105
106#define INTERN_BUCKET_MIN 1024u
107#define INTERN_LOAD_NUM 4u
108#define INTERN_LOAD_DEN 5u
109
110static bool intern_table_init(void) {
111 if (intern_buckets) return true;
112 intern_bucket_count = INTERN_BUCKET_MIN;
113 intern_buckets = ant_calloc(sizeof(*intern_buckets) * intern_bucket_count);
114 if (!intern_buckets) {
115 intern_bucket_count = 0;
116 return false;
117 }
118 return true;
119}
120
121static bool intern_table_rehash(size_t new_bucket_count) {
122 if (!intern_buckets || new_bucket_count < INTERN_BUCKET_MIN) return false;
123
124 interned_string_t **next = ant_calloc(sizeof(*next) * new_bucket_count);
125 if (!next) return false;
126
127 for (size_t i = 0; i < intern_bucket_count; i++) {
128 interned_string_t *entry = intern_buckets[i];
129 while (entry) {
130 interned_string_t *link = entry->next;
131 size_t bucket = (size_t)(entry->hash & (new_bucket_count - 1));
132 entry->next = next[bucket];
133 next[bucket] = entry;
134 entry = link;
135 }
136 }
137
138 free(intern_buckets);
139 intern_buckets = next;
140 intern_bucket_count = new_bucket_count;
141
142 return true;
143}
144
145static const UT_icd promise_handler_icd = {
146 .sz = sizeof(promise_handler_t),
147 .init = NULL,
148 .copy = NULL,
149 .dtor = NULL,
150};
151
152static uint32_t next_promise_id = 1;
153static uint32_t get_promise_id(ant_t *js, ant_value_t p);
154
155static ant_promise_state_t *get_promise_data(ant_t *js, ant_value_t promise, bool create);
156static ant_proxy_state_t *get_proxy_data(ant_value_t obj);
157
158static inline uint32_t promise_handler_count(const ant_promise_state_t *pd) {
159 return pd ? (uint32_t)pd->handler_count : 0;
160}
161
162static inline bool promise_has_handlers(const ant_promise_state_t *pd) {
163 return promise_handler_count(pd) != 0;
164}
165
166static bool promise_handler_append(ant_promise_state_t *pd, const promise_handler_t *handler) {
167 if (!pd || !handler) return false;
168
169 if (pd->handler_count == 0) {
170 pd->inline_handler = *handler;
171 pd->handler_count = 1;
172 return true;
173 }
174
175 if (pd->handler_count == 1) {
176 if (!pd->handlers) utarray_new(pd->handlers, &promise_handler_icd);
177 if (!pd->handlers) return false;
178 utarray_push_back(pd->handlers, &pd->inline_handler);
179 utarray_push_back(pd->handlers, handler);
180 pd->handler_count = 2;
181 return true;
182 }
183
184 if (!pd->handlers) utarray_new(pd->handlers, &promise_handler_icd);
185 if (!pd->handlers) return false;
186
187 utarray_push_back(pd->handlers, handler);
188 pd->handler_count++;
189
190 return true;
191}
192
193static inline promise_handler_t *promise_handler_at(ant_promise_state_t *pd, uint32_t index) {
194 if (!pd || index >= pd->handler_count) return NULL;
195 if (pd->handler_count == 1) return &pd->inline_handler;
196 if (!pd->handlers) return NULL;
197 return (promise_handler_t *)utarray_eltptr(pd->handlers, (unsigned int)index);
198}
199
200static inline void promise_handlers_clear(ant_promise_state_t *pd) {
201 if (!pd) return;
202 pd->handler_count = 0;
203 pd->inline_handler = (promise_handler_t){ 0 };
204 if (pd->handlers) utarray_clear(pd->handlers);
205}
206
207ant_value_t tov(double d) {
208 union { double d; ant_value_t v; } u = {d};
209 if (__builtin_expect(isnan(d), 0))
210 return (u.v > NANBOX_PREFIX)
211 ? 0x7FF8000000000000ULL : u.v; // canonical NaN
212 return u.v;
213}
214
215double tod(ant_value_t v) {
216 union { ant_value_t v; double d; } u = {v}; return u.d;
217}
218
219static bool is_tagged(ant_value_t v) {
220 return v > NANBOX_PREFIX;
221}
222
223size_t vdata(ant_value_t v) {
224 return (size_t)(v & NANBOX_DATA_MASK);
225}
226
227ant_object_t *js_obj_ptr(ant_value_t v) {
228 if (!is_object_type(v)) return NULL;
229 ant_value_t as_obj = js_as_obj(v);
230 return (ant_object_t *)(uintptr_t)vdata(as_obj);
231}
232
233ant_value_t js_obj_from_ptr(ant_object_t *obj) {
234 if (!obj) return js_mkundef();
235 return mkval(T_OBJ, (uintptr_t)obj);
236}
237
238void js_mark_constructor(ant_value_t value, bool is_constructor) {
239 ant_object_t *obj = js_obj_ptr(value);
240 if (obj) obj->is_constructor = is_constructor ? 1u : 0u;
241}
242
243static ant_value_t obj_extra_get(ant_object_t *obj, internal_slot_t slot) {
244 ant_extra_slot_t *entry = ant_object_extra_slot(obj, (uint8_t)slot);
245 return entry ? entry->value : js_mkundef();
246}
247
248static bool obj_extra_set(ant_object_t *obj, internal_slot_t slot, ant_value_t value) {
249 if (!obj) return false;
250
251 ant_extra_slot_t *entry = ant_object_extra_slot(obj, (uint8_t)slot);
252 if (entry) {
253 entry->value = value;
254 return true;
255 }
256
257 uint8_t count = 0;
258 ant_extra_slot_t *entries = ant_object_extra_slots(obj, &count);
259
260 if (count == UINT8_MAX) return false;
261 uint8_t next_count = (uint8_t)(count + 1);
262
263 ant_extra_slot_t *next = realloc(entries, sizeof(*next) * next_count);
264 if (!next) return false;
265
266 next[count].slot = (uint8_t)slot;
267 next[count].value = value;
268 ant_object_sidecar_t *sidecar = ant_object_sidecar(obj);
269
270 if (sidecar) {
271 sidecar->extra_slots = next;
272 sidecar->extra_count = next_count;
273 } else {
274 obj->extra_slots = next;
275 obj->extra_count = next_count;
276 }
277
278 return true;
279}
280
281static ant_offset_t propref_make(ant_t *js, ant_object_t *obj, uint32_t slot) {
282 if (!js || !obj) return 0;
283
284 if (js->prop_refs_len >= js->prop_refs_cap) {
285 ant_offset_t next_cap = js->prop_refs_cap ? js->prop_refs_cap * 2 : 256;
286 ant_prop_ref_t *next = realloc(js->prop_refs, sizeof(*next) * next_cap);
287 if (!next) return 0;
288 js->prop_refs = next;
289 js->prop_refs_cap = next_cap;
290 }
291
292 ant_offset_t handle = js->prop_refs_len + 1;
293 js->prop_refs[js->prop_refs_len++] = (ant_prop_ref_t){
294 .obj = obj,
295 .slot = slot,
296 .valid = true,
297 };
298 obj->propref_count++;
299 return handle;
300}
301
302static ant_prop_ref_t *propref_get(ant_t *js, ant_offset_t handle) {
303 if (!js || handle == 0 || handle > js->prop_refs_len) return NULL;
304 ant_prop_ref_t *ref = &js->prop_refs[handle - 1];
305 return ref->valid ? ref : NULL;
306}
307
308static inline ant_value_t propref_load(ant_t *js, ant_offset_t handle) {
309 ant_prop_ref_t *ref = propref_get(js, handle);
310 if (!ref || !ref->obj || ref->slot >= ref->obj->prop_count) return js_mkundef();
311 return ant_object_prop_get_unchecked(ref->obj, ref->slot);
312}
313
314static inline bool propref_store(ant_t *js, ant_offset_t handle, ant_value_t value) {
315 ant_prop_ref_t *ref = propref_get(js, handle);
316 if (!ref || !ref->obj || ref->slot >= ref->obj->prop_count) return false;
317 ant_object_prop_set_unchecked(ref->obj, ref->slot, value);
318 gc_write_barrier(js, ref->obj, value);
319 return true;
320}
321
322static void propref_adjust_after_swap_delete(ant_t *js, ant_object_t *obj, uint32_t deleted_slot, uint32_t swapped_from) {
323 if (!js || !obj || obj->propref_count == 0) return;
324
325 for (ant_offset_t i = js->prop_refs_len; i-- > 0;) {
326 ant_prop_ref_t *ref = &js->prop_refs[i];
327 if (!ref->valid || ref->obj != obj) continue;
328
329 if (ref->slot == deleted_slot) {
330 ref->valid = false;
331 obj->propref_count--;
332 if (obj->propref_count == 0) return;
333 } else if (ref->slot == swapped_from) ref->slot = deleted_slot;
334 }
335}
336
337bool js_obj_ensure_prop_capacity(ant_object_t *obj, uint32_t needed) {
338 if (!obj) return false;
339 uint32_t inobj_limit = ant_object_inobj_limit(obj);
340 uint32_t old_count = obj->prop_count;
341 if (needed <= obj->prop_count) return true;
342
343 if (needed > inobj_limit) {
344 uint32_t overflow_needed = needed - inobj_limit;
345 if (overflow_needed > obj->overflow_cap) {
346 uint32_t new_cap = obj->overflow_cap ? (uint32_t)obj->overflow_cap * 2 : 4;
347 while (new_cap < overflow_needed) new_cap *= 2;
348
349 if (new_cap > 255) new_cap = overflow_needed;
350 ant_value_t *next = realloc(obj->overflow_prop, sizeof(*next) * new_cap);
351 if (!next) return false;
352
353 obj->overflow_prop = next;
354 obj->overflow_cap = (uint8_t)new_cap;
355 }
356 } else if (obj->overflow_prop) {
357 free(obj->overflow_prop);
358 obj->overflow_prop = NULL;
359 obj->overflow_cap = 0;
360 }
361
362 obj->prop_count = needed;
363 for (uint32_t i = old_count; i < needed; i++) {
364 ant_object_prop_set_unchecked(obj, i, js_mkundef());
365 }
366 return true;
367}
368
369bool js_obj_ensure_unique_shape(ant_object_t *obj) {
370 if (!obj || !obj->shape) return false;
371 if (!ant_shape_is_shared(obj->shape)) return true;
372
373 ant_shape_t *copy = ant_shape_clone(obj->shape);
374 if (!copy) return false;
375
376 ant_shape_release(obj->shape);
377 obj->shape = copy;
378 return true;
379}
380
381static void obj_remove_prop_slot(ant_object_t *obj, uint32_t slot) {
382 if (!obj || slot >= obj->prop_count) return;
383 uint32_t last = obj->prop_count - 1;
384 if (slot != last) {
385 ant_object_prop_set_unchecked(obj, slot, ant_object_prop_get_unchecked(obj, last));
386 }
387 obj->prop_count--;
388}
389
390static ant_exotic_ops_t *obj_ensure_exotic_ops(ant_object_t *obj) {
391 if (!obj) return NULL;
392 if (!obj->exotic_ops) {
393 ant_exotic_ops_t *ops = calloc(1, sizeof(*ops));
394 if (!ops) return NULL;
395 obj->exotic_ops = ops;
396 }
397 return (ant_exotic_ops_t *)(void *)obj->exotic_ops;
398}
399
400static ant_object_t *obj_alloc(ant_t *js, uint8_t type_tag, uint8_t inobj_limit) {
401 size_t threshold = gc_live_major_threshold(js);
402 if (js->obj_arena.live_count >= threshold) gc_run(js);
403
404 ant_object_t *obj = (ant_object_t *)fixed_arena_alloc(&js->obj_arena);
405 if (!obj) return NULL;
406
407 obj->type_tag = type_tag;
408 obj->proto = js_mkundef();
409 obj->u.data.value = js_mkundef();
410
411 obj->shape = ant_shape_new_with_inobj_limit(inobj_limit);
412 if (!obj->shape) {
413 obj->mark_epoch = ANT_GC_DEAD;
414 fixed_arena_free_elem(&js->obj_arena, obj);
415 return NULL;
416 }
417 obj->inobj_limit = ant_shape_get_inobj_limit(obj->shape);
418
419 obj->overflow_prop = NULL;
420 obj->overflow_cap = 0;
421 obj->prop_count = 0;
422 obj->propref_count = 0;
423
424 for (uint32_t i = 0; i < ANT_INOBJ_MAX_SLOTS; i++)
425 obj->inobj[i] = js_mkundef();
426
427 obj->exotic_ops = NULL;
428 obj->exotic_keys = NULL;
429 obj->promise_state = NULL;
430 obj->extra_slots = NULL;
431 obj->extra_count = 0;
432
433 obj->finalizer = NULL;
434 obj->native.ptr = NULL;
435 obj->native.tag = 0;
436
437 obj->mark_epoch = 0;
438 obj->extensible = 1;
439 obj->frozen = 0;
440 obj->sealed = 0;
441 obj->is_exotic = 0;
442 obj->is_constructor = 0;
443 obj->fast_array = 0;
444 obj->may_have_holes = 0;
445 obj->may_have_dense_elements = 0;
446 obj->gc_permanent = 0;
447 obj->generation = 0;
448 obj->in_remember_set = 0;
449
450 obj->next = js->objects;
451 js->objects = obj;
452
453 return obj;
454}
455
456static ant_value_t get_slot(ant_value_t obj, internal_slot_t slot);
457static void set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value);
458
459static ant_value_t get_proto(ant_t *js, ant_value_t obj);
460static void set_proto(ant_t *js, ant_value_t obj, ant_value_t proto);
461
462const char *typestr(uint8_t t) {
463 static const char *names[] = {
464 [T_UNDEF] = "undefined",
465 [T_NULL] = "object",
466 [T_BOOL] = "boolean",
467 [T_NUM] = "number",
468 [T_BIGINT] = "bigint",
469 [T_STR] = "string",
470 [T_SYMBOL] = "symbol",
471 [T_OBJ] = "object",
472 [T_ARR] = "object",
473 [T_FUNC] = "function",
474 [T_CFUNC] = "function",
475 [T_PROMISE] = "object",
476 [T_GENERATOR] = "object",
477 [T_TYPEDARRAY] = "typedarray",
478 [T_ERR] = "err",
479 [T_NTARG] = "ntarg"
480 };
481
482 return (t < sizeof(names) / sizeof(names[0])) ? names[t] : "??";
483}
484
485uint8_t vtype(ant_value_t v) {
486 return is_tagged(v) ? ((v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK) : (uint8_t)T_NUM;
487}
488
489ant_value_t mkval(uint8_t type, uint64_t data) {
490 return NANBOX_PREFIX
491 | ((ant_value_t)(type & NANBOX_TYPE_MASK) << NANBOX_TYPE_SHIFT)
492 | (data & NANBOX_DATA_MASK);
493}
494
495ant_value_t js_obj_to_func_ex(ant_value_t obj, uint8_t flags) {
496 ant_t *js = rt->js;
497
498 sv_closure_t *closure = js_closure_alloc(js);
499 if (!closure) return mkval(T_ERR, 0);
500
501 closure->func_obj = (vtype(obj) == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj));
502 closure->bound_this = js_mkundef();
503 closure->bound_args = js_mkundef();
504 closure->super_val = js_mkundef();
505 closure->call_flags = flags;
506
507 ant_object_t *func_obj = js_obj_ptr(closure->func_obj);
508 if (func_obj) {
509 if (flags & SV_CALL_IS_DEFAULT_CTOR) func_obj->is_constructor = 1;
510 // mark native function objects as constructors when they are
511 // created with an explicit .prototype own property.
512 else if (
513 !func_obj->is_constructor &&
514 func_obj->shape &&
515 js->intern.prototype &&
516 vtype(obj_extra_get(func_obj, SLOT_CFUNC)) == T_CFUNC
517 ) if (ant_shape_lookup_interned(func_obj->shape, js->intern.prototype) >= 0
518 ) func_obj->is_constructor = 1;
519 }
520
521 return mkval(T_FUNC, (uintptr_t)closure);
522}
523
524ant_value_t js_obj_to_func(ant_value_t obj) {
525 return js_obj_to_func_ex(obj, 0);
526}
527
528ant_value_t js_mktypedarray(void *data) {
529 return mkval(T_TYPEDARRAY, (uintptr_t)data);
530}
531
532void *js_gettypedarray(ant_value_t val) {
533 if (vtype(val) != T_TYPEDARRAY) return NULL;
534 return (void *)vdata(val);
535}
536
537ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot) {
538 return get_slot(js_as_obj(obj), slot);
539}
540
541typedef enum {
542 NTARG_INVALID = 0,
543 NTARG_NEW_TARGET = 1
544} ntarg_kind_t;
545
546static inline bool is_unboxed_obj(ant_t *js, ant_value_t val, ant_value_t expected_proto) {
547 if (vtype(val) != T_OBJ) return false;
548 if (vtype(get_slot(val, SLOT_PRIMITIVE)) != T_UNDEF) return false;
549 ant_value_t proto = get_slot(val, SLOT_PROTO);
550 return vdata(proto) == vdata(expected_proto);
551}
552
553uint32_t js_to_uint32(double d) {
554 if (!isfinite(d) || d == 0) return 0;
555 double sign = (d < 0) ? -1.0 : 1.0;
556 double posInt = sign * floor(fabs(d));
557 double val = fmod(posInt, 4294967296.0);
558 if (val < 0) val += 4294967296.0;
559 return (uint32_t) val;
560}
561
562int32_t js_to_int32(double d) {
563 uint32_t uint32 = js_to_uint32(d);
564 if (uint32 >= 2147483648U) return (int32_t)(uint32 - 4294967296.0);
565 return (int32_t) uint32;
566}
567
568static size_t strstring(ant_t *js, ant_value_t value, char *buf, size_t len);
569static size_t strkey(ant_t *js, ant_value_t value, char *buf, size_t len);
570
571ant_offset_t vstrlen(ant_t *js, ant_value_t v) {
572 if (str_is_heap_rope(v)) {
573 ant_rope_heap_t *rope = ant_str_rope_ptr(v);
574 return rope ? rope->len : 0;
575 }
576 if (str_is_heap_builder(v)) {
577 ant_string_builder_t *builder = ant_str_builder_ptr(v);
578 return builder ? builder->len : 0;
579 }
580 ant_flat_string_t *flat = ant_str_flat_ptr(v);
581 return flat ? flat->len : 0;
582}
583
584static ant_value_t make_data_cfunc(
585 ant_t *js, ant_value_t data,
586 ant_value_t (*fn)(ant_t *, ant_value_t *, int)
587);
588
589static ant_value_t proxy_read_target(ant_t *js, ant_value_t obj);
590static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj);
591static ant_value_t proxy_aware_get_elem(ant_t *js, ant_value_t obj, const char *key, size_t key_len);
592
593static ant_offset_t get_dense_buf(ant_value_t arr);
594static ant_offset_t dense_capacity(ant_offset_t doff);
595static ant_offset_t get_array_length(ant_t *js, ant_value_t arr);
596static ant_value_t arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx);
597static bool arr_has(ant_t *js, ant_value_t arr, ant_offset_t idx);
598
599static bool streq(const char *buf, size_t len, const char *p, size_t n);
600static bool parse_func_params(ant_t *js, uint8_t *flags, int *out_count);
601static bool try_dynamic_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value);
602static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out);
603
604static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type);
605static ant_value_t js_cfunc_name_value(ant_t *js, ant_value_t cfunc);
606static ant_value_t js_cfunc_length_value(ant_value_t cfunc);
607static bool js_cfunc_has_prototype(ant_value_t cfunc);
608static ant_value_t setup_func_prototype_property(ant_t *js, ant_value_t func, bool mark_constructor);
609static ant_value_t js_expose_cfunc_for_key(ant_t *js, ant_value_t value, const char *key, size_t key_len);
610
611static inline ant_value_t lkp_val(ant_t *js, ant_value_t obj, const char *buf, size_t len);
612static inline ant_value_t lkp_sym_proto_val(ant_t *js, ant_value_t obj, ant_offset_t sym_off);
613static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len);
614static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len);
615
616static ant_value_t js_call_valueOf(ant_t *js, ant_value_t value);
617static ant_value_t js_call_toString(ant_t *js, ant_value_t value);
618static ant_value_t js_call_method(ant_t *js, ant_value_t obj, const char *method, ant_value_t *args, int nargs);
619static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, int nargs);
620
621static inline bool is_slot_prop(ant_offset_t header);
622static inline ant_offset_t next_prop(ant_offset_t header);
623
624static ant_value_t builtin_promise_then(ant_t *js, ant_value_t *args, int nargs);
625static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size_t key_len);
626static ant_value_t proxy_get_val(ant_t *js, ant_value_t proxy, ant_value_t key_val);
627static ant_value_t proxy_get_prototype_of(ant_t *js, ant_value_t proxy);
628static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size_t key_len, ant_value_t value);
629static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size_t key_len);
630static ant_value_t proxy_has_val(ant_t *js, ant_value_t proxy, ant_value_t key_val);
631static ant_value_t proxy_get_own_property_descriptor(ant_t *js, ant_value_t proxy, ant_value_t key_val);
632static ant_value_t proxy_has_own(ant_t *js, ant_value_t proxy, ant_value_t key_val);
633static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, size_t key_len);
634static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t key_val);
635static ant_value_t proxy_define_property(ant_t *js, ant_value_t proxy, ant_value_t key_val, ant_value_t descriptor);
636
637static ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len);
638static inline void array_len_set(ant_t *js, ant_value_t obj, ant_offset_t new_len);
639
640typedef struct { ant_value_t handle; bool is_new; } ctor_t;
641
642static ctor_t get_constructor(ant_t *js, const char *name, size_t len) {
643 ctor_t ctor;
644
645 ctor.handle = get_ctor_proto(js, name, len);
646 ctor.is_new = (vtype(js->new_target) != T_UNDEF);
647
648 return ctor;
649}
650
651ant_value_t unwrap_primitive(ant_t *js, ant_value_t val) {
652 if (__builtin_expect(vtype(val) != T_OBJ, 1)) return val;
653 ant_value_t prim = get_slot(val, SLOT_PRIMITIVE);
654 if (__builtin_expect(vtype(prim) == T_UNDEF, 1)) return val;
655 return prim;
656}
657
658static ant_value_t to_string_val(ant_t *js, ant_value_t val) {
659 uint8_t t = vtype(val);
660 if (t == T_STR) return val;
661 if (t == T_OBJ) {
662 ant_value_t prim = get_slot(val, SLOT_PRIMITIVE);
663 if (vtype(prim) == T_STR) return prim;
664 }
665 return js_call_toString(js, val);
666}
667
668bool js_truthy(ant_t *js, ant_value_t v) {
669 static const void *dispatch[] = {
670 [T_OBJ] = &&l_true,
671 [T_FUNC] = &&l_true,
672 [T_CFUNC] = &&l_true,
673 [T_ARR] = &&l_true,
674 [T_PROMISE] = &&l_true,
675 [T_GENERATOR] = &&l_true,
676 [T_SYMBOL] = &&l_true,
677 [T_BOOL] = &&l_bool,
678 [T_STR] = &&l_str,
679 [T_BIGINT] = &&l_bigint,
680 [T_NUM] = &&l_num,
681 };
682
683 uint8_t t = vtype(v);
684 if (t < sizeof(dispatch) / sizeof(*dispatch) && dispatch[t])
685 goto *dispatch[t];
686 return false;
687
688 l_true: return true;
689 l_bool: return vdata(v) != 0;
690 l_str: return vstrlen(js, v) > 0;
691 l_bigint: return !bigint_is_zero(js, v);
692 l_num: {
693 double d = tod(v);
694 return d != 0.0 && !isnan(d);
695 }
696}
697
698static size_t cpy(char *dst, size_t dstlen, const char *src, size_t srclen) {
699 if (dstlen == 0) return srclen;
700 size_t len = srclen < dstlen - 1 ? srclen : dstlen - 1;
701 memcpy(dst, src, len); dst[len] = '\0';
702 return srclen;
703}
704
705size_t uint_to_str(char *buf, size_t bufsize, uint64_t val) {
706 if (bufsize == 0) return 0;
707 if (val == 0) {
708 buf[0] = '0';
709 buf[1] = '\0';
710 return 1;
711 }
712 char temp[24];
713 size_t len = 0;
714 while (val > 0 && len < sizeof(temp)) {
715 temp[len++] = '0' + (val % 10);
716 val /= 10;
717 }
718 if (len >= bufsize) len = bufsize - 1;
719 for (size_t i = 0; i < len; i++) {
720 buf[i] = temp[len - 1 - i];
721 }
722 buf[len] = '\0';
723 return len;
724}
725
726static ant_value_t stringify_stack[MAX_STRINGIFY_DEPTH];
727static int stringify_depth = 0;
728static int stringify_indent = 0;
729
730static ant_value_t multiref_objs[MAX_MULTIREF_OBJS];
731static int multiref_ids[MAX_MULTIREF_OBJS];
732static int multiref_count = 0;
733static int multiref_next_id = 0;
734
735static void scan_refs(ant_t *js, ant_value_t value);
736static ant_value_t strobj_call_custom_inspect(ant_t *js, ant_value_t obj);
737
738static int find_multiref(ant_value_t obj) {
739 for (int i = 0; i < multiref_count; i++) {
740 if (multiref_objs[i] == obj) return multiref_ids[i];
741 }
742 return 0;
743}
744
745static bool is_on_stack(ant_value_t obj) {
746 for (int i = 0; i < stringify_depth; i++) {
747 if (stringify_stack[i] == obj) return true;
748 }
749 return false;
750}
751
752static void mark_multiref(ant_value_t obj) {
753 for (int i = 0; i < multiref_count; i++) {
754 if (multiref_objs[i] == obj) {
755 if (multiref_ids[i] == 0) multiref_ids[i] = ++multiref_next_id;
756 return;
757 }
758 }
759 if (multiref_count < MAX_MULTIREF_OBJS) {
760 multiref_objs[multiref_count] = obj;
761 multiref_ids[multiref_count] = 0;
762 multiref_count++;
763 }
764}
765
766static void scan_obj_refs(ant_t *js, ant_value_t obj) {
767 if (is_on_stack(obj)) {
768 mark_multiref(obj);
769 return;
770 }
771
772 if (stringify_depth >= MAX_STRINGIFY_DEPTH) return;
773 stringify_stack[stringify_depth++] = obj;
774
775 ant_object_t *ptr = js_obj_ptr(obj);
776 if (ptr && ptr->shape) {
777 uint32_t count = ant_shape_count(ptr->shape);
778 for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) {
779 scan_refs(js, ant_object_prop_get_unchecked(ptr, i));
780 }
781 }
782
783 ant_value_t proto_val = get_proto(js, obj);
784 if (vtype(proto_val) == T_OBJ) scan_refs(js, proto_val);
785
786 stringify_depth--;
787}
788
789static void scan_arr_refs(ant_t *js, ant_value_t obj) {
790 if (is_on_stack(obj)) {
791 mark_multiref(obj);
792 return;
793 }
794
795 if (stringify_depth >= MAX_STRINGIFY_DEPTH) return;
796 stringify_stack[stringify_depth++] = obj;
797
798 ant_object_t *ptr = js_obj_ptr(obj);
799 if (ptr && ptr->shape) {
800 uint32_t count = ant_shape_count(ptr->shape);
801 for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) {
802 scan_refs(js, ant_object_prop_get_unchecked(ptr, i));
803 }
804 }
805
806 stringify_depth--;
807}
808
809static void scan_func_refs(ant_t *js, ant_value_t value) {
810 ant_value_t func_obj = js_func_obj(value);
811
812 if (is_on_stack(func_obj)) {
813 mark_multiref(func_obj);
814 return;
815 }
816
817 if (stringify_depth >= MAX_STRINGIFY_DEPTH) return;
818 stringify_stack[stringify_depth++] = func_obj;
819
820 ant_object_t *ptr = js_obj_ptr(func_obj);
821 if (ptr && ptr->shape) {
822 uint32_t count = ant_shape_count(ptr->shape);
823 for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) {
824 scan_refs(js, ant_object_prop_get_unchecked(ptr, i));
825 }
826 }
827
828 stringify_depth--;
829}
830
831static void scan_refs(ant_t *js, ant_value_t value) {
832 switch (vtype(value)) {
833 case T_OBJ: scan_obj_refs(js, value); break;
834 case T_ARR: scan_arr_refs(js, value); break;
835 case T_FUNC: scan_func_refs(js, value); break;
836 default: break;
837 }
838}
839
840static int get_circular_ref(ant_value_t obj) {
841 if (is_on_stack(obj)) {
842 int ref = find_multiref(obj);
843 return ref ? ref : -1;
844 }
845 return 0;
846}
847
848static bool is_circular(ant_value_t obj) {
849 return is_on_stack(obj);
850}
851
852static int get_self_ref(ant_value_t obj) {
853 return find_multiref(obj);
854}
855
856static void push_stringify(ant_value_t obj) {
857 if (stringify_depth < MAX_STRINGIFY_DEPTH) {
858 stringify_stack[stringify_depth++] = obj;
859 }
860}
861
862static void pop_stringify(void) {
863 if (stringify_depth > 0) stringify_depth--;
864}
865
866static size_t add_indent(char *buf, size_t len, int level) {
867 size_t wanted = (size_t)(level * 2);
868 size_t n = 0;
869 for (int i = 0; i < level * 2 && n < len; i++) {
870 buf[n++] = ' ';
871 }
872 return wanted;
873}
874
875const char *get_str_prop(ant_t *js, ant_value_t obj, const char *key, ant_offset_t klen, ant_offset_t *out_len) {
876 GC_ROOT_SAVE(root_mark, js);
877 GC_ROOT_PIN(js, obj);
878 ant_value_t v = lkp_val(js, obj, key, klen);
879
880 GC_ROOT_PIN(js, v);
881 if (vtype(v) != T_STR) {
882 GC_ROOT_RESTORE(js, root_mark);
883 return NULL;
884 }
885
886 const char *str = (const char *)(uintptr_t)(vstr(js, v, out_len));
887 GC_ROOT_RESTORE(js, root_mark);
888
889 return str;
890}
891
892static bool is_small_array(ant_t *js, ant_value_t obj, int *elem_count) {
893 ant_offset_t length = get_array_length(js, obj);
894 if (length > 64) { if (elem_count) *elem_count = (int)length; return false; }
895
896 int count = 0; bool has_nested = false;
897 for (ant_offset_t i = 0; i < length; i++) {
898 ant_value_t val = arr_get(js, obj, i); uint8_t t = vtype(val);
899 if (t == T_OBJ || t == T_ARR || t == T_FUNC) has_nested = true;
900 count++;
901 }
902
903 if (elem_count) *elem_count = count;
904 return count <= 4 && !has_nested;
905}
906
907static inline bool is_array_index(const char *key, ant_offset_t klen) {
908 if (klen == 0 || (klen > 1 && key[0] == '0')) return false;
909 for (ant_offset_t i = 0; i < klen; i++) {
910 if (key[i] < '0' || key[i] > '9') return false;
911 }
912 return true;
913}
914
915static inline bool parse_array_index(const char *key, size_t klen, ant_offset_t max_len, unsigned long *out_idx) {
916 if (klen == 0 || key[0] < '0' || key[0] > '9') return false;
917 unsigned long parsed_idx = 0;
918
919 for (size_t i = 0; i < klen; i++) {
920 if (key[i] < '0' || key[i] > '9') return false;
921 parsed_idx = parsed_idx * 10 + (key[i] - '0');
922 }
923
924 if (parsed_idx >= max_len) return false;
925 *out_idx = parsed_idx;
926 return true;
927}
928
929#define ANT_ARRAY_INDEX_EXCLUSIVE ((ant_offset_t)UINT32_MAX)
930
931static inline ant_object_t *array_obj_ptr(ant_value_t obj) {
932 if (!is_object_type(obj)) return NULL;
933 ant_object_t *ptr = js_obj_ptr(obj);
934 return (ptr && ptr->type_tag == T_ARR) ? ptr : NULL;
935}
936
937static inline bool array_may_have_holes(ant_value_t obj) {
938 ant_object_t *ptr = array_obj_ptr(obj);
939 return ptr ? ptr->may_have_holes : true;
940}
941
942static inline bool array_may_have_dense_elements(ant_value_t obj) {
943 ant_object_t *ptr = array_obj_ptr(obj);
944 return ptr ? ptr->may_have_dense_elements : true;
945}
946
947static inline void array_mark_may_have_holes(ant_value_t obj) {
948 ant_object_t *ptr = array_obj_ptr(obj);
949 if (ptr) ptr->may_have_holes = 1;
950}
951
952static inline void array_define_or_set_index(ant_t *js, ant_value_t obj, const char *key, size_t klen) {
953 if (!key) return;
954 if (!array_obj_ptr(obj)) return;
955
956 unsigned long idx = 0;
957 if (!parse_array_index(key, klen, ANT_ARRAY_INDEX_EXCLUSIVE, &idx)) return;
958
959 ant_offset_t cur_len = get_array_length(js, obj);
960 ant_offset_t next_len = (ant_offset_t)idx + 1;
961
962 if (next_len > cur_len) {
963 if ((ant_offset_t)idx > cur_len) array_mark_may_have_holes(obj);
964 array_len_set(js, obj, next_len);
965 }
966}
967
968static ant_offset_t get_array_length(ant_t *js, ant_value_t arr) {
969 if (!is_object_type(arr)) return 0;
970
971 ant_object_t *arr_ptr = array_obj_ptr(arr);
972 if (arr_ptr) return (ant_offset_t)arr_ptr->u.array.len;
973
974 ant_value_t val = lkp_interned_val(js, arr, js->intern.length);
975 if (vtype(val) == T_NUM) return (ant_offset_t) tod(val);
976
977 return 0;
978}
979
980static ant_value_t get_obj_ctor(ant_t *js, ant_value_t obj) {
981 ant_value_t ctor = get_slot(obj, SLOT_CTOR);
982 if (vtype(ctor) == T_FUNC) return ctor;
983 ant_value_t proto = get_slot(obj, SLOT_PROTO);
984 if (vtype(proto) != T_OBJ) return js_mkundef();
985 return lkp_interned_val(js, proto, js->intern.constructor);
986}
987
988static const char *get_func_name(ant_t *js, ant_value_t func, ant_offset_t *out_len) {
989 if (vtype(func) == T_CFUNC) {
990 const ant_cfunc_meta_t *meta = js_as_cfunc_meta(func);
991 if (!meta || !meta->name) return NULL;
992 if (out_len) *out_len = (ant_offset_t)strlen(meta->name);
993 return meta->name;
994 }
995
996 if (vtype(func) != T_FUNC) return NULL;
997 ant_value_t func_obj = js_func_obj(func);
998 ant_value_t name = lkp_val(js, func_obj, "name", 4);
999
1000 if (vtype(name) == T_STR) {
1001 ant_offset_t str_off = vstr(js, name, out_len);
1002 return (const char *)(uintptr_t)(str_off);
1003 }
1004
1005 ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC);
1006 if (vtype(cfunc_slot) == T_CFUNC) return get_func_name(js, cfunc_slot, out_len);
1007
1008 return NULL;
1009}
1010
1011static bool js_cfunc_try_get_own(ant_t *js, ant_value_t cfunc, const char *key, size_t key_len, ant_value_t *out) {
1012 if (key_len == 4 && memcmp(key, "name", 4) == 0) {
1013 ant_value_t name = js_cfunc_name_value(js, cfunc);
1014 if (vtype(name) != T_UNDEF) {
1015 *out = name;
1016 return true;
1017 }
1018 return false;
1019 }
1020
1021 if (key_len == 6 && memcmp(key, "length", 6) == 0) {
1022 *out = js_cfunc_length_value(cfunc);
1023 return true;
1024 }
1025
1026 if (key_len == 9 && memcmp(key, "prototype", 9) == 0 && js_cfunc_has_prototype(cfunc)) {
1027 ant_value_t promoted = js_cfunc_promote(js, cfunc);
1028 if (is_err(promoted)) {
1029 *out = promoted;
1030 return true;
1031 }
1032 *out = js_get(js, promoted, "prototype");
1033 return true;
1034 }
1035
1036 return false;
1037}
1038
1039static const char *get_class_name(ant_t *js, ant_value_t obj, ant_offset_t *out_len, const char *skip) {
1040 const char *name = get_func_name(js, get_obj_ctor(js, obj), out_len);
1041 if (!name) return NULL;
1042 if (skip && *out_len == (ant_offset_t)strlen(skip) && memcmp(name, skip, *out_len) == 0) return NULL;
1043 return name;
1044}
1045
1046static inline ant_offset_t dense_iterable_length(ant_t *js, ant_value_t obj) {
1047 ant_offset_t doff = get_dense_buf(obj);
1048 if (!doff) return 0;
1049 ant_offset_t dense_len = dense_capacity(doff);
1050 ant_offset_t semantic_len = get_array_length(js, obj);
1051 return dense_len < semantic_len ? dense_len : semantic_len;
1052}
1053
1054static size_t strarr(ant_t *js, ant_value_t obj, char *buf, size_t len) {
1055 int ref = get_circular_ref(obj);
1056 if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10);
1057
1058 push_stringify(obj);
1059 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
1060 ant_offset_t length = get_array_length(js, obj);
1061 ant_offset_t d_len = dense_iterable_length(js, obj);
1062 ant_offset_t iter_len = (d_len >= length) ? length : d_len;
1063
1064 ant_offset_t class_len = 0;
1065 const char *class_name = get_class_name(js, obj, &class_len, "Array");
1066
1067 int elem_count = 0;
1068 bool inline_mode = is_small_array(js, obj, &elem_count);
1069 size_t n = 0;
1070
1071 if (class_name) {
1072 n += cpy(buf + n, REMAIN(n, len), class_name, class_len);
1073 n += (size_t) snprintf(buf + n, REMAIN(n, len), "(%u) ", (unsigned) length);
1074 }
1075
1076 if (length == 0) {
1077 n += cpy(buf + n, REMAIN(n, len), "[]", 2);
1078 pop_stringify();
1079 return n;
1080 }
1081
1082 n += cpy(buf + n, REMAIN(n, len), inline_mode ? "[ " : "[\n", 2);
1083 if (!inline_mode) stringify_indent++;
1084
1085 bool printed_first = false;
1086 for (ant_offset_t i = 0; i < iter_len; i++) {
1087 if (printed_first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2);
1088 if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1089
1090 ant_value_t val = arr_get(js, obj, i); bool found = arr_has(js, obj, i);
1091 n += found ? tostr(js, val, buf + n, REMAIN(n, len)) : cpy(buf + n, REMAIN(n, len), "undefined", 9);
1092 printed_first = true;
1093 }
1094
1095 if (ptr && ptr->shape) {
1096 uint32_t shape_count = ant_shape_count(ptr->shape);
1097 for (uint32_t i = 0; i < shape_count; i++) {
1098 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
1099 if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue;
1100 const char *key = prop->key.interned;
1101 ant_offset_t klen = (ant_offset_t)strlen(key);
1102 if (is_length_key(key, klen)) continue;
1103
1104 if (printed_first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2);
1105 if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1106
1107 ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef();
1108 if (is_array_index(key, klen)) {
1109 n += tostr(js, val, buf + n, REMAIN(n, len));
1110 } else {
1111 n += cpy(buf + n, REMAIN(n, len), key, klen);
1112 n += cpy(buf + n, REMAIN(n, len), ": ", 2);
1113 n += tostr(js, val, buf + n, REMAIN(n, len));
1114 }
1115 printed_first = true;
1116 }
1117 }
1118
1119 if (!inline_mode) {
1120 stringify_indent--;
1121 n += cpy(buf + n, REMAIN(n, len), "\n", 1);
1122 n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1123 }
1124
1125 n += cpy(buf + n, REMAIN(n, len), inline_mode ? " ]" : "]", inline_mode ? 2 : 1);
1126 pop_stringify();
1127 return n;
1128}
1129
1130static size_t strdate(ant_t *js, ant_value_t obj, char *buf, size_t len) {
1131 ant_value_t time_val = js_get_slot(obj, SLOT_DATA);
1132 if (vtype(time_val) != T_NUM) return cpy(buf, len, "Invalid Date", 12);
1133
1134 static const date_string_spec_t kSpec = {DATE_STRING_FMT_ISO, DATE_STRING_PART_ALL};
1135 ant_value_t iso = get_date_string(js, obj, kSpec);
1136 if (is_err(iso) || vtype(iso) != T_STR) return cpy(buf, len, "Invalid Date", 12);
1137
1138 ant_offset_t slen;
1139 ant_offset_t soff = vstr(js, iso, &slen);
1140
1141 return cpy(buf, len, (const char *)(uintptr_t)(soff), slen);
1142}
1143
1144static bool is_valid_identifier(const char *str, ant_offset_t slen) {
1145 if (slen == 0) return false;
1146 char c = str[0];
1147 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$')) return false;
1148 for (ant_offset_t i = 1; i < slen; i++) {
1149 c = str[i];
1150 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '$')) return false;
1151 }
1152 return true;
1153}
1154
1155static size_t strkey(ant_t *js, ant_value_t value, char *buf, size_t len) {
1156 ant_offset_t slen, off = vstr(js, value, &slen);
1157 const char *str = (const char *)(uintptr_t)(off);
1158
1159 if (is_valid_identifier(str, slen)) {
1160 return cpy(buf, len, str, slen);
1161 }
1162 return strstring(js, value, buf, len);
1163}
1164
1165static size_t strkey_interned(ant_t *js, const char *key, size_t klen, char *buf, size_t len) {
1166 if (is_valid_identifier(key, (ant_offset_t)klen)) {
1167 return cpy(buf, len, key, klen);
1168 }
1169 ant_value_t key_str = js_mkstr(js, key, klen);
1170 return strstring(js, key_str, buf, len);
1171}
1172
1173static bool is_small_object(ant_t *js, ant_value_t obj, int *prop_count) {
1174 int count = 0;
1175 bool has_nested = false;
1176
1177 ant_value_t as_obj = js_as_obj(obj);
1178 ant_object_t *ptr = js_obj_ptr(as_obj);
1179 uintptr_t obj_off = (uintptr_t)vdata(as_obj);
1180 if (ptr && ptr->shape) {
1181 uint32_t shape_count = ant_shape_count(ptr->shape);
1182 for (uint32_t i = 0; i < shape_count; i++) {
1183 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
1184 if (!prop) continue;
1185
1186 if (prop->type == ANT_SHAPE_KEY_SYMBOL) {
1187 count++;
1188 continue;
1189 }
1190
1191 if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue;
1192
1193 ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef();
1194 uint8_t t = vtype(val);
1195 if (t == T_OBJ || t == T_ARR || t == T_FUNC) has_nested = true;
1196 count++;
1197 }
1198 }
1199
1200 if (ptr && ptr->is_exotic) {
1201 descriptor_entry_t *desc, *tmp;
1202 HASH_ITER(hh, desc_registry, desc, tmp) {
1203 if (desc->obj_off != obj_off) continue;
1204 if (!desc->enumerable) continue;
1205 if (!desc->has_getter && !desc->has_setter) continue;
1206 count++;
1207 }
1208 }
1209
1210 if (prop_count) *prop_count = count;
1211 return count <= 4 && !has_nested;
1212}
1213
1214void js_inspect_builder_init_fixed(js_inspect_builder_t *builder, ant_t *js, char *buf, size_t len, size_t initial_n) {
1215 builder->js = js;
1216 builder->buf = buf;
1217 builder->len = len;
1218 builder->n = initial_n;
1219 builder->growable = false;
1220 builder->inline_mode = false;
1221 builder->first = true;
1222 builder->closed = false;
1223 builder->did_indent = false;
1224}
1225
1226bool js_inspect_builder_init_dynamic(js_inspect_builder_t *builder, ant_t *js, size_t initial_cap) {
1227 size_t cap = initial_cap ? initial_cap : 128;
1228 char *buf = malloc(cap);
1229 if (!buf) return false;
1230
1231 buf[0] = '\0';
1232 builder->js = js;
1233 builder->buf = buf;
1234 builder->len = cap;
1235 builder->n = 0;
1236 builder->growable = true;
1237 builder->inline_mode = false;
1238 builder->first = true;
1239 builder->closed = false;
1240 builder->did_indent = false;
1241
1242 return true;
1243}
1244
1245void js_inspect_builder_dispose(js_inspect_builder_t *builder) {
1246 if (builder->growable) free(builder->buf);
1247 builder->buf = NULL;
1248 builder->len = 0;
1249 builder->n = 0;
1250}
1251
1252ant_value_t js_inspect_builder_result(js_inspect_builder_t *builder) {
1253 ant_value_t out = js_mkstr(builder->js, builder->buf ? builder->buf : "", builder->n);
1254 js_inspect_builder_dispose(builder);
1255 return out;
1256}
1257
1258static inline char *fixed_buf_write_ptr(char *buf, size_t len, size_t n, size_t *avail) {
1259 if (!buf || len == 0) {
1260 if (avail) *avail = 0;
1261 return NULL;
1262 }
1263
1264 size_t write_index = n < len ? n : len - 1;
1265 if (avail) *avail = len - write_index;
1266 return buf + write_index;
1267}
1268
1269static inline char *js_inspect_builder_write_ptr(js_inspect_builder_t *builder, size_t *avail) {
1270 return fixed_buf_write_ptr(builder->buf, builder->len, builder->n, avail);
1271}
1272
1273static bool js_inspect_builder_reserve(js_inspect_builder_t *builder, size_t extra) {
1274 if (!builder->growable) return true;
1275
1276 size_t needed = builder->n + extra + 1;
1277 if (needed <= builder->len) return true;
1278
1279 size_t new_cap = builder->len ? builder->len : 128;
1280 while (new_cap < needed) new_cap *= 2;
1281
1282 char *new_buf = realloc(builder->buf, new_cap);
1283 if (!new_buf) return false;
1284
1285 builder->buf = new_buf;
1286 builder->len = new_cap;
1287
1288 return true;
1289}
1290
1291static bool js_inspect_append(js_inspect_builder_t *builder, const char *src, size_t srclen) {
1292 if (builder->growable) {
1293 if (!js_inspect_builder_reserve(builder, srclen)) return false;
1294 memcpy(builder->buf + builder->n, src, srclen);
1295 builder->n += srclen;
1296 builder->buf[builder->n] = '\0';
1297 return true;
1298 }
1299
1300 size_t avail = 0;
1301 char *dst = js_inspect_builder_write_ptr(builder, &avail);
1302 builder->n += cpy(dst, avail, src, srclen);
1303
1304 return true;
1305}
1306
1307static bool __attribute__((format(printf, 2, 0)))
1308js_inspect_vappendf(js_inspect_builder_t *builder, const char *fmt, va_list args) {
1309 if (builder->growable) {
1310 va_list copy;
1311 va_copy(copy, args);
1312 int needed = vsnprintf(NULL, 0, fmt, copy);
1313 va_end(copy);
1314 if (needed < 0) return false;
1315 if (!js_inspect_builder_reserve(builder, (size_t)needed)) return false;
1316 vsnprintf(builder->buf + builder->n, builder->len - builder->n, fmt, args);
1317 builder->n += (size_t)needed;
1318 return true;
1319 }
1320
1321 size_t avail = 0;
1322 char *dst = js_inspect_builder_write_ptr(builder, &avail);
1323 int needed = vsnprintf(dst, avail, fmt, args);
1324
1325 if (needed < 0) return false;
1326 builder->n += (size_t)needed;
1327
1328 return true;
1329}
1330
1331static bool __attribute__((format(printf, 2, 3)))
1332js_inspect_appendf(js_inspect_builder_t *builder, const char *fmt, ...) {
1333 va_list args;
1334 va_start(args, fmt);
1335 bool ok = js_inspect_vappendf(builder, fmt, args);
1336 va_end(args);
1337 return ok;
1338}
1339
1340static bool js_inspect_append_indent(js_inspect_builder_t *builder, int indent) {
1341 for (int i = 0; i < indent; i++) if (!js_inspect_append(builder, " ", 2)) return false;
1342 return true;
1343}
1344
1345static bool js_inspect_append_tostr(js_inspect_builder_t *builder, ant_value_t value) {
1346 if (!builder->growable) {
1347 size_t avail = 0;
1348 char *dst = js_inspect_builder_write_ptr(builder, &avail);
1349 builder->n += tostr(builder->js, value, dst, avail);
1350 return true;
1351 }
1352
1353 size_t cap = 128;
1354 char *tmp = malloc(cap);
1355 if (!tmp) return false;
1356
1357 for (;;) {
1358 size_t written = tostr(builder->js, value, tmp, cap);
1359 if (written < cap) {
1360 bool ok = js_inspect_append(builder, tmp, written);
1361 free(tmp);
1362 return ok;
1363 }
1364
1365 size_t new_cap = written + 1;
1366 char *new_tmp = realloc(tmp, new_cap);
1367 if (!new_tmp) {
1368 free(tmp);
1369 return false;
1370 }
1371 tmp = new_tmp;
1372 cap = new_cap;
1373 }
1374}
1375
1376static bool js_inspect_append_key_interned(js_inspect_builder_t *builder, const char *key, size_t klen) {
1377 if (!builder->growable) {
1378 size_t avail = 0;
1379 char *dst = js_inspect_builder_write_ptr(builder, &avail);
1380 builder->n += strkey_interned(builder->js, key, klen, dst, avail);
1381 return true;
1382 }
1383
1384 size_t cap = klen + 16;
1385 char *tmp = malloc(cap);
1386 if (!tmp) return false;
1387
1388 for (;;) {
1389 size_t written = strkey_interned(builder->js, key, klen, tmp, cap);
1390 if (written < cap) {
1391 bool ok = js_inspect_append(builder, tmp, written);
1392 free(tmp);
1393 return ok;
1394 }
1395
1396 size_t new_cap = written + 1;
1397 char *new_tmp = realloc(tmp, new_cap);
1398 if (!new_tmp) {
1399 free(tmp);
1400 return false;
1401 }
1402 tmp = new_tmp;
1403 cap = new_cap;
1404 }
1405}
1406
1407static bool __attribute__((format(printf, 3, 0)))
1408js_inspect_vheader_for(js_inspect_builder_t *builder, ant_value_t obj, const char *fmt, va_list args) {
1409 bool ok = js_inspect_vappendf(builder, fmt, args);
1410 if (!ok) return false;
1411
1412 if (is_object_type(obj)) {
1413 int prop_count = 0;
1414 bool inline_mode = is_small_object(builder->js, obj, &prop_count);
1415
1416 if (prop_count == 0) {
1417 if (!js_inspect_append(builder, " {}", 3)) return false;
1418 builder->inline_mode = false;
1419 builder->first = true;
1420 builder->closed = true;
1421 builder->did_indent = false;
1422 return true;
1423 }
1424
1425 if (inline_mode) {
1426 if (!js_inspect_append(builder, " { ", 3)) return false;
1427 builder->inline_mode = true;
1428 builder->first = true;
1429 builder->closed = false;
1430 builder->did_indent = false;
1431 return true;
1432 }}
1433
1434 if (!js_inspect_append(builder, " {\n", 3)) return false;
1435
1436 builder->inline_mode = false;
1437 builder->first = true;
1438 builder->closed = false;
1439 builder->did_indent = false;
1440
1441 return true;
1442}
1443
1444bool js_inspect_header_for(js_inspect_builder_t *builder, ant_value_t obj, const char *fmt, ...) {
1445 va_list args; va_start(args, fmt);
1446 bool ok = js_inspect_vheader_for(builder, obj, fmt, args);
1447 va_end(args);
1448 return ok;
1449}
1450
1451bool js_inspect_header(js_inspect_builder_t *builder, const char *fmt, ...) {
1452 va_list args; va_start(args, fmt);
1453 bool ok = js_inspect_vheader_for(builder, js_mkundef(), fmt, args);
1454 va_end(args);
1455 return ok;
1456}
1457
1458bool js_inspect_tagged_header(js_inspect_builder_t *builder, const char *tag, size_t tag_len) {
1459 if (!js_inspect_append(builder, "Object [", 8)) return false;
1460 if (!js_inspect_append(builder, tag, tag_len)) return false;
1461 if (!js_inspect_append(builder, "] {\n", 4)) return false;
1462
1463 builder->inline_mode = false;
1464 builder->first = true;
1465 builder->closed = false;
1466 builder->did_indent = false;
1467
1468 return true;
1469}
1470
1471// TODO: modularize
1472static bool js_inspect_plain_header(js_inspect_builder_t *builder, ant_value_t obj) {
1473 ant_t *js = builder->js;
1474 int prop_count = 0;
1475 bool inline_mode = is_small_object(js, obj, &prop_count);
1476
1477 ant_value_t proto_val = js_get_proto(js, obj);
1478 bool is_null_proto = (vtype(proto_val) == T_NULL);
1479 bool proto_is_null_proto = false;
1480 const char *class_name = NULL;
1481 ant_offset_t class_name_len = 0;
1482
1483 do {
1484 if (is_null_proto) break;
1485 uint8_t pt = vtype(proto_val);
1486 if (pt != T_OBJ && pt != T_FUNC) break;
1487
1488 ant_value_t proto_proto = js_get_proto(js, proto_val);
1489 ant_value_t object_proto = js->sym.object_proto;
1490 proto_is_null_proto = (vtype(proto_proto) == T_NULL) && (vdata(proto_val) != vdata(object_proto));
1491
1492 class_name = get_class_name(js, obj, &class_name_len, "Object");
1493 } while (0);
1494
1495 if (prop_count == 0) {
1496 if (is_null_proto) {
1497 if (!js_inspect_append(builder, "[Object: null prototype] {}", 27)) return false;
1498 } else if (class_name && class_name_len > 0) {
1499 if (!js_inspect_append(builder, class_name, class_name_len)) return false;
1500 if (proto_is_null_proto) {
1501 if (!js_inspect_append(builder, " <[Object: null prototype] {}> {}", 33)) return false;
1502 } else if (!js_inspect_append(builder, " {}", 3)) return false;
1503 } else if (proto_is_null_proto) {
1504 if (!js_inspect_append(builder, "<[Object: null prototype] {}> {}", 32)) return false;
1505 } else if (!js_inspect_append(builder, "{}", 2)) return false;
1506
1507 builder->closed = true;
1508 return true;
1509 }
1510
1511 if (is_null_proto) {
1512 if (!js_inspect_append(builder, "[Object: null prototype] ", 25)) return false;
1513 } else if (class_name && class_name_len > 0) {
1514 if (!js_inspect_append(builder, class_name, class_name_len)) return false;
1515 if (proto_is_null_proto) {
1516 if (!js_inspect_append(builder, " <[Object: null prototype] {}> ", 31)) return false;
1517 } else if (!js_inspect_append(builder, " ", 1)) return false;
1518 } else if (proto_is_null_proto) {
1519 if (!js_inspect_append(builder, "<[Object: null prototype] {}> ", 30)) return false;
1520 }
1521
1522 if (!js_inspect_append(builder, inline_mode ? "{ " : "{\n", 2)) return false;
1523 builder->inline_mode = inline_mode;
1524 builder->first = true;
1525 builder->closed = false;
1526 builder->did_indent = false;
1527
1528 return true;
1529}
1530
1531bool js_inspect_object_body(js_inspect_builder_t *builder, ant_value_t obj) {
1532 if (builder->closed) return true;
1533
1534 if (!builder->inline_mode && !builder->did_indent) {
1535 stringify_indent++;
1536 builder->did_indent = true;
1537 }
1538
1539 bool first = builder->first;
1540 ant_t *js = builder->js;
1541 ant_value_t tag_sym = get_toStringTag_sym();
1542 ant_value_t as_obj = js_as_obj(obj);
1543 ant_object_t *ptr = js_obj_ptr(as_obj);
1544 uintptr_t obj_off = (uintptr_t)vdata(as_obj);
1545 uint32_t shape_count = (ptr && ptr->shape) ? ant_shape_count(ptr->shape) : 0;
1546
1547 for (uint32_t i = 0; i < shape_count; i++) {
1548 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
1549 if (!prop) continue;
1550 if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue;
1551 ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef();
1552
1553 if (prop->type == ANT_SHAPE_KEY_SYMBOL) {
1554 ant_offset_t sym_off = prop->key.sym_off;
1555 if (vtype(tag_sym) == T_SYMBOL && sym_off == (ant_offset_t)vdata(tag_sym)) continue;
1556
1557 if (ptr && ptr->is_exotic) {
1558 prop_meta_t meta;
1559 if (lookup_symbol_prop_meta(as_obj, sym_off, &meta) && !meta.enumerable) continue;
1560 }
1561
1562 ant_value_t sym = mkval(T_SYMBOL, sym_off);
1563
1564 if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false;
1565 first = false;
1566 if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false;
1567 if (!js_inspect_append(builder, "[", 1)) return false;
1568 if (!js_inspect_append_tostr(builder, sym)) return false;
1569 if (!js_inspect_append(builder, "]: ", 3)) return false;
1570 if (!js_inspect_append_tostr(builder, val)) return false;
1571 continue;
1572 }
1573
1574 const char *key = prop->key.interned;
1575 ant_offset_t klen = (ant_offset_t)strlen(key);
1576 if (ptr && ptr->is_exotic) {
1577 prop_meta_t meta;
1578 if (lookup_string_prop_meta(js, as_obj, key, (size_t)klen, &meta) && !meta.enumerable) continue;
1579 }
1580
1581 if (prop->has_getter || prop->has_setter) {
1582 if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false;
1583 first = false;
1584 if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false;
1585 if (!js_inspect_append_key_interned(builder, key, (size_t)klen)) return false;
1586 if (!js_inspect_append(builder, ": ", 2)) return false;
1587 if (prop->has_getter && prop->has_setter) {
1588 if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false;
1589 } else if (prop->has_getter) {
1590 if (!js_inspect_append(builder, "[Getter]", 8)) return false;
1591 } else if (!js_inspect_append(builder, "[Setter]", 8)) return false;
1592 continue;
1593 }
1594
1595 if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false;
1596 first = false;
1597 if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false;
1598
1599 bool is_special_global = false;
1600 if (vtype(val) == T_UNDEF && streq(key, klen, "undefined", 9)) {
1601 is_special_global = true;
1602 } else if (vtype(val) == T_NUM) {
1603 double d = tod(val);
1604 if (isinf(d) && d > 0 && streq(key, klen, "Infinity", 8)) {
1605 is_special_global = true;
1606 } else if (isnan(d) && streq(key, klen, "NaN", 3)) {
1607 is_special_global = true;
1608 }
1609 }
1610
1611 if (is_special_global) {
1612 if (!js_inspect_append_tostr(builder, val)) return false;
1613 } else {
1614 if (!js_inspect_append_key_interned(builder, key, (size_t)klen)) return false;
1615 if (!js_inspect_append(builder, ": ", 2)) return false;
1616 if (!js_inspect_append_tostr(builder, val)) return false;
1617 }
1618 }
1619
1620 if (ptr && ptr->is_exotic) {
1621 descriptor_entry_t *desc, *tmp;
1622 HASH_ITER(hh, desc_registry, desc, tmp) {
1623 if (desc->obj_off != obj_off) continue;
1624 if (!desc->enumerable) continue;
1625 if (!desc->has_getter && !desc->has_setter) continue;
1626
1627 if (!first && !js_inspect_append(builder, builder->inline_mode ? ", " : ",\n", 2)) return false;
1628 first = false;
1629 if (!builder->inline_mode && !js_inspect_append_indent(builder, stringify_indent)) return false;
1630 if (!js_inspect_append(builder, desc->prop_name, desc->prop_len)) return false;
1631 if (!js_inspect_append(builder, ": ", 2)) return false;
1632
1633 if (desc->has_getter && desc->has_setter) {
1634 if (!js_inspect_append(builder, "[Getter/Setter]", 15)) return false;
1635 } else if (desc->has_getter) {
1636 if (!js_inspect_append(builder, "[Getter]", 8)) return false;
1637 } else if (!js_inspect_append(builder, "[Setter]", 8)) return false;
1638 }
1639 }
1640
1641 builder->first = first;
1642 return true;
1643}
1644
1645bool js_inspect_close(js_inspect_builder_t *builder) {
1646 if (builder->closed) return true;
1647
1648 if (builder->did_indent) {
1649 stringify_indent--;
1650 builder->did_indent = false;
1651 }
1652
1653 if (builder->inline_mode) {
1654 if (!js_inspect_append(builder, " }", 2)) return false;
1655 } else {
1656 if (!builder->first && !js_inspect_append(builder, "\n", 1)) return false;
1657 if (!js_inspect_append_indent(builder, stringify_indent)) return false;
1658 if (!js_inspect_append(builder, "}", 1)) return false;
1659 }
1660
1661 builder->closed = true;
1662 return true;
1663}
1664
1665// todo: split into smaller functions
1666static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) {
1667 int ref = get_circular_ref(obj);
1668 if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10);
1669
1670 push_stringify(obj);
1671
1672 size_t n = 0;
1673 int self_ref = get_self_ref(obj);
1674 if (self_ref) {
1675 n += (size_t) snprintf(buf + n, REMAIN(n, len), "<ref *%d> ", self_ref);
1676 }
1677
1678 ant_value_t inspect_val = strobj_call_custom_inspect(js, obj);
1679 if (vtype(inspect_val) == T_STR) {
1680 size_t slen = 0;
1681 const char *s = js_getstr(js, inspect_val, &slen);
1682 n += cpy(buf + n, REMAIN(n, len), s ? s : "", slen);
1683 pop_stringify();
1684 return n;
1685 }
1686
1687 if (is_date_instance(obj)) {
1688 n += strdate(js, obj, buf + n, REMAIN(n, len));
1689 pop_stringify();
1690 return n;
1691 }
1692
1693 ant_value_t tag_sym = get_toStringTag_sym();
1694 ant_value_t tag_val = (vtype(tag_sym) == T_SYMBOL) ? lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(tag_sym)) : js_mkundef();
1695 bool is_map = false, is_set = false, is_arraybuffer = false;
1696 ant_offset_t tlen = 0, toff = 0;
1697 const char *tag_str = NULL;
1698
1699 if (vtype(tag_val) == T_STR) {
1700 toff = vstr(js, tag_val, &tlen);
1701 tag_str = (const char *)(uintptr_t)(toff);
1702 is_map = (tlen == 3 && memcmp(tag_str, "Map", 3) == 0);
1703 is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0);
1704 is_arraybuffer = (tlen >= 11 && memcmp(tag_str + tlen - 11, "ArrayBuffer", 11) == 0);
1705
1706 TypedArrayData *ta = buffer_get_typedarray_data(obj);
1707 if (ta && ta->buffer) {
1708 const char *type_name = NULL;
1709 size_t type_len = 0;
1710
1711 ant_value_t proto = js_get_proto(js, obj);
1712 ant_value_t buffer_proto = get_ctor_proto(js, "Buffer", 6);
1713 if (vtype(proto) == T_OBJ && vtype(buffer_proto) == T_OBJ && vdata(proto) == vdata(buffer_proto)) {
1714 type_name = "Buffer";
1715 type_len = 6;
1716 } else if (ta->type <= TYPED_ARRAY_BIGUINT64) {
1717 type_name = buffer_typedarray_type_name(ta->type);
1718 type_len = strlen(type_name);
1719 } else {
1720 type_name = "TypedArray";
1721 type_len = 10;
1722 }
1723
1724 n += cpy(buf + n, REMAIN(n, len), type_name, type_len);
1725 n += (size_t) snprintf(buf + n, REMAIN(n, len), "(%zu) ", ta->length);
1726 n += cpy(buf + n, REMAIN(n, len), "[ ", 2);
1727
1728 uint8_t *data = ta->buffer->data + ta->byte_offset;
1729
1730 for (size_t i = 0; i < ta->length && i < 100; i++) {
1731 if (i > 0) n += cpy(buf + n, REMAIN(n, len), ", ", 2);
1732
1733 switch (ta->type) {
1734 case TYPED_ARRAY_INT8:
1735 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int8_t*)data)[i]);
1736 break;
1737 case TYPED_ARRAY_UINT8:
1738 case TYPED_ARRAY_UINT8_CLAMPED:
1739 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]);
1740 break;
1741 case TYPED_ARRAY_INT16:
1742 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", (int)((int16_t*)data)[i]);
1743 break;
1744 case TYPED_ARRAY_UINT16:
1745 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)((uint16_t*)data)[i]);
1746 break;
1747 case TYPED_ARRAY_INT32:
1748 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%d", ((int32_t*)data)[i]);
1749 break;
1750 case TYPED_ARRAY_UINT32:
1751 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", ((uint32_t*)data)[i]);
1752 break;
1753 case TYPED_ARRAY_FLOAT16:
1754 n += (size_t) snprintf(
1755 buf + n, REMAIN(n, len), "%g", half_to_double(((uint16_t*)data)[i])
1756 );
1757 break;
1758 case TYPED_ARRAY_FLOAT32:
1759 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", (double)((float*)data)[i]);
1760 break;
1761 case TYPED_ARRAY_FLOAT64:
1762 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%g", ((double*)data)[i]);
1763 break;
1764 case TYPED_ARRAY_BIGINT64:
1765 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%lldn", (long long)((int64_t*)data)[i]);
1766 break;
1767 case TYPED_ARRAY_BIGUINT64:
1768 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%llun", (unsigned long long)((uint64_t*)data)[i]);
1769 break;
1770 default:
1771 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", (unsigned)data[i]);
1772 break;
1773 }
1774 }
1775
1776 if (ta->length > 100) n += cpy(buf + n, REMAIN(n, len), ", ...", 5);
1777 n += cpy(buf + n, REMAIN(n, len), " ]", 2);
1778 pop_stringify();
1779 return n;
1780 }
1781
1782 if (is_arraybuffer) {
1783 ArrayBufferData *ab_data = buffer_get_arraybuffer_data(obj);
1784 if (ab_data) {
1785 size_t bytelen = ab_data ? ab_data->length : 0;
1786
1787 n += cpy(buf + n, REMAIN(n, len), tag_str, tlen);
1788 n += cpy(buf + n, REMAIN(n, len), " {\n", 3);
1789 n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 20);
1790
1791 if (ab_data && ab_data->data && bytelen > 0) {
1792 for (size_t i = 0; i < bytelen; i++) {
1793 if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1);
1794 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", ab_data->data[i]);
1795 }
1796 }
1797
1798 n += cpy(buf + n, REMAIN(n, len), ">,\n", 3);
1799 n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16);
1800 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bytelen);
1801 n += cpy(buf + n, REMAIN(n, len), "\n}", 2);
1802 pop_stringify();
1803 return n;
1804 }
1805 }
1806
1807 bool is_dataview = (tlen == 8 && memcmp(tag_str, "DataView", 8) == 0);
1808 if (is_dataview) {
1809 DataViewData *dv = buffer_get_dataview_data(obj);
1810 if (dv && dv->buffer) {
1811 n += cpy(buf + n, REMAIN(n, len), "DataView {\n", 11);
1812 n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 16);
1813 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_length);
1814 n += cpy(buf + n, REMAIN(n, len), ",\n", 2);
1815 n += cpy(buf + n, REMAIN(n, len), " [byteOffset]: ", 16);
1816 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->byte_offset);
1817 n += cpy(buf + n, REMAIN(n, len), ",\n", 2);
1818 n += cpy(buf + n, REMAIN(n, len), " [buffer]: ArrayBuffer {\n", 26);
1819 n += cpy(buf + n, REMAIN(n, len), " [Uint8Contents]: <", 22);
1820
1821 if (dv->buffer->data && dv->buffer->length > 0) {
1822 for (size_t i = 0; i < dv->buffer->length; i++) {
1823 if (i > 0) n += cpy(buf + n, REMAIN(n, len), " ", 1);
1824 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%02x", dv->buffer->data[i]);
1825 }
1826 }
1827
1828 n += cpy(buf + n, REMAIN(n, len), ">,\n", 3);
1829 n += cpy(buf + n, REMAIN(n, len), " [byteLength]: ", 18);
1830 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", dv->buffer->length);
1831 n += cpy(buf + n, REMAIN(n, len), "\n }\n}", 6);
1832 pop_stringify();
1833 return n;
1834 }
1835 }
1836
1837 if (is_map) {
1838 ant_value_t map_val = js_get_slot(obj, SLOT_MAP);
1839 if (vtype(map_val) != T_UNDEF) {
1840 map_entry_t **map_ptr = (map_entry_t**)(size_t)tod(map_val);
1841 n += cpy(buf + n, REMAIN(n, len), "Map(", 4);
1842
1843 unsigned int count = 0;
1844 if (map_ptr && *map_ptr) count = HASH_COUNT(*map_ptr);
1845 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count);
1846 n += cpy(buf + n, REMAIN(n, len), ") ", 2);
1847
1848 if (count == 0) {
1849 n += cpy(buf + n, REMAIN(n, len), "{}", 2);
1850 } else {
1851 n += cpy(buf + n, REMAIN(n, len), "{\n", 2);
1852 stringify_indent++;
1853 bool first = true;
1854 if (map_ptr && *map_ptr) {
1855 map_entry_t *entry, *tmp;
1856 HASH_ITER(hh, *map_ptr, entry, tmp) {
1857 if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2);
1858 first = false;
1859 n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1860 n += tostr(js, entry->key_val, buf + n, REMAIN(n, len));
1861 n += cpy(buf + n, REMAIN(n, len), " => ", 4);
1862 n += tostr(js, entry->value, buf + n, REMAIN(n, len));
1863 }
1864 }
1865 stringify_indent--;
1866 n += cpy(buf + n, REMAIN(n, len), "\n", 1);
1867 n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1868 n += cpy(buf + n, REMAIN(n, len), "}", 1);
1869 }
1870 pop_stringify();
1871 return n;
1872 }
1873 }
1874
1875 if (is_set) {
1876 ant_value_t set_val = js_get_slot(obj, SLOT_SET);
1877 if (vtype(set_val) != T_UNDEF) {
1878 set_entry_t **set_ptr = (set_entry_t**)(size_t)tod(set_val);
1879 n += cpy(buf + n, REMAIN(n, len), "Set(", 4);
1880
1881 unsigned int count = 0;
1882 if (set_ptr && *set_ptr) count = HASH_COUNT(*set_ptr);
1883 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%u", count);
1884 n += cpy(buf + n, REMAIN(n, len), ") ", 2);
1885
1886 if (count == 0) {
1887 n += cpy(buf + n, REMAIN(n, len), "{}", 2);
1888 } else {
1889 n += cpy(buf + n, REMAIN(n, len), "{\n", 2);
1890 stringify_indent++;
1891 bool first = true;
1892 if (set_ptr && *set_ptr) {
1893 set_entry_t *entry, *tmp;
1894 HASH_ITER(hh, *set_ptr, entry, tmp) {
1895 if (!first) n += cpy(buf + n, REMAIN(n, len), ",\n", 2);
1896 first = false;
1897 n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1898 n += tostr(js, entry->value, buf + n, REMAIN(n, len));
1899 }
1900 }
1901 stringify_indent--;
1902 n += cpy(buf + n, REMAIN(n, len), "\n", 1);
1903 n += add_indent(buf + n, REMAIN(n, len), stringify_indent);
1904 n += cpy(buf + n, REMAIN(n, len), "}", 1);
1905 }
1906 pop_stringify();
1907 return n;
1908 }
1909 }
1910
1911 if (tag_str) {
1912 bool is_blob = (tlen == 4 && memcmp(tag_str, "Blob", 4) == 0);
1913 bool is_file = (tlen == 4 && memcmp(tag_str, "File", 4) == 0);
1914 if (is_blob || is_file) {
1915 blob_data_t *bd = blob_get_data(obj);
1916 n += cpy(buf + n, REMAIN(n, len), is_file ? "File" : "Blob", 4);
1917 n += cpy(buf + n, REMAIN(n, len), " { size: ", 9);
1918 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%zu", bd ? bd->size : 0);
1919 n += cpy(buf + n, REMAIN(n, len), ", type: '", 9);
1920 if (bd && bd->type) n += cpy(buf + n, REMAIN(n, len), bd->type, strlen(bd->type));
1921 n += cpy(buf + n, REMAIN(n, len), "'", 1);
1922 if (is_file) {
1923 n += cpy(buf + n, REMAIN(n, len), ", name: '", 9);
1924 if (bd && bd->name) n += cpy(buf + n, REMAIN(n, len), bd->name, strlen(bd->name));
1925 n += cpy(buf + n, REMAIN(n, len), "'", 1);
1926 n += cpy(buf + n, REMAIN(n, len), ", lastModified: ", 16);
1927 n += (size_t) snprintf(buf + n, REMAIN(n, len), "%" PRId64, bd ? bd->last_modified : 0);
1928 }
1929 n += cpy(buf + n, REMAIN(n, len), " }", 2);
1930 pop_stringify();
1931 return n;
1932 }
1933 }
1934
1935 js_inspect_builder_t builder;
1936 js_inspect_builder_init_fixed(&builder, js, buf, len, n);
1937 bool ok = js_inspect_tagged_header(&builder, (const char *)(uintptr_t)toff, (size_t)tlen);
1938 if (ok) ok = js_inspect_object_body(&builder, obj);
1939 if (ok) ok = js_inspect_close(&builder);
1940 n = builder.n;
1941 pop_stringify();
1942 return n;
1943 }
1944
1945 js_inspect_builder_t builder;
1946 js_inspect_builder_init_fixed(&builder, js, buf, len, n);
1947 bool ok = js_inspect_plain_header(&builder, obj);
1948 if (ok) ok = js_inspect_object_body(&builder, obj);
1949 if (ok) ok = js_inspect_close(&builder);
1950 n = builder.n;
1951 pop_stringify();
1952
1953 return n;
1954}
1955
1956static size_t strnum(ant_value_t value, char *buf, size_t len) {
1957 double dv = tod(value);
1958
1959 if (__builtin_expect(isnan(dv), 0))
1960 return cpy(buf, len, "NaN", 3);
1961
1962 if (__builtin_expect(isinf(dv), 0))
1963 return cpy(buf, len, dv > 0 ? "Infinity" : "-Infinity", dv > 0 ? 8 : 9);
1964
1965 return ant_number_to_shortest(dv, buf, len);
1966}
1967
1968static inline ant_offset_t assert_flat_string_len(ant_value_t value, const char **out_ptr) {
1969 ant_flat_string_t *flat = ant_str_flat_ptr(value);
1970 assert(flat != NULL);
1971 ant_offset_t len = flat->len;
1972 if (out_ptr) *out_ptr = flat->bytes;
1973 return len;
1974}
1975
1976static inline ant_rope_heap_t *assert_rope_ptr(ant_value_t value) {
1977 assert(vtype(value) == T_STR);
1978 assert(str_is_heap_rope(value));
1979 ant_rope_heap_t *ptr = ant_str_rope_ptr(value);
1980 assert(ptr != NULL);
1981 return ptr;
1982}
1983
1984static inline ant_offset_t rope_len(ant_value_t value) {
1985 ant_rope_heap_t *ptr = assert_rope_ptr(value);
1986 return ptr->len;
1987}
1988
1989static inline uint16_t rope_depth(ant_value_t value) {
1990 ant_rope_heap_t *ptr = assert_rope_ptr(value);
1991 return ptr->depth;
1992}
1993
1994static inline ant_value_t rope_left(ant_value_t value) {
1995 ant_rope_heap_t *ptr = assert_rope_ptr(value);
1996 return ptr->left;
1997}
1998
1999static inline ant_value_t rope_right(ant_value_t value) {
2000 ant_rope_heap_t *ptr = assert_rope_ptr(value);
2001 return ptr->right;
2002}
2003
2004static inline ant_value_t rope_cached_flat(ant_value_t value) {
2005 ant_rope_heap_t *ptr = assert_rope_ptr(value);
2006 return ptr->cached;
2007}
2008
2009static inline void rope_set_cached_flat(ant_value_t rope, ant_value_t flat) {
2010 ant_rope_heap_t *ptr = assert_rope_ptr(rope);
2011 ptr->cached = flat;
2012}
2013
2014static inline ant_string_builder_t *assert_builder_ptr(ant_value_t value) {
2015 assert(vtype(value) == T_STR);
2016 assert(str_is_heap_builder(value));
2017
2018 ant_string_builder_t *ptr = ant_str_builder_ptr(value);
2019 assert(ptr != NULL);
2020
2021 return ptr;
2022}
2023
2024static inline ant_offset_t builder_len(ant_value_t value) {
2025 ant_string_builder_t *ptr = assert_builder_ptr(value);
2026 return ptr->len;
2027}
2028
2029static inline ant_value_t builder_cached_flat(ant_value_t value) {
2030 ant_string_builder_t *ptr = assert_builder_ptr(value);
2031 return ptr->cached;
2032}
2033
2034static inline void builder_set_cached_flat(ant_value_t builder, ant_value_t flat) {
2035 ant_string_builder_t *ptr = assert_builder_ptr(builder);
2036 ptr->cached = flat;
2037}
2038
2039static void rope_flatten_into(ant_t *js, ant_value_t str, char *dest, ant_offset_t *pos) {
2040 assert(vtype(str) == T_STR);
2041
2042 if (!str_is_heap_rope(str)) {
2043 const char *sptr;
2044 ant_offset_t slen = assert_flat_string_len(str, &sptr);
2045 memcpy(dest + *pos, sptr, slen);
2046 *pos += slen; return;
2047 }
2048
2049 ant_value_t cached = rope_cached_flat(str);
2050 if (vtype(cached) == T_STR && !str_is_heap_rope(cached)) {
2051 const char *cptr;
2052 ant_offset_t clen = assert_flat_string_len(cached, &cptr);
2053 memcpy(dest + *pos, cptr, clen);
2054 *pos += clen; return;
2055 }
2056
2057 ant_value_t stack[ROPE_MAX_DEPTH + 8];
2058 int sp = 0; stack[sp++] = str;
2059
2060 while (sp > 0) {
2061 ant_value_t node = stack[--sp];
2062 assert(vtype(node) == T_STR);
2063
2064 if (!str_is_heap_rope(node)) {
2065 const char *sptr;
2066 ant_offset_t slen = assert_flat_string_len(node, &sptr);
2067 memcpy(dest + *pos, sptr, slen);
2068 *pos += slen; continue;
2069 }
2070
2071 ant_value_t c = rope_cached_flat(node);
2072 if (vtype(c) == T_STR && !str_is_heap_rope(c)) {
2073 const char *cptr;
2074 ant_offset_t clen = assert_flat_string_len(c, &cptr);
2075 memcpy(dest + *pos, cptr, clen);
2076 *pos += clen; continue;
2077 }
2078
2079 if (sp + 2 <= ROPE_MAX_DEPTH + 8) {
2080 stack[sp++] = rope_right(node);
2081 stack[sp++] = rope_left(node);
2082 }
2083 }
2084}
2085
2086ant_value_t rope_flatten(ant_t *js, ant_value_t rope) {
2087 assert(vtype(rope) == T_STR);
2088 if (!str_is_heap_rope(rope)) return rope;
2089
2090 GC_ROOT_SAVE(root_mark, js);
2091 GC_ROOT_PIN(js, rope);
2092
2093 ant_value_t cached = rope_cached_flat(rope);
2094 GC_ROOT_PIN(js, cached);
2095 if (vtype(cached) == T_STR && !str_is_heap_rope(cached)) {
2096 GC_ROOT_RESTORE(js, root_mark);
2097 return cached;
2098 }
2099
2100 ant_offset_t total_len = rope_len(rope);
2101 ant_value_t flat = js_mkstr(js, NULL, total_len);
2102 GC_ROOT_PIN(js, flat);
2103
2104 if (is_err(flat)) {
2105 GC_ROOT_RESTORE(js, root_mark);
2106 return flat;
2107 }
2108
2109 ant_flat_string_t *flat_ptr = (ant_flat_string_t *)(uintptr_t)vdata(flat);
2110 ant_offset_t pos = 0;
2111
2112 rope_flatten_into(js, rope, flat_ptr->bytes, &pos);
2113 flat_ptr->bytes[pos] = '\0';
2114 flat_ptr->is_ascii = str_detect_ascii_bytes(flat_ptr->bytes, (size_t)pos);
2115
2116 rope_set_cached_flat(rope, flat);
2117 GC_ROOT_RESTORE(js, root_mark);
2118
2119 return flat;
2120}
2121
2122static ant_value_t builder_flatten(ant_t *js, ant_value_t builder) {
2123 assert(vtype(builder) == T_STR);
2124 if (!str_is_heap_builder(builder)) return builder;
2125
2126 GC_ROOT_SAVE(root_mark, js);
2127 GC_ROOT_PIN(js, builder);
2128
2129 ant_value_t cached = builder_cached_flat(builder);
2130 GC_ROOT_PIN(js, cached);
2131
2132 if (vtype(cached) == T_STR && !str_is_heap_rope(cached) && !str_is_heap_builder(cached)) {
2133 GC_ROOT_RESTORE(js, root_mark);
2134 return cached;
2135 }
2136
2137 ant_string_builder_t *ptr = assert_builder_ptr(builder);
2138 ant_value_t flat = js_mkstr(js, NULL, (size_t)ptr->len);
2139
2140 GC_ROOT_PIN(js, flat);
2141 if (is_err(flat)) {
2142 GC_ROOT_RESTORE(js, root_mark);
2143 return flat;
2144 }
2145
2146 ant_flat_string_t *flat_ptr = (ant_flat_string_t *)(uintptr_t)vdata(flat);
2147 size_t cursor = 0;
2148
2149 for (ant_builder_chunk_t *chunk = ptr->head; chunk; chunk = chunk->next) {
2150 ant_value_t chunk_value = chunk->value;
2151 if (str_is_heap_rope(chunk_value) || str_is_heap_builder(chunk_value)) {
2152 chunk_value = str_materialize(js, chunk_value);
2153 if (is_err(chunk_value)) {
2154 GC_ROOT_RESTORE(js, root_mark);
2155 return chunk_value;
2156 }}
2157
2158 ant_flat_string_t *chunk_flat = ant_str_flat_ptr(chunk_value);
2159 if (!chunk_flat || chunk_flat->len == 0) continue;
2160 memcpy(flat_ptr->bytes + cursor, chunk_flat->bytes, (size_t)chunk_flat->len);
2161 cursor += (size_t)chunk_flat->len;
2162 }
2163
2164 if (ptr->tail_len > 0) {
2165 memcpy(flat_ptr->bytes + cursor, ptr->tail, ptr->tail_len);
2166 cursor += ptr->tail_len;
2167 }
2168
2169 flat_ptr->bytes[cursor] = '\0';
2170 flat_ptr->is_ascii = ptr->ascii_state;
2171 builder_set_cached_flat(builder, flat);
2172 GC_ROOT_RESTORE(js, root_mark);
2173
2174 return flat;
2175}
2176
2177ant_value_t str_materialize(ant_t *js, ant_value_t value) {
2178 if (vtype(value) != T_STR) return value;
2179 if (str_is_heap_rope(value)) return rope_flatten(js, value);
2180 if (str_is_heap_builder(value)) return builder_flatten(js, value);
2181 return value;
2182}
2183
2184ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len) {
2185 if (str_is_heap_rope(value) || str_is_heap_builder(value)) {
2186 ant_value_t flat = str_materialize(js, value);
2187 assert(!is_err(flat));
2188 value = flat;
2189 }
2190
2191 const char *ptr = NULL;
2192 ant_offset_t slen = assert_flat_string_len(value, &ptr);
2193 if (len) *len = slen;
2194 return (ant_offset_t)(uintptr_t)ptr;
2195}
2196
2197static size_t strstring(ant_t *js, ant_value_t value, char *buf, size_t len) {
2198 ant_offset_t slen, off = vstr(js, value, &slen);
2199 const char *str = (const char *)(uintptr_t)off;
2200
2201 size_t n = 0;
2202 size_t avail = 0;
2203 char *dst = fixed_buf_write_ptr(buf, len, n, &avail);
2204
2205 n += cpy(dst, avail, "'", 1);
2206
2207 for (ant_offset_t i = 0; i < slen; i++) {
2208 char c = str[i];
2209 const char *escaped = NULL;
2210
2211 dst = fixed_buf_write_ptr(buf, len, n, &avail);
2212 switch (c) {
2213 case '\n': escaped = "\\n"; break;
2214 case '\r': escaped = "\\r"; break;
2215 case '\t': escaped = "\\t"; break;
2216 case '\\': escaped = "\\\\"; break;
2217 case '\'': escaped = "\\'"; break;
2218 default: break;
2219 }
2220
2221 if (escaped) {
2222 n += cpy(dst, avail, escaped, 2);
2223 continue;
2224 }
2225
2226 if (avail > 1) {
2227 *dst = c;
2228 dst[1] = '\0';
2229 } else if (avail == 1) *dst = '\0';
2230 n++;
2231 }
2232
2233 dst = fixed_buf_write_ptr(buf, len, n, &avail);
2234 n += cpy(dst, avail, "'", 1);
2235
2236 return n;
2237}
2238
2239const char *intern_string(const char *str, size_t len) {
2240 if (!intern_table_init()) return NULL;
2241
2242 if ((intern_count + 1) * INTERN_LOAD_DEN >= intern_bucket_count * INTERN_LOAD_NUM) {
2243 size_t next_bucket_count = intern_bucket_count << 1;
2244 if (next_bucket_count > intern_bucket_count) intern_table_rehash(next_bucket_count);
2245 }
2246
2247 uint64_t h = hash_key(str, len);
2248 size_t bucket = (size_t)(h & (intern_bucket_count - 1));
2249
2250 for (interned_string_t *e = intern_buckets[bucket]; e; e = e->next) {
2251 if (e->hash == h && e->len == len && memcmp(e->str, str, len) == 0) return e->str;
2252 }
2253
2254 size_t alloc_size = sizeof(interned_string_t) + len + 1;
2255 interned_string_t *entry = (interned_string_t *)ant_calloc(alloc_size);
2256 if (!entry) return NULL;
2257
2258 entry->str = (char *)(entry + 1);
2259 memcpy(entry->str, str, len);
2260 entry->str[len] = '\0';
2261 entry->len = len;
2262 entry->hash = h;
2263 entry->next = intern_buckets[bucket];
2264 intern_buckets[bucket] = entry;
2265
2266 intern_count++;
2267 intern_bytes += alloc_size;
2268
2269 return entry->str;
2270}
2271
2272js_intern_stats_t js_intern_stats(void) {
2273 return (js_intern_stats_t){
2274 .count = intern_count,
2275 .bytes = intern_bytes
2276 };
2277}
2278
2279struct func_format {
2280 const char *prefix;
2281 size_t prefix_len;
2282 const char *anon;
2283 size_t anon_len;
2284};
2285
2286// TODO: migrate to Symbol.inspect
2287static const struct func_format formats[] = {
2288 [0] = { "[Function: ", 11, "[Function (anonymous)]", 22 },
2289 [1] = { "[AsyncFunction: ", 16, "[AsyncFunction (anonymous)]", 27 },
2290 [2] = { "[GeneratorFunction: ", 20, "[GeneratorFunction (anonymous)]", 31 },
2291 [3] = { "[AsyncGeneratorFunction: ", 25, "[AsyncGeneratorFunction (anonymous)]", 36 },
2292};
2293
2294// todo: make it work with bytecode NAME
2295static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) {
2296 ant_offset_t name_len = 0;
2297 const char *name = get_func_name(js, value, &name_len);
2298
2299 ant_value_t func_obj = js_func_obj(value);
2300 ant_value_t code_slot = get_slot(func_obj, SLOT_CODE);
2301 ant_value_t builtin_slot = get_slot(func_obj, SLOT_BUILTIN);
2302 ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC);
2303 sv_closure_t *closure = js_func_closure(value);
2304
2305 bool is_async = (async_slot == js_true);
2306 bool has_code = (vtype(code_slot) == T_NTARG);
2307 bool is_generator = closure != NULL && closure->func != NULL && closure->func->is_generator;
2308
2309 const struct func_format *fmt = &formats[(is_generator ? 2 : 0) | (is_async ? 1 : 0)];
2310
2311 if (vtype(builtin_slot) == T_NUM) {
2312 if (name && name_len > 0) {
2313 size_t n = cpy(buf, len, fmt->prefix, fmt->prefix_len);
2314 n += cpy(buf + n, REMAIN(n, len), name, name_len);
2315 n += cpy(buf + n, REMAIN(n, len), "]", 1);
2316 return n;
2317 }
2318 return cpy(buf, len, fmt->anon, fmt->anon_len);
2319 }
2320
2321 if (!has_code) {
2322 ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC);
2323 bool is_native = (vtype(cfunc_slot) == T_CFUNC);
2324 size_t n;
2325
2326 if (name && name_len > 0) {
2327 n = cpy(buf, len, fmt->prefix, fmt->prefix_len);
2328 n += cpy(buf + n, REMAIN(n, len), name, name_len);
2329 n += cpy(buf + n, REMAIN(n, len), "]", 1);
2330 } else {
2331 n = cpy(buf, len, fmt->anon, fmt->anon_len);
2332 }
2333
2334 if (!is_native) return n;
2335
2336 ant_value_t proto = get_slot(func_obj, SLOT_PROTO);
2337 uint8_t pt = vtype(proto);
2338 if (pt != T_OBJ && pt != T_FUNC) return n;
2339
2340 ant_value_t ctor = lkp_val(js, proto, "constructor", 11);
2341 uint8_t ct = vtype(ctor);
2342 if (ct != T_FUNC && ct != T_CFUNC) return n;
2343
2344 ant_offset_t ctor_name_len = 0;
2345 const char *ctor_name = get_func_name(js, ctor, &ctor_name_len);
2346 if (ctor_name && ctor_name_len > 0) {
2347 n += cpy(buf + n, REMAIN(n, len), " ", 1);
2348 n += cpy(buf + n, REMAIN(n, len), ctor_name, ctor_name_len);
2349 }
2350 return n;
2351 }
2352
2353 if (name && name_len > 0) {
2354 size_t n = cpy(buf, len, fmt->prefix, fmt->prefix_len);
2355 n += cpy(buf + n, REMAIN(n, len), name, name_len);
2356 n += cpy(buf + n, REMAIN(n, len), "]", 1);
2357 return n;
2358 }
2359
2360 return cpy(buf, len, fmt->anon, fmt->anon_len);
2361}
2362
2363static size_t strcfunc(ant_t *js, ant_value_t value, char *buf, size_t len) {
2364 ant_offset_t name_len = 0;
2365 const char *name = get_func_name(js, value, &name_len);
2366 const struct func_format *fmt = &formats[0];
2367
2368 if (name && name_len > 0) {
2369 size_t n = cpy(buf, len, fmt->prefix, fmt->prefix_len);
2370 n += cpy(buf + n, REMAIN(n, len), name, name_len);
2371 n += cpy(buf + n, REMAIN(n, len), "]", 1);
2372 return n;
2373 }
2374
2375 return cpy(buf, len, fmt->anon, fmt->anon_len);
2376}
2377
2378static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len) {
2379 switch (vtype(value)) {
2380 case T_UNDEF: return ANT_COPY(buf, len, "undefined");
2381 case T_NULL: return ANT_COPY(buf, len, "null");
2382
2383 case T_BOOL: {
2384 bool b = vdata(value) & 1;
2385 return b ? ANT_COPY(buf, len, "true") : ANT_COPY(buf, len, "false");
2386 }
2387
2388 case T_ARR: return strarr(js, value, buf, len);
2389 case T_OBJ: return strobj(js, value, buf, len);
2390 case T_STR: return strstring(js, value, buf, len);
2391 case T_NUM: return strnum(value, buf, len);
2392 case T_BIGINT: return strbigint(js, value, buf, len);
2393 case T_PROMISE: return strpromise(js, value, buf, len);
2394 case T_FUNC: return strfunc(js, value, buf, len);
2395 case T_CFUNC: return strcfunc(js, value, buf, len);
2396
2397 case T_ERR: {
2398 uint64_t data = vdata(value);
2399 if (data != 0) {
2400 ant_value_t obj = mkval(T_OBJ, data);
2401 ant_value_t stack = js_get(js, obj, "stack");
2402 if (vtype(stack) == T_STR) {
2403 ant_offset_t slen;
2404 ant_offset_t off = vstr(js, stack, &slen);
2405 return cpy(buf, len, (const char *)(uintptr_t)(off), slen);
2406 }
2407 }
2408 return ANT_COPY(buf, len, "Error");
2409 }
2410
2411 case T_SYMBOL: {
2412 const char *desc = js_sym_desc(value);
2413 if (desc) return (size_t) snprintf(buf, len, "Symbol(%s)", desc);
2414 return ANT_COPY(buf, len, "Symbol()");
2415 }
2416
2417 default: return (size_t) snprintf(buf, len, "VTYPE%d", vtype(value));
2418 }
2419}
2420
2421static char *tostr_alloc(ant_t *js, ant_value_t value) {
2422 size_t cap = 64;
2423 char *buf = ant_calloc(cap);
2424 size_t n = tostr(js, value, buf, cap);
2425 if (n >= cap) {
2426 free(buf);
2427 buf = ant_calloc(n + 1);
2428 tostr(js, value, buf, n + 1);
2429 }
2430 return buf;
2431}
2432
2433js_cstr_t js_to_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size) {
2434 js_cstr_t out = { .ptr = "", .len = 0, .needs_free = false };
2435
2436 if (is_err(value)) {
2437 uint64_t data = vdata(value);
2438 if (data != 0) {
2439 ant_value_t obj = mkval(T_OBJ, data);
2440 ant_value_t stack = js_get(js, obj, "stack");
2441 if (vtype(stack) == T_STR) {
2442 ant_offset_t slen;
2443 ant_offset_t off = vstr(js, stack, &slen);
2444 out.ptr = (const char *)(uintptr_t)(off);
2445 out.len = slen;
2446 return out;
2447 }
2448 }
2449 out.ptr = "Error";
2450 out.len = 5;
2451 return out;
2452 }
2453
2454 if (vtype(value) == T_STR) {
2455 size_t len = 0;
2456 char *str = js_getstr(js, value, &len);
2457 out.ptr = str ? str : ""; out.len = len;
2458 return out;
2459 }
2460
2461 multiref_count = 0;
2462 multiref_next_id = 0;
2463 stringify_depth = 0;
2464 scan_refs(js, value);
2465
2466 size_t capacity = stack_size;
2467 char *buf = stack_buf;
2468 out.needs_free = false;
2469
2470 if (!buf || capacity == 0) {
2471 capacity = 64;
2472 buf = ant_calloc(capacity);
2473 if (!buf) return out;
2474 out.needs_free = true;
2475 }
2476
2477 for (;;) {
2478 stringify_depth = 0;
2479 stringify_indent = 0;
2480 size_t len = tostr(js, value, buf, capacity);
2481
2482 if (len < capacity - 1) {
2483 out.ptr = buf;
2484 out.len = len;
2485 return out;
2486 }
2487
2488 size_t new_capacity = capacity * 2;
2489 char *next = out.needs_free
2490 ? ant_realloc(buf, new_capacity)
2491 : ant_calloc(new_capacity);
2492
2493 if (!next) {
2494 if (out.needs_free) free(buf);
2495 out.ptr = ""; out.len = 0;
2496 out.needs_free = false;
2497 return out;
2498 }
2499
2500 if (!out.needs_free) {
2501 memcpy(next, buf, capacity);
2502 out.needs_free = true;
2503 }
2504
2505 buf = next;
2506 capacity = new_capacity;
2507 }
2508}
2509
2510ant_value_t js_tostring_val(ant_t *js, ant_value_t value) {
2511 uint8_t t = vtype(value);
2512 char *buf; size_t len, buflen;
2513
2514 static const void *jump_table[] = {
2515 [T_OBJ] = &&L_OBJ, [T_STR] = &&L_STR,
2516 [T_UNDEF] = &&L_UNDEF, [T_NULL] = &&L_NULL, [T_NUM] = &&L_NUM,
2517 [T_BOOL] = &&L_BOOL, [T_FUNC] = &&L_OBJ, [T_CFUNC] = &&L_OBJ,
2518 [T_ERR] = &&L_DEFAULT, [T_ARR] = &&L_OBJ,
2519 [T_PROMISE] = &&L_DEFAULT, [T_TYPEDARRAY] = &&L_DEFAULT,
2520 [T_BIGINT] = &&L_BIGINT, [T_SYMBOL] = &&L_DEFAULT,
2521 [T_GENERATOR] = &&L_DEFAULT
2522 };
2523
2524 if (t < sizeof(jump_table) / sizeof(jump_table[0])) goto *jump_table[t];
2525 goto L_DEFAULT;
2526
2527 L_STR: return value;
2528 L_UNDEF: return js_mkstr(js, "undefined", 9);
2529 L_NULL: return js_mkstr(js, "null", 4);
2530 L_BOOL: return vdata(value) ? js_mkstr(js, "true", 4) : js_mkstr(js, "false", 5);
2531 L_OBJ: return js_call_toString(js, value);
2532
2533 L_NUM: {
2534 buf = (char *)ant_calloc(32);
2535 len = strnum(value, buf, 32);
2536 ant_value_t result = js_mkstr(js, buf, len);
2537 free(buf); return result;
2538 }
2539
2540 L_BIGINT: {
2541 buflen = bigint_digits_len(js, value);
2542 buf = (char *)ant_calloc(buflen + 2);
2543 len = strbigint(js, value, buf, buflen + 2);
2544 ant_value_t result = js_mkstr(js, buf, len);
2545 free(buf); return result;
2546 }
2547
2548 L_DEFAULT: {
2549 buf = tostr_alloc(js, value);
2550 ant_value_t result = js_mkstr(js, buf, strlen(buf));
2551 free(buf); return result;
2552 }
2553}
2554
2555const char *js_str(ant_t *js, ant_value_t value) {
2556 if (is_err(value)) {
2557 uint64_t data = vdata(value);
2558 if (data != 0) {
2559 ant_value_t obj = mkval(T_OBJ, data);
2560 ant_value_t stack = js_get(js, obj, "stack");
2561 if (vtype(stack) == T_STR) {
2562 ant_offset_t slen, off = vstr(js, stack, &slen);
2563 return (const char *)(uintptr_t)off;
2564 }
2565 }
2566 return "Error";
2567 }
2568
2569 multiref_count = 0;
2570 multiref_next_id = 0;
2571 stringify_depth = 0;
2572 scan_refs(js, value);
2573
2574 size_t capacity = 4096;
2575 char *buf = (char *)ant_calloc(capacity);
2576 if (!buf) return "";
2577
2578 size_t len;
2579 for (;;) {
2580 stringify_depth = 0;
2581 stringify_indent = 0;
2582 len = tostr(js, value, buf, capacity);
2583
2584 if (len < capacity - 1) break;
2585
2586 capacity *= 2;
2587 buf = (char *)ant_realloc(buf, capacity);
2588 if (!buf) return "";
2589 }
2590
2591 ant_value_t str = js_mkstr(js, buf, len);
2592 free(buf);
2593
2594 if (is_err(str)) return "";
2595 ant_offset_t off = vstr(js, str, NULL);
2596 return (const char *)(uintptr_t)off;
2597}
2598
2599static inline ant_offset_t get_dense_buf(ant_value_t arr) {
2600 ant_object_t *ptr = js_obj_ptr(js_as_obj(arr));
2601 if (!ptr || !ptr->fast_array || !ptr->u.array.data || ptr->u.array.cap == 0) return 0;
2602 return (ant_offset_t)(uintptr_t)ptr;
2603}
2604
2605static inline ant_object_t *dense_obj(ant_offset_t doff) {
2606 if (!doff) return NULL;
2607 ant_object_t *ptr = (ant_object_t *)(uintptr_t)doff;
2608 if (!ptr || !ptr->fast_array || !ptr->u.array.data || ptr->u.array.cap == 0) return NULL;
2609 return ptr;
2610}
2611
2612static inline ant_value_t *dense_data(ant_offset_t doff) {
2613 ant_object_t *ptr = dense_obj(doff);
2614 return ptr ? ptr->u.array.data : NULL;
2615}
2616
2617static inline ant_offset_t dense_capacity(ant_offset_t doff) {
2618 ant_object_t *ptr = dense_obj(doff);
2619 return ptr ? (ant_offset_t)ptr->u.array.cap : 0;
2620}
2621
2622static inline ant_value_t dense_get(ant_offset_t doff, ant_offset_t idx) {
2623 ant_object_t *ptr = dense_obj(doff);
2624 if (!ptr || idx >= ptr->u.array.cap) return js_mkundef();
2625 return ptr->u.array.data[idx];
2626}
2627
2628static inline void dense_set(ant_t *js, ant_offset_t doff, ant_offset_t idx, ant_value_t val) {
2629 ant_object_t *ptr = dense_obj(doff);
2630 if (!ptr || idx >= ptr->u.array.cap) return;
2631 ptr->u.array.data[idx] = val;
2632 if (!is_empty_slot(val)) ptr->may_have_dense_elements = 1;
2633 gc_write_barrier(js, ptr, val);
2634}
2635
2636static ant_offset_t dense_grow(ant_t *js, ant_value_t arr, ant_offset_t needed) {
2637 ant_object_t *obj = js_obj_ptr(js_as_obj(arr));
2638 if (!obj) return 0;
2639
2640 ant_offset_t old_cap = obj->u.array.cap;
2641 ant_offset_t new_cap = old_cap ? old_cap : MAX_DENSE_INITIAL_CAP;
2642
2643 while (new_cap < needed) new_cap *= 2;
2644 ant_value_t *next = realloc(obj->u.array.data, sizeof(*next) * (size_t)new_cap);
2645 if (!next) return 0;
2646
2647 for (ant_offset_t i = old_cap; i < new_cap; i++) next[i] = T_EMPTY;
2648
2649 obj->u.array.data = next;
2650 obj->u.array.cap = (uint32_t)new_cap;
2651 if (obj->u.array.len > obj->u.array.cap) obj->u.array.len = obj->u.array.cap;
2652 obj->fast_array = 1;
2653 return (ant_offset_t)(uintptr_t)obj;
2654}
2655
2656// TODO: make get and set dry
2657static inline ant_value_t arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx) {
2658 ant_offset_t semantic_len = get_array_length(js, arr);
2659
2660 if (idx >= semantic_len) return js_mkundef();
2661 ant_offset_t doff = get_dense_buf(arr);
2662
2663 if (doff) {
2664 ant_offset_t len = dense_iterable_length(js, arr);
2665 if (idx < len) {
2666 ant_value_t v = dense_get(doff, idx);
2667 if (!is_empty_slot(v)) return v;
2668 }
2669 }
2670
2671 char idxstr[16];
2672 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx);
2673
2674 return lkp_val(js, arr, idxstr, idxlen);
2675}
2676
2677static inline void arr_set(ant_t *js, ant_value_t arr, ant_offset_t idx, ant_value_t val) {
2678 ant_offset_t doff = get_dense_buf(arr);
2679
2680 if (doff) {
2681 ant_offset_t len = dense_iterable_length(js, arr);
2682
2683 if (idx < len) {
2684 dense_set(js, doff, idx, val);
2685 return;
2686 }
2687
2688 ant_offset_t density_limit = len > 0 ? len * 4 : 64;
2689 if (idx >= density_limit) goto sparse;
2690
2691 ant_offset_t cap = dense_capacity(doff);
2692 if (idx >= cap) {
2693 doff = dense_grow(js, arr, idx + 1);
2694 if (doff == 0) goto sparse;
2695 }
2696
2697 if (idx > len) array_mark_may_have_holes(arr);
2698 for (ant_offset_t i = len; i < idx; i++) {
2699 ant_value_t v = dense_get(doff, i);
2700 if (!is_empty_slot(v) && vtype(v) == T_UNDEF) dense_set(js, doff, i, T_EMPTY);
2701 }
2702
2703 dense_set(js, doff, idx, val);
2704 array_len_set(js, arr, idx + 1);
2705
2706 return;
2707 }
2708
2709 sparse:;
2710 char idxstr[24];
2711 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (uint64_t)idx);
2712 ant_value_t key = js_mkstr(js, idxstr, idxlen);
2713
2714 js_setprop(js, arr, key, val);
2715}
2716
2717static inline bool arr_has(ant_t *js, ant_value_t arr, ant_offset_t idx) {
2718 ant_offset_t semantic_len = get_array_length(js, arr);
2719 if (idx >= semantic_len) return false;
2720 ant_offset_t doff = get_dense_buf(arr);
2721
2722 if (doff) {
2723 ant_offset_t len = dense_iterable_length(js, arr);
2724 if (idx < len) {
2725 ant_value_t v = dense_get(doff, idx);
2726 if (!is_empty_slot(v)) return true;
2727 }
2728 }
2729
2730 char idxstr[16];
2731 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx);
2732
2733 return lkp(js, arr, idxstr, idxlen) != 0;
2734}
2735
2736enum { ANT_ARGUMENTS_NATIVE_TAG = 0x41524753u }; // ARGS
2737
2738typedef struct {
2739 sv_vm_t *vm;
2740 int frame_index;
2741 uint32_t mapped_count;
2742 uint8_t in_setter;
2743 uint8_t deleted[];
2744} ant_arguments_state_t;
2745
2746static inline ant_arguments_state_t *js_arguments_state(ant_value_t obj) {
2747 return (ant_arguments_state_t *)js_get_native(obj, ANT_ARGUMENTS_NATIVE_TAG);
2748}
2749
2750static ant_value_t js_arguments_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
2751 ant_offset_t arr_len = get_array_length(js, obj);
2752 unsigned long idx = 0;
2753
2754 if (!parse_array_index(key, key_len, arr_len, &idx)) return js_mkundef();
2755 if ((ant_offset_t)idx >= arr_len) return js_mkundef();
2756
2757 ant_arguments_state_t *state = js_arguments_state(obj);
2758 if (
2759 state && state->frame_index >= 0 &&
2760 (uint32_t)idx < state->mapped_count &&
2761 !state->deleted[idx]
2762 ) {
2763 sv_frame_t *frame = &state->vm->frames[state->frame_index];
2764 return frame->bp[idx];
2765 }
2766
2767 return arr_get(js, obj, (ant_offset_t)idx);
2768}
2769
2770static bool js_arguments_setter(
2771 ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value
2772) {
2773 unsigned long idx = 0;
2774 if (!parse_array_index(key, key_len, (ant_offset_t)-1, &idx)) return false;
2775
2776 ant_arguments_state_t *state = js_arguments_state(obj);
2777 if (state) state->in_setter = 1;
2778 arr_set(js, obj, (ant_offset_t)idx, value);
2779
2780 if (state) state->in_setter = 0;
2781 if (
2782 state && state->frame_index >= 0 &&
2783 (uint32_t)idx < state->mapped_count &&
2784 !state->deleted[idx]
2785 ) {
2786 sv_frame_t *frame = &state->vm->frames[state->frame_index];
2787 frame->bp[idx] = value;
2788 }
2789
2790 return true;
2791}
2792
2793static bool js_arguments_deleter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
2794 unsigned long idx = 0;
2795 if (!parse_array_index(key, key_len, (ant_offset_t)-1, &idx)) return false;
2796
2797 ant_arguments_state_t *state = js_arguments_state(obj);
2798 if (state && (uint32_t)idx < state->mapped_count) state->deleted[idx] = 1;
2799 return true;
2800}
2801
2802static void js_arguments_finalizer(ant_t *js, ant_object_t *obj) {
2803 ant_value_t value = js_obj_from_ptr(obj);
2804 free(js_get_native(value, ANT_ARGUMENTS_NATIVE_TAG));
2805 js_clear_native(value, ANT_ARGUMENTS_NATIVE_TAG);
2806}
2807
2808ant_value_t js_create_arguments_object(
2809 ant_t *js,
2810 ant_value_t callee,
2811 sv_frame_t *frame,
2812 int argc,
2813 int mapped_count,
2814 bool is_strict
2815) {
2816 GC_ROOT_SAVE(root_mark, js);
2817
2818 ant_value_t arr = js_mkarr(js);
2819 if (is_err(arr)) {
2820 GC_ROOT_RESTORE(js, root_mark);
2821 return arr;
2822 } GC_ROOT_PIN(js, arr);
2823
2824 if (frame && frame->bp && argc > 0) {
2825 for (int i = 0; i < argc; i++) js_arr_push(js, arr, frame->bp[i]);
2826 }
2827
2828 if (is_strict) js_set_slot(arr, SLOT_STRICT_ARGS, js_true);
2829 else if (vtype(callee) == T_FUNC) setprop_cstr(js, arr, "callee", 6, callee);
2830 js_set_sym(js, arr, get_toStringTag_sym(), js_mkstr(js, "Arguments", 9));
2831
2832 if (is_object_type(js->sym.array_proto)) {
2833 ant_value_t iter_fn = js_get_sym(js, js->sym.array_proto, get_iterator_sym());
2834 if (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC)
2835 js_set_sym(js, arr, get_iterator_sym(), iter_fn);
2836 }
2837
2838 if (!is_strict && mapped_count > 0 && frame && js->vm) {
2839 ant_arguments_state_t *state = calloc(
2840 1, sizeof(*state) + (size_t)mapped_count * sizeof(state->deleted[0]));
2841 if (!state) {
2842 GC_ROOT_RESTORE(js, root_mark);
2843 return js_mkerr(js, "oom");
2844 }
2845
2846 state->vm = js->vm;
2847 state->frame_index = (int)(frame - js->vm->frames);
2848 state->mapped_count = (uint32_t)mapped_count;
2849
2850 js_set_native(arr, state, ANT_ARGUMENTS_NATIVE_TAG);
2851 js_set_finalizer(arr, js_arguments_finalizer);
2852 js_set_getter(arr, js_arguments_getter);
2853 js_set_setter(arr, js_arguments_setter);
2854 js_set_deleter(arr, js_arguments_deleter);
2855 }
2856
2857 GC_ROOT_RESTORE(js, root_mark);
2858 return arr;
2859}
2860
2861void js_arguments_detach(ant_t *js, ant_value_t obj) {
2862 ant_arguments_state_t *state = js_arguments_state(obj);
2863 if (!state || state->frame_index < 0) return;
2864
2865 GC_ROOT_SAVE(root_mark, js);
2866 GC_ROOT_PIN(js, obj);
2867
2868 sv_frame_t *frame = &state->vm->frames[state->frame_index];
2869 ant_offset_t arr_len = get_array_length(js, obj);
2870 ant_offset_t limit = (ant_offset_t)state->mapped_count;
2871 if (arr_len < limit) limit = arr_len;
2872
2873 for (ant_offset_t i = 0; i < limit; i++) {
2874 if (state->deleted[i]) continue;
2875 state->in_setter = 1;
2876 arr_set(js, obj, i, frame->bp[i]);
2877 state->in_setter = 0;
2878 }
2879
2880 state->frame_index = -1;
2881 GC_ROOT_RESTORE(js, root_mark);
2882}
2883
2884void js_arguments_sync_slot(ant_t *js, ant_value_t obj, uint32_t idx, ant_value_t value) {
2885 ant_arguments_state_t *state = js_arguments_state(obj);
2886 if (!state || state->frame_index < 0 ||
2887 idx >= state->mapped_count || state->deleted[idx]) {
2888 return;
2889 }
2890
2891 state->in_setter = 1;
2892 arr_set(js, obj, (ant_offset_t)idx, value);
2893 state->in_setter = 0;
2894}
2895
2896static inline void arr_del(ant_t *js, ant_value_t arr, ant_offset_t idx) {
2897 ant_offset_t semantic_len = get_array_length(js, arr);
2898 if (idx >= semantic_len) return;
2899
2900 array_mark_may_have_holes(arr);
2901 ant_offset_t doff = get_dense_buf(arr);
2902
2903 if (doff) {
2904 ant_offset_t len = dense_iterable_length(js, arr);
2905 if (idx < len) dense_set(js, doff, idx, T_EMPTY);
2906 }
2907
2908 char idxstr[16];
2909 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx);
2910
2911 js_delete_prop(js, arr, idxstr, idxlen);
2912}
2913
2914ant_value_t js_mkstr(ant_t *js, const void *ptr, size_t len) {
2915 ant_flat_string_t *flat = (ant_flat_string_t *)js_type_alloc(
2916 js, ANT_ALLOC_STRING, sizeof(*flat) + len + 1, _Alignof(ant_flat_string_t)
2917 );
2918 if (!flat) return js_mkerr(js, "oom");
2919
2920 flat->len = (ant_offset_t)len;
2921 if (ptr && len > 0) memcpy(flat->bytes, ptr, len);
2922
2923 flat->bytes[len] = '\0';
2924 flat->is_ascii = (ptr || len == 0)
2925 ? str_detect_ascii_bytes(flat->bytes, len)
2926 : STR_ASCII_UNKNOWN;
2927
2928 return mkval(T_STR, (uintptr_t)flat);
2929}
2930
2931ant_value_t js_mkstr_permanent(ant_t *js, const void *ptr, size_t len) {
2932 size_t size = sizeof(ant_flat_string_t) + len + 1;
2933 size_t align = _Alignof(ant_flat_string_t);
2934
2935 if (js->pool.permanent.block_size == 0)
2936 js->pool.permanent.block_size = ANT_POOL_STRING_BLOCK_SIZE;
2937 ant_flat_string_t *flat = (ant_flat_string_t *)pool_alloc_chain(
2938 &js->pool.permanent.head, NULL, js->pool.permanent.block_size, size, align
2939 );
2940 if (!flat) return js_mkerr(js, "oom");
2941
2942 flat->len = (ant_offset_t)len;
2943 if (ptr && len > 0) memcpy(flat->bytes, ptr, len);
2944
2945 flat->bytes[len] = '\0';
2946 flat->is_ascii = (ptr || len == 0)
2947 ? str_detect_ascii_bytes(flat->bytes, len)
2948 : STR_ASCII_UNKNOWN;
2949
2950 return mkval(T_STR, (uintptr_t)flat);
2951}
2952
2953static ant_value_t js_mkrope(ant_t *js, ant_value_t left, ant_value_t right, ant_offset_t total_len, uint16_t depth) {
2954 ant_rope_heap_t *rope = (ant_rope_heap_t *)js_type_alloc(
2955 js, ANT_ALLOC_ROPE, sizeof(*rope), _Alignof(ant_rope_heap_t)
2956 );
2957 if (!rope) return js_mkerr(js, "oom");
2958 rope->len = total_len;
2959 rope->depth = depth;
2960 rope->left = left;
2961 rope->right = right;
2962 rope->cached = js_mkundef();
2963 return ant_mkrope_value(rope);
2964}
2965
2966
2967static ant_value_t mkobj_with_inobj_limit(ant_t *js, ant_offset_t parent, uint8_t inobj_limit) {
2968 (void)parent;
2969 ant_object_t *obj = obj_alloc(js, T_OBJ, inobj_limit);
2970 if (!obj) return js_mkerr(js, "oom");
2971 return mkval(T_OBJ, (uintptr_t)obj);
2972}
2973
2974ant_value_t mkobj(ant_t *js, ant_offset_t parent) {
2975 return mkobj_with_inobj_limit(js, parent, (uint8_t)ANT_INOBJ_MAX_SLOTS);
2976}
2977
2978ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit) {
2979 return mkobj_with_inobj_limit(js, 0, inobj_limit);
2980}
2981
2982static ant_value_t alloc_array_with_proto(ant_t *js, ant_value_t proto) {
2983 ant_object_t *obj = obj_alloc(js, T_ARR, (uint8_t)ANT_INOBJ_MAX_SLOTS);
2984 if (!obj) return js_mkerr(js, "oom");
2985
2986 ant_value_t arr = mkval(T_ARR, (uintptr_t)obj);
2987 if (is_object_type(proto)) js_set_proto_init(arr, proto);
2988
2989 obj->u.array.cap = MAX_DENSE_INITIAL_CAP;
2990 obj->u.array.len = 0;
2991 obj->u.array.data = malloc(sizeof(*obj->u.array.data) * (size_t)obj->u.array.cap);
2992
2993 if (obj->u.array.data) {
2994 for (uint32_t i = 0; i < obj->u.array.cap; i++) obj->u.array.data[i] = T_EMPTY;
2995 obj->fast_array = 1;
2996 obj->may_have_holes = 0;
2997 obj->may_have_dense_elements = 0;
2998 } else {
2999 obj->u.array.cap = 0;
3000 obj->u.array.len = 0;
3001 obj->fast_array = 0;
3002 obj->may_have_holes = 1;
3003 obj->may_have_dense_elements = 1;
3004 }
3005
3006 return arr;
3007}
3008
3009static inline ant_value_t mkarr(ant_t *js) {
3010 return alloc_array_with_proto(js, js->sym.array_proto);
3011}
3012
3013ant_value_t js_mkarr(ant_t *js) {
3014 return mkarr(js);
3015}
3016
3017ant_value_t js_newobj(ant_t *js) {
3018 ant_value_t obj = mkobj(js, 0);
3019 js_set_proto_init(obj, js->sym.object_proto);
3020 return obj;
3021}
3022
3023ant_offset_t js_arr_len(ant_t *js, ant_value_t arr) {
3024 if (!array_obj_ptr(arr)) return 0;
3025 return get_array_length(js, arr);
3026}
3027
3028ant_value_t js_arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx) {
3029 if (vtype(arr) != T_ARR) return js_mkundef();
3030 return arr_get(js, arr, idx);
3031}
3032
3033static inline bool is_const_prop(ant_t *js, ant_offset_t propoff) {
3034 ant_prop_ref_t *ref = propref_get(js, propoff);
3035 if (!ref) return false;
3036 uint8_t attrs = ant_shape_get_attrs(ref->obj->shape, ref->slot);
3037 return (attrs & ANT_PROP_ATTR_WRITABLE) == 0;
3038}
3039
3040static inline const ant_shape_prop_t *prop_shape_meta(ant_t *js, ant_offset_t propoff) {
3041 ant_prop_ref_t *ref = propref_get(js, propoff);
3042 if (!ref || !ref->obj || !ref->obj->shape) return NULL;
3043 return ant_shape_prop_at(ref->obj->shape, ref->slot);
3044}
3045
3046static inline bool is_nonconfig_prop(ant_t *js, ant_offset_t propoff) {
3047 ant_prop_ref_t *ref = propref_get(js, propoff);
3048 if (!ref) return false;
3049 uint8_t attrs = ant_shape_get_attrs(ref->obj->shape, ref->slot);
3050 return (attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0;
3051}
3052
3053static void js_init_intern_cache(ant_t *js) {
3054 js->intern.length = intern_string("length", 6);
3055 js->intern.buffer = intern_string("buffer", 6);
3056 js->intern.prototype = intern_string("prototype", 9);
3057 js->intern.constructor = intern_string("constructor", 11);
3058 js->intern.name = intern_string("name", 4);
3059 js->intern.message = intern_string("message", 7);
3060 js->intern.done = intern_string("done", 4);
3061 js->intern.value = intern_string("value", 5);
3062 js->intern.get = intern_string("get", 3);
3063 js->intern.set = intern_string("set", 3);
3064 js->intern.arguments = intern_string("arguments", 9);
3065 js->intern.callee = intern_string("callee", 6);
3066 js->intern.idx[0] = intern_string("0", 1);
3067 js->intern.idx[1] = intern_string("1", 1);
3068 js->intern.idx[2] = intern_string("2", 1);
3069 js->intern.idx[3] = intern_string("3", 1);
3070 js->intern.idx[4] = intern_string("4", 1);
3071 js->intern.idx[5] = intern_string("5", 1);
3072 js->intern.idx[6] = intern_string("6", 1);
3073 js->intern.idx[7] = intern_string("7", 1);
3074 js->intern.idx[8] = intern_string("8", 1);
3075 js->intern.idx[9] = intern_string("9", 1);
3076}
3077
3078ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs) {
3079 obj = js_as_obj(obj);
3080 ant_object_t *ptr = js_obj_ptr(obj);
3081
3082 if (!ptr || !ptr->shape) return js_mkerr(js, "invalid object");
3083 if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT;
3084
3085 uint32_t slot = 0;
3086 bool added = false;
3087 if (vtype(k) == T_SYMBOL) {
3088 ant_offset_t sym_off = (ant_offset_t)vdata(k);
3089 int32_t found = ant_shape_lookup_symbol(ptr->shape, sym_off);
3090 if (found >= 0) {
3091 slot = (uint32_t)found;
3092 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
3093 ant_shape_set_attrs_symbol(ptr->shape, sym_off, attrs);
3094 } else {
3095 if (!ant_shape_add_symbol_tr(&ptr->shape, sym_off, attrs, &slot)) {
3096 return js_mkerr(js, "oom");
3097 }
3098 added = true;
3099 }
3100 } else {
3101 ant_offset_t klen = 0;
3102 ant_offset_t koff = vstr(js, k, &klen);
3103 const char *p = (const char *)(uintptr_t)(koff);
3104 const char *interned = intern_string(p, klen);
3105 if (!interned) return js_mkerr(js, "oom");
3106 v = js_expose_cfunc_for_key(js, v, interned, (size_t)klen);
3107 if (is_err(v)) return v;
3108
3109 int32_t found = ant_shape_lookup_interned(ptr->shape, interned);
3110 if (found >= 0) {
3111 slot = (uint32_t)found;
3112 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
3113 ant_shape_set_attrs_interned(ptr->shape, interned, attrs);
3114 } else {
3115 if (!ant_shape_add_interned_tr(&ptr->shape, interned, attrs, &slot)) return js_mkerr(js, "oom");
3116 added = true;
3117 }
3118 }
3119
3120 if (added && !js_obj_ensure_prop_capacity(ptr, ant_shape_count(ptr->shape))) {
3121 return js_mkerr(js, "oom");
3122 }
3123
3124 if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) {
3125 return js_mkerr(js, "oom");
3126 }
3127
3128 ant_object_prop_set_unchecked(ptr, slot, v);
3129 gc_write_barrier(js, ptr, v);
3130
3131 return v;
3132}
3133
3134static ant_value_t mkprop_interned_impl(
3135 ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs, bool expose_cfunc
3136) {
3137 obj = js_as_obj(obj);
3138 ant_object_t *ptr = js_obj_ptr(obj);
3139
3140 if (!ptr || !ptr->shape || !interned_key) return js_mkerr(js, "invalid object");
3141 if (expose_cfunc) {
3142 v = js_expose_cfunc_for_key(js, v, interned_key, strlen(interned_key));
3143 if (is_err(v)) return v;
3144 }
3145
3146 if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT;
3147
3148 uint32_t slot = 0;
3149 bool added = false;
3150 int32_t found = ant_shape_lookup_interned(ptr->shape, interned_key);
3151
3152 if (found >= 0) {
3153 slot = (uint32_t)found;
3154 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
3155 ant_shape_set_attrs_interned(ptr->shape, interned_key, attrs);
3156 } else {
3157 if (!ant_shape_add_interned_tr(&ptr->shape, interned_key, attrs, &slot)) return js_mkerr(js, "oom");
3158 added = true;
3159 }
3160
3161 if (added && !js_obj_ensure_prop_capacity(ptr, ant_shape_count(ptr->shape))) {
3162 return js_mkerr(js, "oom");
3163 }
3164 if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) {
3165 return js_mkerr(js, "oom");
3166 }
3167
3168 ant_object_prop_set_unchecked(ptr, slot, v);
3169 gc_write_barrier(js, ptr, v);
3170
3171 return v;
3172}
3173
3174ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) {
3175 return mkprop_interned_impl(js, obj, interned_key, v, attrs, true);
3176}
3177
3178ant_value_t mkprop_interned_exact(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) {
3179 return mkprop_interned_impl(js, obj, interned_key, v, attrs, false);
3180}
3181
3182ant_value_t js_mkprop_fast(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) {
3183 const char *interned = intern_string(key, len);
3184 if (!interned) return js_mkerr(js, "oom");
3185 return mkprop_interned(js, obj, interned, v, 0);
3186}
3187
3188ant_offset_t js_mkprop_fast_off(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) {
3189 const char *interned = intern_string(key, len);
3190 if (!interned) return 0;
3191 ant_value_t prop = mkprop_interned(js, obj, interned, v, 0);
3192 if (is_err(prop)) return 0;
3193 return lkp_interned(js, obj, interned, len);
3194}
3195
3196void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v) {
3197 bool ok = propref_store(js, off, v);
3198 assert(ok && "js_saveval expects a valid property handle");
3199}
3200
3201static void set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t val) {
3202 ant_object_t *ptr = js_obj_ptr(obj);
3203 if (!ptr || slot < 0 || slot > SLOT_MAX) return;
3204 if (slot == SLOT_PROTO) {
3205 ptr->proto = val;
3206 ant_ic_epoch_bump();
3207 return;
3208 }
3209 if (slot == SLOT_DATA) {
3210 ptr->u.data.value = val;
3211 return;
3212 }
3213 (void)obj_extra_set(ptr, slot, val);
3214}
3215
3216static void set_slot_wb(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t val) {
3217 ant_object_t *ptr = js_obj_ptr(obj);
3218 if (!ptr || slot < 0 || slot > SLOT_MAX) return;
3219 if (slot == SLOT_PROTO) {
3220 ptr->proto = val;
3221 gc_write_barrier(js, ptr, val);
3222 ant_ic_epoch_bump();
3223 return;
3224 }
3225 if (slot == SLOT_DATA) {
3226 ptr->u.data.value = val;
3227 gc_write_barrier(js, ptr, val);
3228 return;
3229 }
3230 (void)obj_extra_set(ptr, slot, val);
3231 gc_write_barrier(js, ptr, val);
3232}
3233
3234void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value) {
3235 set_slot(js_as_obj(obj), slot, value);
3236}
3237
3238void js_set_slot_wb(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t value) {
3239 set_slot_wb(js, js_as_obj(obj), slot, value);
3240}
3241
3242static ant_value_t get_slot(ant_value_t obj, internal_slot_t slot) {
3243 ant_object_t *ptr = js_obj_ptr(obj);
3244 if (!ptr || slot < 0 || slot > SLOT_MAX) return js_mkundef();
3245 if (slot == SLOT_PROTO) return ptr->proto;
3246 if (slot == SLOT_DATA) {
3247 return ptr->u.data.value;
3248 }
3249 return obj_extra_get(ptr, slot);
3250}
3251
3252static void set_func_code_ptr(ant_t *js, ant_value_t func_obj, const char *code, size_t len) {
3253 set_slot(func_obj, SLOT_CODE, mkval(T_NTARG, (size_t)code));
3254 set_slot(func_obj, SLOT_CODE_LEN, tov((double)len));
3255}
3256
3257static void set_func_code(ant_t *js, ant_value_t func_obj, const char *code, size_t len) {
3258 const char *arena_code = code_arena_alloc(code, len);
3259 if (!arena_code) return;
3260 set_func_code_ptr(js, func_obj, arena_code, len);
3261}
3262
3263static const char *get_func_code(ant_t *js, ant_value_t func_obj, ant_offset_t *len) {
3264 ant_value_t code_val = get_slot(func_obj, SLOT_CODE);
3265 ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN);
3266
3267 if (vtype(code_val) != T_NTARG) {
3268 if (len) *len = 0;
3269 return NULL;
3270 }
3271
3272 if (len) *len = (ant_offset_t)tod(len_val);
3273 return (const char *)(uintptr_t)vdata(code_val);
3274}
3275
3276double js_to_number(ant_t *js, ant_value_t arg) {
3277 if (vtype(arg) == T_NULL) return 0.0;
3278 if (vtype(arg) == T_UNDEF) return JS_NAN;
3279
3280 if (vtype(arg) == T_NUM) return tod(arg);
3281 if (vtype(arg) == T_BOOL) return vdata(arg) ? 1.0 : 0.0;
3282 if (vtype(arg) == T_BIGINT) return bigint_to_double(js, arg);
3283
3284 if (vtype(arg) == T_STR) {
3285 ant_flat_string_t *flat = ant_str_flat_ptr(arg);
3286 const char *base = NULL;
3287 const char *end = NULL;
3288
3289 if (flat) {
3290 base = flat->bytes;
3291 end = base + flat->len;
3292 } else {
3293 ant_offset_t len = 0;
3294 ant_offset_t off = vstr(js, arg, &len);
3295 base = (const char *)(uintptr_t)off;
3296 end = base + len;
3297 }
3298
3299 double val = JS_NAN; if (
3300 ant_number_parse(base, (size_t)(end - base),
3301 ANT_NUMBER_PARSE_JS_NUMBER, &val, NULL)
3302 ) return val;
3303
3304 return JS_NAN;
3305 }
3306
3307 if (vtype(arg) == T_OBJ || vtype(arg) == T_ARR) {
3308 if (vtype(arg) == T_OBJ) {
3309 ant_value_t prim = js_call_valueOf(js, arg);
3310 uint8_t pt = vtype(prim);
3311 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) return js_to_number(js, prim);
3312 }
3313
3314 ant_value_t str_val = js_tostring_val(js, arg);
3315 if (is_err(str_val) || vtype(str_val) != T_STR) return JS_NAN;
3316 return js_to_number(js, str_val);
3317 }
3318
3319 return JS_NAN;
3320}
3321
3322static ant_value_t setup_func_prototype(ant_t *js, ant_value_t func) {
3323 return setup_func_prototype_property(js, func, true);
3324}
3325
3326static inline bool same_object_identity(ant_value_t a, ant_value_t b) {
3327 if (!is_object_type(a) || !is_object_type(b)) return false;
3328 return vdata(js_as_obj(a)) == vdata(js_as_obj(b));
3329}
3330
3331static inline ant_value_t proto_next_obj_or_null(ant_t *js, ant_value_t cur) {
3332 if (!is_object_type(cur)) return js_mknull();
3333 ant_value_t next = get_proto(js, cur);
3334 return is_object_type(next) ? next : js_mknull();
3335}
3336
3337typedef struct {
3338 int depth;
3339 bool overflow;
3340 ant_value_t fast;
3341} proto_overflow_guard_t;
3342
3343static inline void proto_overflow_guard_init(proto_overflow_guard_t *g) {
3344 g->depth = 0;
3345 g->overflow = false;
3346 g->fast = js_mknull();
3347}
3348
3349static inline bool proto_overflow_guard_hit_cycle(ant_t *js, proto_overflow_guard_t *g, ant_value_t cur) {
3350 if (!g->overflow) {
3351 if (++g->depth < MAX_PROTO_CHAIN_DEPTH) return false;
3352 g->overflow = true;
3353 g->fast = cur;
3354 }
3355
3356 g->fast = proto_next_obj_or_null(js, g->fast);
3357 g->fast = proto_next_obj_or_null(js, g->fast);
3358 return same_object_identity(cur, g->fast);
3359}
3360
3361static inline bool proto_walk_next(ant_t *js, ant_value_t *cur, uint8_t *t, uint8_t flags) {
3362 uint8_t ct = *t;
3363
3364 if (flags & PROTO_WALK_F_OBJECT_ONLY) {
3365 if (!is_object_type(*cur)) return false;
3366 ant_value_t next = get_proto(js, *cur);
3367 uint8_t nt = vtype(next);
3368 if (nt == T_NULL || nt == T_UNDEF || !is_object_type(next)) return false;
3369 *cur = next; *t = nt;
3370 return true;
3371 }
3372
3373 if (JS_TPFLG(ct) & T_OBJECT_MASK) {
3374 ant_value_t as_obj = js_as_obj(*cur);
3375 ant_value_t proto = get_slot(as_obj, SLOT_PROTO);
3376
3377 uint8_t pt = vtype(proto);
3378 if (pt == T_OBJ || pt == T_ARR || pt == T_FUNC) {
3379 *cur = proto;
3380 *t = pt;
3381 return true;
3382 }
3383
3384 if (JS_TPFLG(ct) & T_NEEDS_PROTO_FALLBACK) {
3385 ant_value_t fallback = get_prototype_for_type(js, ct);
3386 uint8_t ft = vtype(fallback);
3387 if (ft == T_NULL || ft == T_UNDEF) return false;
3388 *cur = fallback;
3389 *t = ft;
3390 return true;
3391 }
3392
3393 return false;
3394 }
3395
3396 if (ct == T_STR || ct == T_NUM || ct == T_BOOL || ct == T_BIGINT || ct == T_SYMBOL) {
3397 ant_value_t proto = get_prototype_for_type(js, ct);
3398 uint8_t pt = vtype(proto);
3399 if (pt == T_NULL || pt == T_UNDEF) return false;
3400 *cur = proto; *t = pt;
3401 return true;
3402 }
3403
3404 return false;
3405}
3406
3407typedef struct {
3408 int depth;
3409 bool overflow;
3410 bool fast_active;
3411 ant_value_t fast_cur;
3412 uint8_t fast_t;
3413} proto_walk_overflow_guard_t;
3414
3415static inline void proto_walk_overflow_guard_init(proto_walk_overflow_guard_t *g) {
3416 g->depth = 0;
3417 g->overflow = false;
3418 g->fast_active = false;
3419 g->fast_cur = js_mknull();
3420 g->fast_t = T_NULL;
3421}
3422
3423static inline bool proto_walk_overflow_guard_hit_cycle(
3424 ant_t *js,
3425 proto_walk_overflow_guard_t *g,
3426 ant_value_t cur,
3427 uint8_t cur_t,
3428 uint8_t flags
3429) {
3430 if (!g->overflow) {
3431 if (++g->depth < MAX_PROTO_CHAIN_DEPTH) return false;
3432 g->overflow = true;
3433 g->fast_active = true;
3434 g->fast_cur = cur;
3435 g->fast_t = cur_t;
3436 }
3437
3438 if (g->fast_active && !proto_walk_next(js, &g->fast_cur, &g->fast_t, flags))
3439 g->fast_active = false;
3440 if (g->fast_active && !proto_walk_next(js, &g->fast_cur, &g->fast_t, flags))
3441 g->fast_active = false;
3442
3443 return g->fast_active && same_object_identity(cur, g->fast_cur);
3444}
3445
3446ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_proto) {
3447 ant_value_t instance_proto = js_mkundef();
3448
3449 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) {
3450 ant_value_t nt_obj = js_as_obj(js->new_target);
3451 ant_value_t nt_proto = lkp_interned_val(js, nt_obj, js->intern.prototype);
3452 if (is_object_type(nt_proto)) instance_proto = nt_proto;
3453 }
3454
3455 if (!is_object_type(instance_proto) && is_object_type(fallback_proto))
3456 instance_proto = fallback_proto;
3457
3458 return instance_proto;
3459}
3460
3461bool proto_chain_contains(ant_t *js, ant_value_t obj, ant_value_t proto_target) {
3462 if (!is_object_type(obj) || !is_object_type(proto_target)) return false;
3463 ant_value_t cur = obj;
3464 uint8_t t = vtype(cur);
3465 proto_overflow_guard_t guard;
3466 proto_overflow_guard_init(&guard);
3467
3468 while (true) {
3469 if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_OBJECT_ONLY)) break;
3470 if (same_object_identity(cur, proto_target)) return true;
3471 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
3472 }
3473 return false;
3474}
3475
3476static inline bool is_wrapper_ctor_target(ant_t *js, ant_value_t this_val, ant_value_t expected_proto) {
3477 if (vtype(js->new_target) == T_UNDEF) return false;
3478 if (vtype(this_val) != T_OBJ) return false;
3479 if (vtype(get_slot(this_val, SLOT_PRIMITIVE)) != T_UNDEF) return false;
3480 return proto_chain_contains(js, this_val, expected_proto);
3481}
3482
3483ant_value_t get_ctor_species_value(ant_t *js, ant_value_t ctor) {
3484 if (!is_object_type(ctor) && vtype(ctor) != T_CFUNC) return js_mkundef();
3485 return js_get_sym(js, ctor, get_species_sym());
3486}
3487
3488bool same_ctor_identity(ant_t *js, ant_value_t a, ant_value_t b) {
3489 if (vtype(a) == vtype(b) && vdata(a) == vdata(b)) return true;
3490
3491 if (vtype(a) == T_FUNC && vtype(b) == T_CFUNC) {
3492 ant_value_t c = get_slot(a, SLOT_CFUNC);
3493 return vtype(c) == T_CFUNC && vdata(c) == vdata(b);
3494 }
3495
3496 if (vtype(a) == T_CFUNC && vtype(b) == T_FUNC) {
3497 ant_value_t c = get_slot(b, SLOT_CFUNC);
3498 return vtype(c) == T_CFUNC && vdata(c) == vdata(a);
3499 }
3500
3501 if (vtype(a) == T_FUNC && vtype(b) == T_FUNC) {
3502 ant_value_t ca = get_slot(a, SLOT_CFUNC);
3503 ant_value_t cb = get_slot(b, SLOT_CFUNC);
3504 if (vtype(ca) == T_CFUNC && vtype(cb) == T_CFUNC && vdata(ca) == vdata(cb)) return true;
3505 }
3506
3507 return false;
3508}
3509
3510static ant_value_t array_constructor_from_receiver(ant_t *js, ant_value_t receiver) {
3511 if (!is_object_type(receiver)) return js_mkundef();
3512
3513 ant_value_t species_source = receiver;
3514 if (is_proxy(species_source)) {
3515 species_source = proxy_read_target(js, species_source);
3516 }
3517
3518 bool receiver_is_array = (vtype(species_source) == T_ARR);
3519 if (!receiver_is_array) {
3520 ant_value_t array_proto = get_ctor_proto(js, "Array", 5);
3521 if (is_object_type(array_proto) && is_object_type(species_source)) {
3522 receiver_is_array = proto_chain_contains(js, species_source, array_proto);
3523 }
3524 }
3525 if (!receiver_is_array) return js_mkundef();
3526
3527 ant_value_t ctor = js_getprop_fallback(js, receiver, "constructor");
3528 if (is_err(ctor)) return ctor;
3529
3530 ant_value_t species = get_ctor_species_value(js, ctor);
3531 if (is_err(species)) return species;
3532
3533 if (vtype(species) == T_NULL) return js_mkundef();
3534 if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) return species;
3535 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) return js_mkundef();
3536
3537 return ctor;
3538}
3539
3540static ant_value_t array_alloc_from_ctor_with_length(ant_t *js, ant_value_t ctor, ant_offset_t length_hint) {
3541 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) {
3542 return mkarr(js);
3543 }
3544
3545 ant_value_t seed = js_mkobj(js);
3546 if (is_err(seed)) return seed;
3547
3548 ant_value_t proto = js_get(js, ctor, "prototype");
3549 if (is_err(proto)) return proto;
3550 if (is_object_type(proto)) js_set_proto_init(seed, proto);
3551
3552 ant_value_t ctor_args[1] = { tov((double)length_hint) };
3553 ant_value_t saved_new_target = js->new_target;
3554 js->new_target = ctor;
3555 ant_value_t constructed = sv_vm_call(js->vm, js, ctor, seed, ctor_args, 1, NULL, true);
3556 js->new_target = saved_new_target;
3557 if (is_err(constructed)) return constructed;
3558
3559 ant_value_t result = is_object_type(constructed) ? constructed : seed;
3560 set_slot(js_as_obj(result), SLOT_CTOR, ctor);
3561 return result;
3562}
3563
3564static inline ant_value_t array_alloc_from_ctor(ant_t *js, ant_value_t ctor) {
3565 return array_alloc_from_ctor_with_length(js, ctor, 0);
3566}
3567
3568static inline ant_value_t array_alloc_like(ant_t *js, ant_value_t receiver) {
3569 ant_value_t ctor = array_constructor_from_receiver(js, receiver);
3570 if (is_err(ctor)) return ctor;
3571 return array_alloc_from_ctor(js, ctor);
3572}
3573
3574static ant_value_t validate_array_length(ant_t *js, ant_value_t v) {
3575 if (vtype(v) != T_NUM) {
3576 return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid array length");
3577 }
3578 double d = tod(v);
3579 if (d < 0 || d != (uint32_t)d || d >= 4294967296.0) {
3580 return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid array length");
3581 }
3582 return js_mkundef();
3583}
3584
3585static inline ant_value_t check_object_extensibility(ant_t *js, ant_value_t obj) {
3586 obj = js_as_obj(obj);
3587 ant_object_t *ptr = js_obj_ptr(obj);
3588 if (!ptr) return js_mkundef();
3589
3590 if (ptr->frozen) {
3591 return sv_is_strict_context(js)
3592 ? js_mkerr(js, "cannot add property to frozen object")
3593 : js_false;
3594 }
3595
3596 if (ptr->sealed) {
3597 return sv_is_strict_context(js)
3598 ? js_mkerr(js, "cannot add property to sealed object")
3599 : js_false;
3600 }
3601
3602 if (!ptr->extensible) {
3603 return sv_is_strict_context(js)
3604 ? js_mkerr(js, "cannot add property to non-extensible object")
3605 : js_false;
3606 }
3607
3608 return js_mkundef();
3609}
3610
3611static inline void array_len_set(ant_t *js, ant_value_t obj, ant_offset_t new_len) {
3612 ant_object_t *arr_ptr = array_obj_ptr(obj);
3613 if (arr_ptr) {
3614 if (new_len > (ant_offset_t)UINT32_MAX) new_len = (ant_offset_t)UINT32_MAX;
3615 arr_ptr->u.array.len = (uint32_t)new_len;
3616 return;
3617 }
3618
3619 ant_value_t new_len_val = tov((double)new_len);
3620 ant_offset_t len_off = lkp_interned(js, obj, js->intern.length, 6);
3621
3622 if (len_off != 0) js_saveval(js, len_off, new_len_val);
3623 else js_mkprop_fast(js, obj, "length", 6, new_len_val);
3624}
3625
3626static ant_value_t js_setprop_array_fast(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t klen, const char *key) {
3627 unsigned long idx;
3628 if (!parse_array_index(key, klen, (ant_offset_t)-1, &idx)) return js_mkundef();
3629
3630 ant_offset_t cur_len = get_array_length(js, obj);
3631 ant_offset_t doff = get_dense_buf(obj);
3632 if (doff) {
3633 ant_offset_t dense_len = dense_iterable_length(js, obj);
3634 if (idx < dense_len) { dense_set(js, doff, (ant_offset_t)idx, v); return v; }
3635
3636 ant_offset_t density_limit = dense_len > 0 ? dense_len * 4 : 64;
3637 if (idx >= density_limit) goto sparse;
3638
3639 ant_value_t extensibility_error = check_object_extensibility(js, obj);
3640 if (is_err(extensibility_error)) return extensibility_error;
3641 if (extensibility_error == js_false) return v;
3642
3643 arr_set(js, obj, (ant_offset_t)idx, v);
3644 return v;
3645 }
3646
3647 sparse:;
3648 if (idx < cur_len) return js_mkundef();
3649
3650 ant_value_t extensibility_error = check_object_extensibility(js, obj);
3651 if (is_err(extensibility_error)) return extensibility_error;
3652 if (extensibility_error == js_false) return v;
3653
3654 ant_value_t result = mkprop(js, obj, k, v, 0);
3655 if (is_err(result)) return result;
3656 array_define_or_set_index(js, obj, key, (size_t)klen);
3657
3658 return v;
3659}
3660
3661static inline void prop_meta_defaults(prop_meta_t *out) {
3662 *out = (prop_meta_t){
3663 .has_getter = false,
3664 .has_setter = false,
3665 .writable = true,
3666 .enumerable = true,
3667 .configurable = true,
3668 .getter = js_mkundef(),
3669 .setter = js_mkundef(),
3670 };
3671}
3672
3673static inline void prop_meta_from_shape(prop_meta_t *out, const ant_shape_prop_t *prop) {
3674 out->has_getter = prop->has_getter != 0;
3675 out->has_setter = prop->has_setter != 0;
3676 out->writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0;
3677 out->enumerable = (prop->attrs & ANT_PROP_ATTR_ENUMERABLE) != 0;
3678 out->configurable = (prop->attrs & ANT_PROP_ATTR_CONFIGURABLE) != 0;
3679 out->getter = prop->getter;
3680 out->setter = prop->setter;
3681}
3682
3683static inline void prop_meta_from_desc(prop_meta_t *out, const descriptor_entry_t *desc) {
3684 out->has_getter = desc->has_getter;
3685 out->has_setter = desc->has_setter;
3686 out->writable = desc->writable;
3687 out->enumerable = desc->enumerable;
3688 out->configurable = desc->configurable;
3689 out->getter = desc->getter;
3690 out->setter = desc->setter;
3691}
3692
3693bool lookup_prop_meta(
3694 ant_t *js,
3695 ant_value_t cur_obj,
3696 prop_meta_key_t key_kind,
3697 const char *key,
3698 size_t klen,
3699 ant_offset_t sym_off,
3700 prop_meta_t *out
3701) {
3702 if (!out || !is_object_type(cur_obj)) return false;
3703 if (key_kind == PROP_META_STRING && !key) return false;
3704
3705 prop_meta_defaults(out);
3706
3707 ant_object_t *cur_ptr = js_obj_ptr(cur_obj);
3708 if (!cur_ptr) return false;
3709 if (key_kind == PROP_META_STRING && key && is_length_key(key, klen) &&
3710 cur_ptr->type_tag == T_ARR) {
3711 out->has_getter = false;
3712 out->has_setter = false;
3713 out->writable = true;
3714 out->enumerable = false;
3715 out->configurable = false;
3716 out->getter = js_mkundef();
3717 out->setter = js_mkundef();
3718 return true;
3719 }
3720
3721 if (cur_ptr->shape) {
3722 int32_t slot = -1;
3723 if (key_kind == PROP_META_SYMBOL) {
3724 slot = ant_shape_lookup_symbol(cur_ptr->shape, sym_off);
3725 } else {
3726 const char *interned_key = intern_string(key, klen);
3727 if (interned_key) slot = ant_shape_lookup_interned(cur_ptr->shape, interned_key);
3728 }
3729
3730 if (slot >= 0) {
3731 const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot);
3732 if (!prop) return false;
3733 prop_meta_from_shape(out, prop);
3734 return true;
3735 }
3736 }
3737
3738 if (!cur_ptr->is_exotic) return false;
3739
3740 descriptor_entry_t *desc = NULL;
3741 if (key_kind == PROP_META_SYMBOL) {
3742 desc = lookup_sym_descriptor(cur_obj, sym_off);
3743 } else if (js) {
3744 desc = lookup_descriptor(cur_obj, key, klen);
3745 }
3746
3747 if (!desc) return false;
3748 prop_meta_from_desc(out, desc);
3749 return true;
3750}
3751
3752bool js_try_get_own_data_prop(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t *out) {
3753 if (out) *out = js_mkundef();
3754 if (!key || !out) return false;
3755
3756 uint8_t t = vtype(obj);
3757 if (t == T_FUNC) obj = js_func_obj(obj);
3758 else if (t != T_OBJ && t != T_ARR) return false;
3759
3760 ant_value_t as_obj = js_as_obj(obj);
3761 if (is_proxy(as_obj)) return false;
3762
3763 prop_meta_t meta;
3764 bool has_meta = lookup_string_prop_meta(js, as_obj, key, key_len, &meta);
3765 if (has_meta && (meta.has_getter || meta.has_setter)) return false;
3766
3767 ant_offset_t off = lkp(js, as_obj, key, (ant_offset_t)key_len);
3768 if (off != 0) {
3769 *out = propref_load(js, off);
3770 return true;
3771 }
3772
3773 if (array_obj_ptr(as_obj) && is_length_key(key, (ant_offset_t)key_len)) {
3774 *out = tov((double)get_array_length(js, as_obj));
3775 return true;
3776 }
3777
3778 return false;
3779}
3780
3781static ant_value_t call_proto_accessor(
3782 ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor,
3783 ant_value_t *arg, int arg_count, bool is_setter
3784) {
3785 if (!has_accessor || (vtype(accessor) != T_FUNC && vtype(accessor) != T_CFUNC))
3786 return js_mkundef();
3787
3788 js_error_site_t saved_errsite = js->errsite;
3789 ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, accessor, prim, arg, arg_count, NULL, false);
3790
3791 bool had_throw = js->thrown_exists;
3792 ant_value_t thrown = js->thrown_value;
3793 js->errsite = saved_errsite;
3794
3795 if (had_throw) {
3796 js->thrown_exists = true;
3797 js->thrown_value = thrown;
3798 }
3799
3800 if (is_setter) return is_err(result) ? result : (arg ? *arg : js_mkundef());
3801 return result;
3802}
3803
3804// TODO: decompose into smaller helpers
3805ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) {
3806 uint8_t ot = vtype(obj);
3807
3808 if (ot == T_CFUNC) {
3809 ant_value_t promoted = js_cfunc_promote(js, obj);
3810 return js_setprop(js, promoted, k, v);
3811 }
3812
3813 if (ot == T_STR || ot == T_NUM || ot == T_BOOL) {
3814 ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen);
3815 const char *key = (char *)(uintptr_t)(koff);
3816
3817 ant_value_t proto = get_prototype_for_type(js, ot);
3818 if (is_object_type(proto)) {
3819 ant_value_t setter = js_mkundef();
3820 bool has_setter = false;
3821
3822 lkp_with_setter(js, proto, key, klen, &setter, &has_setter);
3823 if (has_setter && (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC)) {
3824 call_proto_accessor(js, obj, setter, true, &v, 1, true);
3825 return v;
3826 }}
3827
3828 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE,
3829 "Cannot create property '%.*s' on %s",
3830 (int)klen, key, typestr(ot)
3831 );
3832
3833 return v;
3834 }
3835
3836 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj);
3837 if (vtype(k) == T_SYMBOL) {
3838 ant_offset_t sym_off = (ant_offset_t)vdata(k);
3839 ant_value_t cur = obj;
3840 proto_overflow_guard_t guard;
3841 proto_overflow_guard_init(&guard);
3842
3843 while (is_object_type(cur)) {
3844 ant_value_t cur_obj = js_as_obj(cur);
3845 prop_meta_t meta;
3846 if (lookup_symbol_prop_meta(cur_obj, sym_off, &meta)) {
3847 if (meta.has_setter) {
3848 ant_value_t setter = meta.setter;
3849 if (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC) {
3850 ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, setter, obj, &v, 1, NULL, false);
3851 if (is_err(result)) return result;
3852 return v;
3853 }
3854 }
3855 if (meta.has_getter && !meta.has_setter) {
3856 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter");
3857 return v;
3858 }
3859 if (!meta.has_getter && !meta.has_setter && !meta.writable) {
3860 if (sv_is_strict_context(js)) return js_mkerr(js, "assignment to read-only property");
3861 return v;
3862 }
3863 break;
3864 }
3865
3866 ant_value_t proto = get_proto(js, cur_obj);
3867 if (!is_object_type(proto)) break;
3868 cur = proto;
3869 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
3870 }
3871
3872 ant_offset_t existing = lkp_sym(js, obj, sym_off);
3873
3874 if (existing > 0) {
3875 if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant");
3876 js_saveval(js, existing, v);
3877 return v;
3878 }
3879
3880 {
3881 ant_value_t extensibility_error = check_object_extensibility(js, obj);
3882 if (is_err(extensibility_error)) return extensibility_error;
3883 if (extensibility_error == js_false) return v;
3884 }
3885
3886 return mkprop(js, obj, k, v, 0);
3887 }
3888
3889 ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen);
3890 const char *key = (char *)(uintptr_t)(koff);
3891
3892 if (array_obj_ptr(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') {
3893 ant_arguments_state_t *args_state = js_arguments_state(obj);
3894 if (args_state && !args_state->in_setter && js_arguments_setter(js, obj, key, (size_t)klen, v)) return v;
3895 }
3896
3897 if (array_obj_ptr(obj) && !is_proxy(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') {
3898 ant_value_t result = js_setprop_array_fast(js, obj, k, v, klen, key);
3899 if (vtype(result) != T_UNDEF) return result;
3900 }
3901
3902 if (array_obj_ptr(obj) && is_length_key(key, klen)) {
3903 ant_value_t err = validate_array_length(js, v);
3904 if (is_err(err)) return err;
3905
3906 ant_offset_t doff = get_dense_buf(obj);
3907 ant_offset_t cur_len = get_array_length(js, obj);
3908 ant_offset_t new_len_val = (ant_offset_t) tod(v);
3909
3910 if (doff) {
3911 ant_offset_t cap = dense_capacity(doff);
3912 ant_offset_t clear_to = (cur_len < cap) ? cur_len : cap;
3913 if (new_len_val < clear_to)
3914 for (ant_offset_t i = new_len_val; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY);
3915 }
3916
3917 if (new_len_val > cur_len) array_mark_may_have_holes(obj);
3918 array_len_set(js, obj, new_len_val);
3919
3920 return v;
3921 }
3922
3923 if (is_proxy(obj)) {
3924 ant_value_t result = proxy_set(js, obj, key, klen, v);
3925 if (is_err(result)) return result;
3926 return v;
3927 }
3928
3929 if (try_dynamic_setter(js, obj, key, klen, v)) return v;
3930 ant_offset_t existing = lkp(js, obj, key, klen);
3931
3932 {
3933 const char *interned_key = intern_string(key, (size_t)klen);
3934 bool found_desc = false;
3935 bool desc_on_receiver = false;
3936 bool desc_has_getter = false;
3937 bool desc_has_setter = false;
3938 bool desc_writable = true;
3939 ant_value_t desc_setter = js_mkundef();
3940
3941 ant_value_t cur = obj;
3942 proto_overflow_guard_t guard;
3943 proto_overflow_guard_init(&guard);
3944 bool on_receiver = true;
3945 while (is_object_type(cur)) {
3946 ant_value_t cur_obj = js_as_obj(cur);
3947 ant_object_t *cur_ptr = js_obj_ptr(cur_obj);
3948 if (!cur_ptr) break;
3949
3950 bool found_here = false;
3951 if (cur_ptr->shape && interned_key) {
3952 int32_t slot = ant_shape_lookup_interned(cur_ptr->shape, interned_key);
3953 if (slot >= 0) {
3954 const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot);
3955 if (prop) {
3956 found_here = true;
3957 desc_has_getter = prop->has_getter != 0;
3958 desc_has_setter = prop->has_setter != 0;
3959 desc_writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0;
3960 desc_setter = prop->setter;
3961 }
3962 }
3963 }
3964
3965 if (!found_here && cur_ptr->is_exotic) {
3966 descriptor_entry_t *desc = lookup_descriptor(cur_obj, key, klen);
3967 if (desc) {
3968 found_here = true;
3969 desc_has_getter = desc->has_getter;
3970 desc_has_setter = desc->has_setter;
3971 desc_writable = desc->writable;
3972 desc_setter = desc->setter;
3973 }
3974 }
3975
3976 if (found_here) {
3977 found_desc = true;
3978 desc_on_receiver = on_receiver;
3979 break;
3980 }
3981
3982 ant_value_t proto = get_proto(js, cur_obj);
3983 if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break;
3984 cur = proto;
3985 on_receiver = false;
3986 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
3987 }
3988
3989 if (!found_desc) goto no_descriptor;
3990 if (!desc_on_receiver && !desc_has_setter && !desc_has_getter && desc_writable) goto no_descriptor;
3991
3992 if (desc_has_setter) {
3993 ant_value_t setter = desc_setter;
3994 uint8_t setter_type = vtype(setter);
3995 if (setter_type == T_FUNC || setter_type == T_CFUNC) {
3996 js_error_site_t saved_errsite = js->errsite;
3997 ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, setter, obj, &v, 1, NULL, false);
3998 js->errsite = saved_errsite;
3999 if (is_err(result)) return result;
4000 return v;
4001 }
4002 }
4003
4004 if (desc_has_getter && !desc_has_setter) {
4005 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter");
4006 return v;
4007 }
4008
4009 if (!desc_writable) {
4010 if (sv_is_strict_context(js)) return js_mkerr(js, "assignment to read-only property");
4011 return v;
4012 }
4013
4014 if (existing <= 0) goto no_descriptor;
4015 }
4016
4017no_descriptor:
4018 if (existing <= 0) goto create_new;
4019 if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant");
4020
4021 js_saveval(js, existing, v);
4022 array_define_or_set_index(js, obj, key, (size_t)klen);
4023 return v;
4024
4025create_new:
4026 {
4027 ant_value_t extensibility_error = check_object_extensibility(js, obj);
4028 if (is_err(extensibility_error)) return extensibility_error;
4029 if (extensibility_error == js_false) return v;
4030 }
4031
4032 const char *interned_key = intern_string(key, (size_t)klen);
4033 if (!interned_key) return js_mkerr(js, "oom");
4034 ant_value_t result = mkprop_interned_exact(js, obj, interned_key, v, 0);
4035 if (is_err(result)) return result;
4036 array_define_or_set_index(js, obj, key, (size_t)klen);
4037
4038 return v;
4039}
4040
4041ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) {
4042 obj = js_as_obj(obj);
4043 const char *interned = intern_string(key, len);
4044 if (!interned) return js_mkerr(js, "oom");
4045 return mkprop_interned(js, obj, interned, v, 0);
4046}
4047
4048ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v) {
4049 obj = js_as_obj(obj);
4050 if (is_proxy(obj)) {
4051 ant_value_t result = proxy_set(js, obj, key, klen, v);
4052 if (is_err(result)) return result;
4053 return v;
4054 }
4055
4056 if (try_dynamic_setter(js, obj, key, klen, v)) return v;
4057 ant_offset_t existing = lkp(js, obj, key, klen);
4058
4059 {
4060 bool has_desc = false;
4061 bool desc_writable = true;
4062 bool desc_has_setter = false;
4063 ant_value_t desc_setter = js_mkundef();
4064 ant_value_t as_obj = js_as_obj(obj);
4065 ant_object_t *ptr = js_obj_ptr(as_obj);
4066 const char *interned_key = intern_string(key, klen);
4067
4068 if (ptr && ptr->shape && interned_key) {
4069 int32_t slot = ant_shape_lookup_interned(ptr->shape, interned_key);
4070 if (slot >= 0) {
4071 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot);
4072 if (prop) {
4073 has_desc = true;
4074 desc_writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0;
4075 desc_has_setter = prop->has_setter != 0;
4076 desc_setter = prop->setter;
4077 }
4078 }
4079 }
4080
4081 if (!has_desc && ptr && ptr->is_exotic) {
4082 descriptor_entry_t *desc = lookup_descriptor(as_obj, key, klen);
4083 if (desc) {
4084 has_desc = true;
4085 desc_writable = desc->writable;
4086 desc_has_setter = desc->has_setter;
4087 desc_setter = desc->setter;
4088 }
4089 }
4090
4091 if (has_desc) {
4092 if (!desc_writable && !desc_has_setter) {
4093 if (sv_is_strict_context(js)) return js_mkerr(js, "assignment to read-only property");
4094 return v;
4095 }
4096 if (desc_has_setter) {
4097 ant_value_t setter = desc_setter;
4098 uint8_t setter_type = vtype(setter);
4099 if (setter_type == T_FUNC || setter_type == T_CFUNC) {
4100 ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, setter, obj, &v, 1, NULL, false);
4101 if (is_err(result)) return result;
4102 return v;
4103 }
4104 }
4105 }
4106 }
4107
4108 if (existing > 0) {
4109 if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant");
4110 js_saveval(js, existing, v);
4111 array_define_or_set_index(js, obj, key, klen);
4112 return v;
4113 }
4114
4115 {
4116 ant_value_t extensibility_error = check_object_extensibility(js, obj);
4117 if (is_err(extensibility_error)) return extensibility_error;
4118 if (extensibility_error == js_false) return v;
4119 }
4120
4121 const char *interned_key = intern_string(key, klen);
4122 if (!interned_key) return js_mkerr(js, "oom");
4123 ant_value_t created = mkprop_interned_exact(js, obj, interned_key, v, 0);
4124
4125 if (!is_err(created)) array_define_or_set_index(js, obj, key, klen);
4126 return is_err(created) ? created : v;
4127}
4128
4129ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) {
4130 ant_value_t k = js_mkstr(js, key, len);
4131 if (is_err(k)) return k;
4132 return js_setprop(js, obj, k, v);
4133}
4134
4135ant_value_t js_setprop_nonconfigurable(ant_t *js, ant_value_t obj, const char *key, size_t keylen, ant_value_t v) {
4136 ant_value_t k = js_mkstr(js, key, keylen);
4137 if (is_err(k)) return k;
4138 ant_value_t result = js_setprop(js, obj, k, v);
4139 if (is_err(result)) return result;
4140
4141 js_set_descriptor(js, js_as_obj(obj), key, keylen, JS_DESC_W);
4142 return result;
4143}
4144
4145static ant_value_t js_setprop_readonly_nonconfigurable(ant_t *js, ant_value_t obj, const char *key, size_t keylen, ant_value_t v) {
4146 ant_value_t k = js_mkstr(js, key, keylen);
4147 if (is_err(k)) return k;
4148 ant_value_t result = js_setprop(js, obj, k, v);
4149 if (is_err(result)) return result;
4150
4151 js_set_descriptor(js, js_as_obj(obj), key, keylen, 0);
4152 return result;
4153}
4154
4155enum {
4156 SYM_FLAG_GLOBAL = 1u,
4157 SYM_FLAG_WELL_KNOWN = 2u,
4158 SYM_FLAG_HAS_DESC = 4u,
4159};
4160
4161typedef struct sym_registry_entry {
4162 const char *key;
4163 ant_value_t sym;
4164 UT_hash_handle hh;
4165} sym_registry_entry_t;
4166
4167ant_value_t js_mksym(ant_t *js, const char *desc) {
4168 uint32_t id = (uint32_t)(++js->sym.counter);
4169 bool has_desc = desc != NULL;
4170
4171 size_t desc_len = has_desc ? strlen(desc) : 0;
4172 size_t total = sizeof(ant_symbol_heap_t) + (has_desc ? desc_len + 1 : 0);
4173
4174 ant_symbol_heap_t *sym_ptr = (ant_symbol_heap_t *)js_type_alloc(
4175 js, ANT_ALLOC_SYMBOL, total,
4176 _Alignof(ant_symbol_heap_t)
4177 ); if (!sym_ptr) return js_mkerr(js, "oom");
4178
4179 sym_ptr->id = id;
4180 sym_ptr->flags = has_desc ? SYM_FLAG_HAS_DESC : 0;
4181 sym_ptr->key = NULL;
4182 sym_ptr->desc_len = (uint32_t)desc_len;
4183
4184 if (has_desc) {
4185 memcpy(sym_ptr->desc, desc, desc_len);
4186 sym_ptr->desc[desc_len] = '\0';
4187 }
4188
4189 return mkval(T_SYMBOL, (uintptr_t)sym_ptr);
4190}
4191
4192ant_value_t js_mksym_well_known(ant_t *js, const char *desc) {
4193 ant_value_t sym = js_mksym(js, desc);
4194 if (is_err(sym)) return sym;
4195 ant_symbol_heap_t *ptr = (ant_symbol_heap_t *)(uintptr_t)vdata(sym);
4196 if (ptr) ptr->flags |= SYM_FLAG_WELL_KNOWN;
4197 return sym;
4198}
4199
4200static inline ant_symbol_heap_t *sym_ptr(ant_value_t v) {
4201 return (ant_symbol_heap_t *)(uintptr_t)vdata(v);
4202}
4203
4204static inline uint32_t sym_get_id(ant_value_t v) {
4205 ant_symbol_heap_t *ptr = sym_ptr(v);
4206 return ptr ? ptr->id : 0;
4207}
4208
4209static inline uint32_t sym_get_flags(ant_value_t v) {
4210 ant_symbol_heap_t *ptr = sym_ptr(v);
4211 return ptr ? ptr->flags : 0;
4212}
4213
4214static inline uintptr_t sym_get_key_ptr(ant_value_t v) {
4215 ant_symbol_heap_t *ptr = sym_ptr(v);
4216 return ptr ? (uintptr_t)ptr->key : 0;
4217}
4218
4219const inline char *js_sym_desc(ant_value_t sym) {
4220 ant_symbol_heap_t *ptr = sym_ptr(sym);
4221 if (!ptr || !(ptr->flags & SYM_FLAG_HAS_DESC)) return NULL;
4222 return ptr->desc;
4223}
4224
4225ant_value_t js_mksym_for(ant_t *js, const char *key) {
4226 const char *interned = intern_string(key, strlen(key));
4227
4228 sym_registry_entry_t *reg = js->sym.registry;
4229 sym_registry_entry_t *found = NULL;
4230 HASH_FIND_PTR(reg, &interned, found);
4231 if (found) return found->sym;
4232
4233 ant_value_t sym = js_mksym(js, key);
4234 if (is_err(sym)) return sym;
4235
4236 ant_symbol_heap_t *ptr = sym_ptr(sym);
4237 if (!ptr) return js_mkerr(js, "oom");
4238 ptr->flags |= SYM_FLAG_GLOBAL;
4239 ptr->key = interned;
4240
4241 sym_registry_entry_t *entry = ant_calloc(sizeof(sym_registry_entry_t));
4242 if (entry) {
4243 entry->key = interned;
4244 entry->sym = sym;
4245 HASH_ADD_PTR(reg, key, entry);
4246 js->sym.registry = reg;
4247 }
4248
4249 return sym;
4250}
4251
4252const char *js_sym_key(ant_value_t sym) {
4253 if (vtype(sym) != T_SYMBOL) return NULL;
4254 uint32_t flags = sym_get_flags(sym);
4255 if (!(flags & SYM_FLAG_GLOBAL) || (flags & SYM_FLAG_WELL_KNOWN)) return NULL;
4256 return (const char *)sym_get_key_ptr(sym);
4257}
4258
4259static inline bool streq(const char *buf, size_t len, const char *s, size_t n) {
4260 return len == n && !memcmp(buf, s, n);
4261}
4262
4263ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len) {
4264 obj = js_as_obj(obj);
4265 ant_object_t *ptr = js_obj_ptr(obj);
4266 if (!search_intern || !ptr || !ptr->shape) return 0;
4267
4268 int32_t shape_slot = ant_shape_lookup_interned(ptr->shape, search_intern);
4269 (void)len;
4270 if (shape_slot < 0) return 0;
4271 return propref_make(js, ptr, (uint32_t)shape_slot);
4272}
4273
4274inline ant_offset_t lkp(ant_t *js, ant_value_t obj, const char *buf, size_t len) {
4275 const char *search_intern = intern_string(buf, len);
4276 if (!search_intern) return 0;
4277 return lkp_interned(js, obj, search_intern, len);
4278}
4279
4280inline ant_value_t lkp_interned_val(ant_t *js, ant_value_t obj, const char *search_intern) {
4281 obj = js_as_obj(obj);
4282 ant_object_t *ptr = js_obj_ptr(obj);
4283 if (!search_intern || !ptr || !ptr->shape) return js_mkundef();
4284 int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern);
4285 if (slot < 0) return js_mkundef();
4286 return ant_object_prop_get_unchecked(ptr, (uint32_t)slot);
4287}
4288
4289static inline ant_value_t lkp_val(ant_t *js, ant_value_t obj, const char *buf, size_t len) {
4290 const char *interned = intern_string(buf, len);
4291 if (!interned) return js_mkundef();
4292 return lkp_interned_val(js, obj, interned);
4293}
4294
4295ant_offset_t lkp_sym(ant_t *js, ant_value_t obj, ant_offset_t sym_off) {
4296 obj = js_as_obj(obj);
4297 ant_object_t *ptr = js_obj_ptr(obj);
4298 if (!ptr || !ptr->shape) return 0;
4299 int32_t slot = ant_shape_lookup_symbol(ptr->shape, sym_off);
4300 if (slot < 0) return 0;
4301 return propref_make(js, ptr, (uint32_t)slot);
4302}
4303
4304ant_offset_t lkp_sym_proto(ant_t *js, ant_value_t obj, ant_offset_t sym_off) {
4305 ant_value_t cur = obj;
4306 proto_overflow_guard_t guard;
4307 proto_overflow_guard_init(&guard);
4308 while (is_object_type(cur)) {
4309 obj = cur;
4310 ant_offset_t off = lkp_sym(js, obj, sym_off);
4311 if (off != 0) return off;
4312 ant_value_t proto = get_proto(js, js_as_obj(cur));
4313 if (!is_object_type(proto)) break;
4314 cur = proto;
4315 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
4316 }
4317 return 0;
4318}
4319
4320static inline ant_value_t lkp_sym_proto_val(ant_t *js, ant_value_t obj, ant_offset_t sym_off) {
4321 ant_value_t cur = obj;
4322 proto_overflow_guard_t guard;
4323 proto_overflow_guard_init(&guard);
4324
4325 while (is_object_type(cur)) {
4326 ant_value_t as_obj = js_as_obj(cur);
4327 ant_object_t *ptr = js_obj_ptr(as_obj);
4328
4329 if (ptr && ptr->shape) {
4330 int32_t slot = ant_shape_lookup_symbol(ptr->shape, sym_off);
4331 if (slot >= 0) return ant_object_prop_get_unchecked(ptr, (uint32_t)slot);
4332 }
4333
4334 ant_value_t proto = get_proto(js, as_obj);
4335 if (!is_object_type(proto)) break;
4336
4337 cur = proto;
4338 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
4339 }
4340
4341 return js_mkundef();
4342}
4343
4344static uintptr_t lkp_with_getter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *getter_out, bool *has_getter_out) {
4345 *has_getter_out = false;
4346 *getter_out = js_mkundef();
4347 const char *search_intern = intern_string(buf, len);
4348
4349 ant_value_t current = obj;
4350 proto_overflow_guard_t guard;
4351 proto_overflow_guard_init(&guard);
4352
4353 while (is_object_type(current)) {
4354 current = js_as_obj(current);
4355 uintptr_t current_id = (uintptr_t)vdata(current);
4356
4357 ant_object_t *ptr = js_obj_ptr(current);
4358 if (ptr && ptr->shape && search_intern) {
4359 int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern);
4360 if (slot >= 0) {
4361 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot);
4362 if (prop && prop->has_getter) {
4363 *getter_out = prop->getter;
4364 *has_getter_out = true;
4365 return current_id;
4366 }
4367 return propref_make(js, ptr, (uint32_t)slot);
4368 }
4369 }
4370
4371 if (ptr && ptr->is_exotic) {
4372 descriptor_entry_t *desc = lookup_descriptor(current, buf, len);
4373 if (desc && desc->has_getter) {
4374 *getter_out = desc->getter;
4375 *has_getter_out = true;
4376 return current_id;
4377 }
4378 }
4379
4380 ant_value_t proto = get_proto(js, current);
4381 if (!is_object_type(proto)) break;
4382 current = proto;
4383 if (proto_overflow_guard_hit_cycle(js, &guard, current)) break;
4384 }
4385
4386 return 0;
4387}
4388
4389static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out) {
4390 *has_setter_out = false;
4391 *setter_out = js_mkundef();
4392 const char *search_intern = intern_string(buf, len);
4393
4394 ant_value_t current = obj;
4395 proto_overflow_guard_t guard;
4396 proto_overflow_guard_init(&guard);
4397 while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) {
4398 current = js_as_obj(current);
4399 uintptr_t current_id = (uintptr_t)vdata(current);
4400
4401 ant_object_t *ptr = js_obj_ptr(current);
4402 if (ptr && ptr->shape && search_intern) {
4403 int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern);
4404 if (slot >= 0) {
4405 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot);
4406 if (prop && prop->has_setter) {
4407 *setter_out = prop->setter;
4408 *has_setter_out = true;
4409 return current_id;
4410 }
4411 return propref_make(js, ptr, (uint32_t)slot);
4412 }
4413 }
4414
4415 if (ptr && ptr->is_exotic) {
4416 descriptor_entry_t *desc = lookup_descriptor(current, buf, len);
4417 if (desc && desc->has_setter) {
4418 *setter_out = desc->setter;
4419 *has_setter_out = true;
4420 return current_id;
4421 }
4422 }
4423
4424 ant_value_t proto = get_proto(js, current);
4425 if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break;
4426 current = proto;
4427 if (proto_overflow_guard_hit_cycle(js, &guard, current)) break;
4428 }
4429
4430 return 0;
4431}
4432
4433ant_value_t js_get_proto(ant_t *js, ant_value_t obj) {
4434 uint8_t t = vtype(obj);
4435
4436 if (!is_object_type(obj)) return js_mknull();
4437 ant_value_t as_obj = js_as_obj(obj);
4438
4439 ant_object_t *ptr = js_obj_ptr(as_obj);
4440 ant_value_t proto = ptr ? ptr->proto : js_mkundef();
4441 if (is_object_type(proto) || vtype(proto) == T_NULL) return proto;
4442
4443 if (t != T_OBJ) return get_prototype_for_type(js, t);
4444 return js_mknull();
4445}
4446
4447static ant_value_t get_proto(ant_t *js, ant_value_t obj) {
4448 return js_get_proto(js, obj);
4449}
4450
4451void js_set_proto(ant_value_t obj, ant_value_t proto) {
4452 if (!is_object_type(obj)) return;
4453
4454 ant_value_t as_obj = js_as_obj(obj);
4455 ant_object_t *ptr = js_obj_ptr(as_obj);
4456 if (!ptr) return;
4457
4458 ptr->proto = proto;
4459 ant_ic_epoch_bump();
4460}
4461
4462void js_set_proto_init(ant_value_t obj, ant_value_t proto) {
4463 if (!is_object_type(obj)) return;
4464 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4465 if (!ptr) return;
4466 ptr->proto = proto;
4467}
4468
4469static void set_proto(ant_t *js, ant_value_t obj, ant_value_t proto) {
4470 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4471 js_set_proto(obj, proto);
4472 if (ptr) gc_write_barrier(js, ptr, proto);
4473}
4474
4475void js_set_proto_wb(ant_t *js, ant_value_t obj, ant_value_t proto) {
4476 set_proto(js, obj, proto);
4477}
4478
4479ant_value_t js_get_ctor_proto(ant_t *js, const char *name, size_t len) {
4480 const char *interned = intern_string(name, len);
4481 ant_value_t ctor = lkp_interned_val(js, js->global, interned);
4482 if (vtype(ctor) != T_FUNC) return js_mknull();
4483 ant_value_t ctor_obj = js_as_obj(ctor);
4484 ant_value_t proto = lkp_interned_val(js, ctor_obj, js->intern.prototype);
4485 return vtype(proto) == T_UNDEF ? js_mknull() : proto;
4486}
4487
4488static inline ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len) {
4489 return js_get_ctor_proto(js, name, len);
4490}
4491
4492static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type) {
4493switch (type) {
4494 case T_OBJ: return js->sym.object_proto;
4495 case T_ARR: return js->sym.array_proto;
4496 case T_STR: return get_ctor_proto(js, "String", 6);
4497 case T_NUM: return get_ctor_proto(js, "Number", 6);
4498 case T_BOOL: return get_ctor_proto(js, "Boolean", 7);
4499 case T_FUNC: return get_ctor_proto(js, "Function", 8);
4500 case T_PROMISE: return get_ctor_proto(js, "Promise", 7);
4501 case T_GENERATOR: return js->sym.generator_proto;
4502 case T_BIGINT: return get_ctor_proto(js, "BigInt", 6);
4503 case T_SYMBOL: return get_ctor_proto(js, "Symbol", 6);
4504 default: return js_mknull();
4505}}
4506
4507ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *key, size_t len) {
4508 uint8_t t = vtype(obj);
4509 const char *key_intern = intern_string(key, len);
4510 if (!key_intern) return 0;
4511
4512 ant_value_t cur = obj;
4513 proto_walk_overflow_guard_t guard;
4514 proto_walk_overflow_guard_init(&guard);
4515
4516 while (true) {
4517 if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE || t == T_GENERATOR) {
4518 ant_value_t as_obj = js_as_obj(cur);
4519 ant_offset_t off = lkp_interned(js, as_obj, key_intern, len);
4520 if (off != 0) return off;
4521 } else if (t == T_CFUNC) {
4522 ant_value_t func_proto = get_ctor_proto(js, "Function", 8);
4523 uint8_t ft = vtype(func_proto);
4524 if (ft == T_OBJ || ft == T_ARR || ft == T_FUNC) {
4525 ant_offset_t off = lkp_interned(js, js_as_obj(func_proto), key_intern, len);
4526 if (off != 0) return off;
4527 }
4528 break;
4529 } else if (t != T_STR && t != T_NUM && t != T_BOOL && t != T_BIGINT && t != T_SYMBOL) break;
4530
4531 if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_LOOKUP)) break;
4532 if (proto_walk_overflow_guard_hit_cycle(js, &guard, cur, t, PROTO_WALK_F_LOOKUP)) break;
4533 }
4534
4535 return 0;
4536}
4537
4538static ant_value_t js_string_from_utf16_code_unit(ant_t *js, uint32_t code_unit) {
4539 char buf[4];
4540 size_t out_len = 0;
4541
4542 if (code_unit >= 0xD800 && code_unit <= 0xDFFF) {
4543 buf[0] = (char)(0xE0 | (code_unit >> 12));
4544 buf[1] = (char)(0x80 | ((code_unit >> 6) & 0x3F));
4545 buf[2] = (char)(0x80 | (code_unit & 0x3F));
4546 out_len = 3;
4547 } else out_len = (size_t)utf8_encode(code_unit, buf);
4548
4549 return js_mkstr(js, buf, out_len);
4550}
4551
4552static bool js_try_get_string_index(
4553 ant_t *js, ant_value_t str,
4554 const char *key, size_t key_len, ant_value_t *out
4555) {
4556 if (!is_array_index(key, (ant_offset_t)key_len)) return false;
4557
4558 unsigned long idx = 0;
4559 ant_offset_t byte_len = 0;
4560 ant_offset_t str_off = vstr(js, str, &byte_len);
4561 const char *str_data = (const char *)(uintptr_t)(str_off);
4562 ant_offset_t str_len = (ant_offset_t)utf16_strlen(str_data, byte_len);
4563 if (!parse_array_index(key, key_len, str_len, &idx)) return false;
4564
4565 uint32_t code_unit = utf16_code_unit_at(str_data, byte_len, (ant_offset_t)idx);
4566 if (code_unit == 0xFFFFFFFF) return false;
4567
4568 *out = js_string_from_utf16_code_unit(js, code_unit);
4569 return true;
4570}
4571
4572static ant_value_t getprop_any(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
4573 uint8_t t = vtype(obj);
4574
4575 if (t == T_STR && is_length_key(key, key_len)) {
4576 ant_offset_t byte_len;
4577 ant_offset_t str_off = vstr(js, obj, &byte_len);
4578 return tov(D(utf16_strlen((const char *)(uintptr_t)(str_off), byte_len)));
4579 }
4580
4581 if (t == T_STR) {
4582 ant_value_t indexed = js_mkundef();
4583 if (js_try_get_string_index(js, obj, key, key_len, &indexed)) return indexed;
4584 }
4585
4586 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) {
4587 ant_offset_t off = lkp_proto(js, obj, key, key_len);
4588 if (off != 0) return propref_load(js, off);
4589 return js_mkundef();
4590 }
4591
4592 if (t == T_OBJ || t == T_ARR || t == T_FUNC) {
4593 ant_value_t as_obj = js_as_obj(obj);
4594 ant_offset_t off = lkp(js, as_obj, key, key_len);
4595 if (off != 0) return propref_load(js, off);
4596 off = lkp_proto(js, obj, key, key_len);
4597 if (off != 0) return propref_load(js, off);
4598 }
4599
4600 return js_mkundef();
4601}
4602
4603static ant_value_t try_dynamic_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
4604 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4605 if (!ptr || !ptr->is_exotic) return js_mkundef();
4606 if (!ptr->exotic_ops || !ptr->exotic_ops->getter) return js_mkundef();
4607 return ptr->exotic_ops->getter(js, obj, key, key_len);
4608}
4609
4610static bool try_dynamic_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value) {
4611 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4612 if (!ptr || !ptr->is_exotic) return false;
4613 if (!ptr->exotic_ops || !ptr->exotic_ops->setter) return false;
4614 return ptr->exotic_ops->setter(js, obj, key, key_len, value);
4615}
4616
4617static bool try_dynamic_deleter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
4618 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4619 if (!ptr || !ptr->is_exotic) return false;
4620 if (!ptr->exotic_ops || !ptr->exotic_ops->deleter) return false;
4621 return ptr->exotic_ops->deleter(js, obj, key, key_len);
4622}
4623
4624static bool try_accessor_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t *out) {
4625 ant_value_t getter = js_mkundef();
4626 bool has_getter = false;
4627 lkp_with_getter(js, obj, key, key_len, &getter, &has_getter);
4628
4629 ant_value_t result = call_proto_accessor(js, obj, getter, has_getter, NULL, 0, false);
4630 if (vtype(result) != T_UNDEF) {
4631 *out = result;
4632 return true;
4633 }
4634 return false;
4635}
4636
4637static bool try_accessor_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t val, ant_value_t *out) {
4638 ant_value_t setter = js_mkundef();
4639 bool has_setter = false;
4640
4641 lkp_with_setter(js, obj, key, key_len, &setter, &has_setter);
4642 if (!has_setter) return false;
4643
4644 ant_value_t result = call_proto_accessor(js, obj, setter, has_setter, &val, 1, true);
4645 if (is_err(result)) {
4646 *out = result;
4647 return true;
4648 }
4649
4650 *out = val;
4651 return true;
4652}
4653
4654ant_value_t js_propref_load(ant_t *js, ant_offset_t handle) {
4655 return propref_load(js, handle);
4656}
4657
4658typedef struct {
4659 char *buffer;
4660 size_t capacity;
4661 size_t size;
4662 bool is_dynamic;
4663} string_builder_t;
4664
4665static void string_builder_init(string_builder_t *sb, char *static_buf, size_t static_cap) {
4666 sb->buffer = static_buf;
4667 sb->capacity = static_cap;
4668 sb->size = 0;
4669 sb->is_dynamic = false;
4670}
4671
4672static bool string_builder_append(string_builder_t *sb, const char *data, size_t len) {
4673 if (sb->size + len > sb->capacity) {
4674 size_t new_capacity = sb->capacity ? sb->capacity * 2 : 256;
4675 while (new_capacity < sb->size + len) new_capacity *= 2;
4676
4677 char *new_buffer = (char *)ant_calloc(new_capacity);
4678 if (!new_buffer) return false;
4679
4680 if (sb->size > 0) memcpy(new_buffer, sb->buffer, sb->size);
4681 if (sb->is_dynamic) free(sb->buffer);
4682
4683 sb->buffer = new_buffer;
4684 sb->capacity = new_capacity;
4685 sb->is_dynamic = true;
4686 }
4687
4688 if (len > 0) {
4689 memcpy(sb->buffer + sb->size, data, len);
4690 sb->size += len;
4691 }
4692
4693 return true;
4694}
4695
4696static ant_value_t string_builder_finalize(ant_t *js, string_builder_t *sb) {
4697 ant_value_t result = js_mkstr(js, sb->buffer, sb->size);
4698 if (sb->is_dynamic && sb->buffer) free(sb->buffer);
4699 return result;
4700}
4701
4702ant_offset_t str_len_fast(ant_t *js, ant_value_t str) {
4703 if (vtype(str) != T_STR) return 0;
4704 if (str_is_heap_rope(str)) return rope_len(str);
4705 if (str_is_heap_builder(str)) return builder_len(str);
4706 return assert_flat_string_len(str, NULL);
4707}
4708
4709ant_value_t do_string_op(ant_t *js, uint8_t op, ant_value_t l, ant_value_t r) {
4710 if (op == TOK_PLUS) {
4711 if (str_is_heap_builder(l)) {
4712 l = builder_flatten(js, l);
4713 if (is_err(l)) return l;
4714 }
4715
4716 if (str_is_heap_builder(r)) {
4717 r = builder_flatten(js, r);
4718 if (is_err(r)) return r;
4719 }
4720
4721 ant_offset_t n1 = str_len_fast(js, l);
4722 ant_offset_t n2 = str_len_fast(js, r);
4723 ant_offset_t total_len = n1 + n2;
4724
4725 if (n2 == 0) return l;
4726 if (n1 == 0) return r;
4727
4728 uint16_t left_depth = (vtype(l) == T_STR && str_is_heap_rope(l)) ? rope_depth(l) : 0;
4729 uint16_t right_depth = (vtype(r) == T_STR && str_is_heap_rope(r)) ? rope_depth(r) : 0;
4730 unsigned int new_depth = (unsigned int)(left_depth > right_depth ? left_depth : right_depth) + 1u;
4731
4732 if (new_depth >= ROPE_MAX_DEPTH || total_len >= ROPE_FLATTEN_THRESHOLD) {
4733 ant_value_t flat_l = l, flat_r = r;
4734 if (str_is_heap_rope(l)) flat_l = rope_flatten(js, l);
4735 if (is_err(flat_l)) return flat_l;
4736 if (str_is_heap_rope(r)) flat_r = rope_flatten(js, r);
4737 if (is_err(flat_r)) return flat_r;
4738
4739 ant_offset_t off1, off2, len1, len2;
4740 off1 = vstr(js, flat_l, &len1);
4741 off2 = vstr(js, flat_r, &len2);
4742
4743 string_builder_t sb;
4744 char static_buffer[512];
4745 string_builder_init(&sb, static_buffer, sizeof(static_buffer));
4746
4747 if (
4748 !string_builder_append(&sb, (char *)(uintptr_t)(off1), len1) ||
4749 !string_builder_append(&sb, (char *)(uintptr_t)(off2), len2)
4750 ) return js_mkerr(js, "string concatenation failed");
4751
4752 return string_builder_finalize(js, &sb);
4753 }
4754
4755 return js_mkrope(js, l, r, total_len, (uint16_t)new_depth);
4756 }
4757
4758 ant_offset_t n1, off1 = vstr(js, l, &n1);
4759 ant_offset_t n2, off2 = vstr(js, r, &n2);
4760
4761 if (op == TOK_EQ) {
4762 bool eq = n1 == n2 &&
4763 memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0;
4764 return mkval(T_BOOL, eq ? 1 : 0);
4765 } else if (op == TOK_NE) {
4766 bool eq = n1 == n2 &&
4767 memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0;
4768 return mkval(T_BOOL, eq ? 0 : 1);
4769 } else if (op == TOK_LT || op == TOK_LE || op == TOK_GT || op == TOK_GE) {
4770 ant_offset_t min_len = n1 < n2 ? n1 : n2;
4771 int cmp = memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, min_len);
4772
4773 if (cmp == 0) {
4774 if (n1 == n2) {
4775 return mkval(T_BOOL, (op == TOK_LE || op == TOK_GE) ? 1 : 0);
4776 } else cmp = (n1 < n2) ? -1 : 1;
4777 }
4778
4779 switch (op) {
4780 case TOK_LT: return mkval(T_BOOL, cmp < 0 ? 1 : 0);
4781 case TOK_LE: return mkval(T_BOOL, cmp <= 0 ? 1 : 0);
4782 case TOK_GT: return mkval(T_BOOL, cmp > 0 ? 1 : 0);
4783 case TOK_GE: return mkval(T_BOOL, cmp >= 0 ? 1 : 0);
4784 default: return js_mkerr(js, "bad str op");
4785 }
4786 } else return js_mkerr(js, "bad str op");
4787}
4788
4789
4790typedef enum { ITER_CONTINUE, ITER_BREAK, ITER_ERROR } iter_action_t;
4791typedef iter_action_t (*iter_callback_t)(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out);
4792
4793static bool js_try_call_method(ant_t *js, ant_value_t obj, const char *method, ant_value_t *args, int nargs, ant_value_t *out_result) {
4794 ant_value_t fn = js_getprop_fallback(js, obj, method);
4795 if (is_err(fn)) {
4796 *out_result = fn;
4797 return true;
4798 }
4799
4800 uint8_t ft = vtype(fn);
4801 if (ft != T_FUNC && ft != T_CFUNC) return false;
4802
4803 ant_value_t saved_this = js->this_val;
4804 js->this_val = obj;
4805
4806 ant_value_t result;
4807 if (ft == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs);
4808 else result = sv_vm_call(js->vm, js, fn, obj, args, nargs, NULL, false);
4809
4810 bool had_throw = js->thrown_exists;
4811 ant_value_t thrown = js->thrown_value;
4812
4813 js->this_val = saved_this;
4814 if (had_throw) {
4815 js->thrown_exists = true;
4816 js->thrown_value = thrown;
4817 }
4818
4819 *out_result = result;
4820 return true;
4821}
4822
4823static ant_value_t js_call_method(ant_t *js, ant_value_t obj, const char *method, ant_value_t *args, int nargs) {
4824 ant_value_t result;
4825 if (!js_try_call_method(js, obj, method, args, nargs, &result)) return js_mkundef();
4826 return result;
4827}
4828
4829static ant_value_t js_call_toString(ant_t *js, ant_value_t value) {
4830 ant_value_t result = js_call_method(js, value, "toString", NULL, 0);
4831
4832 if (is_err(result)) return result;
4833 if (vtype(result) == T_STR) return result;
4834
4835 uint8_t rtype = vtype(result);
4836 if (rtype == T_UNDEF) {
4837 goto fallback;
4838 }
4839
4840 if (rtype != T_OBJ && rtype != T_ARR && rtype != T_FUNC) {
4841 char buf[256];
4842 size_t len = tostr(js, result, buf, sizeof(buf));
4843 return js_mkstr(js, buf, len);
4844 }
4845
4846fallback:;
4847 char buf[4096];
4848 size_t len = tostr(js, value, buf, sizeof(buf));
4849 return js_mkstr(js, buf, len);
4850}
4851
4852static ant_value_t js_call_valueOf(ant_t *js, ant_value_t value) {
4853 ant_value_t result = js_call_method(js, value, "valueOf", NULL, 0);
4854 if (vtype(result) == T_UNDEF) return value;
4855 return result;
4856}
4857
4858static inline bool is_primitive(ant_value_t v) {
4859 uint8_t t = vtype(v);
4860 return t == T_STR || t == T_NUM || t == T_BOOL || t == T_NULL || t == T_UNDEF || t == T_SYMBOL || t == T_BIGINT;
4861}
4862
4863static ant_value_t try_exotic_to_primitive(ant_t *js, ant_value_t value, int hint) {
4864 ant_value_t tp_sym = get_toPrimitive_sym();
4865 if (vtype(tp_sym) != T_SYMBOL) return mkval(T_UNDEF, 0);
4866 ant_value_t tp_fn = js_get_sym(js, value, tp_sym);
4867 uint8_t ft = vtype(tp_fn);
4868
4869 if (ft == T_UNDEF) return mkval(T_UNDEF, 0);
4870 if (ft != T_FUNC && ft != T_CFUNC) {
4871 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.toPrimitive is not a function");
4872 }
4873
4874 const char *hint_str = hint == 1 ? "string" : (hint == 2 ? "number" : "default");
4875 ant_value_t hint_arg = js_mkstr(js, hint_str, strlen(hint_str));
4876 ant_value_t result = sv_vm_call(js->vm, js, tp_fn, value, &hint_arg, 1, NULL, false);
4877
4878 if (is_err(result) || is_primitive(result)) return result;
4879 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert object to primitive value");
4880}
4881
4882static ant_value_t try_ordinary_to_primitive(ant_t *js, ant_value_t value, int hint) {
4883 static const char *names[] = {"valueOf", "toString"};
4884
4885 int first = (hint == 1);
4886 ant_value_t result;
4887
4888 for (int i = 0; i < 2; i++) {
4889 int idx = first ^ i;
4890 if (js_try_call_method(js, value, names[idx], NULL, 0, &result))
4891 if (is_err(result) || is_primitive(result)) return result;
4892 }
4893
4894 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert object to primitive value");
4895}
4896
4897ant_value_t js_to_primitive(ant_t *js, ant_value_t value, int hint) {
4898 if (is_primitive(value)) return value;
4899 if (!is_object_type(value)) return value;
4900
4901 ant_value_t result = try_exotic_to_primitive(js, value, hint);
4902 if (vtype(result) != T_UNDEF) return result;
4903
4904 return try_ordinary_to_primitive(js, value, hint);
4905}
4906
4907bool strict_eq_values(ant_t *js, ant_value_t l, ant_value_t r) {
4908 uint8_t t = vtype(l);
4909 if (t != vtype(r)) return false;
4910 if (t == T_STR) {
4911 ant_offset_t n1, n2, off1 = vstr(js, l, &n1), off2 = vstr(js, r, &n2);
4912 return n1 == n2 &&
4913 memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0;
4914 }
4915 if (t == T_NUM) return tod(l) == tod(r);
4916 if (t == T_BIGINT) return bigint_compare(js, l, r) == 0;
4917 return vdata(l) == vdata(r);
4918}
4919
4920ant_value_t coerce_to_str(ant_t *js, ant_value_t v) {
4921 if (vtype(v) == T_STR) return v;
4922
4923 if (is_object_type(v)) {
4924 ant_value_t prim = js_to_primitive(js, v, 1);
4925 if (is_err(prim)) return prim;
4926 if (vtype(prim) == T_STR) return prim;
4927 return js_tostring_val(js, prim);
4928 }
4929
4930 return js_tostring_val(js, v);
4931}
4932
4933ant_value_t coerce_to_str_concat(ant_t *js, ant_value_t v) {
4934 if (vtype(v) == T_STR) return v;
4935
4936 if (is_object_type(v)) {
4937 ant_value_t prim = js_to_primitive(js, v, 0);
4938 if (is_err(prim)) return prim;
4939 if (vtype(prim) == T_STR) return prim;
4940 return js_tostring_val(js, prim);
4941 }
4942
4943 return js_tostring_val(js, v);
4944}
4945
4946static ant_value_t check_frozen_sealed(ant_t *js, ant_value_t obj, const char *action) {
4947 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
4948 if (!ptr) return js_mkundef();
4949
4950 if (ptr->frozen) {
4951 if (sv_is_strict_context(js)) return js_mkerr(js, "cannot %s property of frozen object", action);
4952 return js_false;
4953 }
4954 if (ptr->sealed) {
4955 if (sv_is_strict_context(js)) return js_mkerr(js, "cannot %s property of sealed object", action);
4956 return js_false;
4957 }
4958 return js_mkundef();
4959}
4960
4961ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t len) {
4962 ant_value_t original_obj = obj;
4963 obj = js_as_obj(obj);
4964 ant_object_t *ptr = js_obj_ptr(obj);
4965 if (!ptr) return js_true;
4966 if (is_proxy(obj)) {
4967 ant_value_t result = proxy_delete(js, obj, key, len);
4968 return is_err(result) ? result : js_bool(js_truthy(js, result));
4969 }
4970
4971 ant_value_t err = check_frozen_sealed(js, obj, "delete");
4972 if (vtype(err) != T_UNDEF) return err;
4973
4974 if (array_obj_ptr(original_obj) && is_length_key(key, len)) {
4975 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property");
4976 return js_false;
4977 }
4978
4979 if (array_obj_ptr(original_obj)) {
4980 ant_offset_t doff = get_dense_buf(original_obj);
4981 unsigned long del_idx = 0;
4982 if (doff && parse_array_index(key, len, get_array_length(js, original_obj), &del_idx)) {
4983 array_mark_may_have_holes(original_obj);
4984 ant_offset_t dense_len = dense_iterable_length(js, original_obj);
4985 if ((ant_offset_t)del_idx < dense_len) dense_set(js, doff, (ant_offset_t)del_idx, T_EMPTY);
4986 }
4987 }
4988
4989 const char *interned = intern_string(key, len);
4990 if (!interned) {
4991 try_dynamic_deleter(js, obj, key, len);
4992 return js_true;
4993 }
4994
4995 int32_t shape_slot = ant_shape_lookup_interned(ptr->shape, interned);
4996 if (shape_slot < 0) {
4997 try_dynamic_deleter(js, obj, key, len);
4998 return js_true;
4999 }
5000
5001 uint8_t attrs = ant_shape_get_attrs(ptr->shape, (uint32_t)shape_slot);
5002 if ((attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0) {
5003 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property");
5004 return js_false;
5005 }
5006
5007 if (ptr->is_exotic) {
5008 descriptor_entry_t *desc = lookup_descriptor(obj, key, len);
5009 if (desc && !desc->configurable) {
5010 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property");
5011 return js_false;
5012 }
5013 }
5014
5015 uint32_t slot = (uint32_t)shape_slot;
5016 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
5017 uint32_t swapped_from = slot;
5018 if (!ant_shape_remove_slot(ptr->shape, slot, &swapped_from)) return js_true;
5019
5020 obj_remove_prop_slot(ptr, slot);
5021 propref_adjust_after_swap_delete(js, ptr, slot, swapped_from);
5022 return js_true;
5023}
5024
5025ant_value_t js_delete_sym_prop(ant_t *js, ant_value_t obj, ant_value_t sym) {
5026 obj = js_as_obj(obj);
5027 ant_object_t *ptr = js_obj_ptr(obj);
5028 if (!ptr) return js_true;
5029 if (is_proxy(obj)) {
5030 ant_value_t result = proxy_delete_val(js, obj, sym);
5031 return is_err(result) ? result : js_bool(js_truthy(js, result));
5032 }
5033
5034 ant_value_t err = check_frozen_sealed(js, obj, "delete");
5035 if (vtype(err) != T_UNDEF) return err;
5036
5037 ant_offset_t sym_off = (ant_offset_t)vdata(sym);
5038 int32_t shape_slot = ant_shape_lookup_symbol(ptr->shape, sym_off);
5039 if (shape_slot < 0) return js_true;
5040
5041 uint8_t attrs = ant_shape_get_attrs(ptr->shape, (uint32_t)shape_slot);
5042 if ((attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0) {
5043 if (sv_is_strict_context(js)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property");
5044 return js_false;
5045 }
5046
5047 uint32_t slot = (uint32_t)shape_slot;
5048 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
5049
5050 uint32_t swapped_from = slot;
5051 if (!ant_shape_remove_slot(ptr->shape, slot, &swapped_from)) return js_true;
5052
5053 obj_remove_prop_slot(ptr, slot);
5054 propref_adjust_after_swap_delete(js, ptr, slot, swapped_from);
5055
5056 return js_true;
5057}
5058
5059static ant_value_t iter_call_noargs_with_this(ant_t *js, ant_value_t this_val, ant_value_t method) {
5060 ant_value_t result = sv_vm_call(js->vm, js, method, this_val, NULL, 0, NULL, false);
5061 return result;
5062}
5063
5064static ant_value_t iter_close_iterator(ant_t *js, ant_value_t iterator) {
5065 ant_offset_t return_off = lkp_proto(js, iterator, "return", 6);
5066 if (return_off == 0) return js_mkundef();
5067 ant_value_t return_method = propref_load(js, return_off);
5068 if (!is_callable(return_method)) {
5069 return js_mkerr(js, "iterator.return is not a function");
5070 }
5071 return iter_call_noargs_with_this(js, iterator, return_method);
5072}
5073
5074static ant_value_t iter_foreach(ant_t *js, ant_value_t iterable, iter_callback_t cb, void *ctx) {
5075 ant_value_t iter_sym = get_iterator_sym();
5076 ant_offset_t iter_prop = (vtype(iter_sym) == T_SYMBOL) ? lkp_sym_proto(js, iterable, (ant_offset_t)vdata(iter_sym)) : 0;
5077 if (iter_prop == 0) return js_mkerr(js, "not iterable");
5078
5079 ant_value_t iter_method = propref_load(js, iter_prop);
5080 ant_value_t iterator = iter_call_noargs_with_this(js, iterable, iter_method);
5081 if (is_err(iterator)) return iterator;
5082
5083 ant_value_t out = js_mkundef();
5084
5085 while (true) {
5086 ant_offset_t next_off = lkp_proto(js, iterator, "next", 4);
5087 if (next_off == 0) { return js_mkerr(js, "iterator.next is not a function"); }
5088
5089 ant_value_t next_method = propref_load(js, next_off);
5090 if (!is_callable(next_method)) {
5091 return js_mkerr(js, "iterator.next is not a function");
5092 }
5093
5094 ant_value_t result = iter_call_noargs_with_this(js, iterator, next_method);
5095 if (is_err(result)) { return result; }
5096
5097 ant_offset_t done_off = lkp(js, result, "done", 4);
5098 ant_value_t done_val = done_off ? propref_load(js, done_off) : js_mkundef();
5099 if (js_truthy(js, done_val)) break;
5100
5101 ant_offset_t value_off = lkp(js, result, "value", 5);
5102 ant_value_t value = value_off ? propref_load(js, value_off) : js_mkundef();
5103
5104 iter_action_t action = cb(js, value, ctx, &out);
5105 if (action == ITER_BREAK) {
5106 ant_value_t close_result = iter_close_iterator(js, iterator);
5107 if (is_err(close_result)) { return close_result; }
5108 break;
5109 }
5110 if (action == ITER_ERROR) {
5111 ant_value_t close_result = iter_close_iterator(js, iterator);
5112 if (is_err(close_result)) return close_result;
5113 return out;
5114 }
5115 }
5116
5117 return out;
5118}
5119
5120ant_value_t js_symbol_to_string(ant_t *js, ant_value_t sym) {
5121 const char *desc = js_sym_desc(sym);
5122 if (!desc) return js_mkstr(js, "Symbol()", 8);
5123
5124 size_t desc_len = strlen(desc);
5125 size_t total = 7 + desc_len + 1;
5126
5127 char stack_buf[128];
5128 char *buf = (total + 1 <= sizeof(stack_buf)) ? stack_buf : malloc(total + 1);
5129 if (!buf) return js_mkerr(js, "out of memory");
5130
5131 memcpy(buf, "Symbol(", 7);
5132 memcpy(buf + 7, desc, desc_len);
5133 buf[7 + desc_len] = ')';
5134 buf[total] = '\0';
5135
5136 ant_value_t result = js_mkstr(js, buf, total);
5137 if (buf != stack_buf) free(buf);
5138 return result;
5139}
5140
5141static ant_value_t builtin_String(ant_t *js, ant_value_t *args, int nargs) {
5142 ant_value_t sval;
5143
5144 if (nargs == 0) sval = js_mkstr(js, "", 0);
5145 else if (vtype(args[0]) == T_STR) sval = args[0];
5146
5147 else if (vtype(args[0]) == T_SYMBOL) {
5148 sval = js_symbol_to_string(js, args[0]);
5149 if (is_err(sval)) return sval;
5150 } else {
5151 sval = coerce_to_str(js, args[0]);
5152 if (is_err(sval)) return sval;
5153 }
5154
5155 ant_value_t string_proto = js_get_ctor_proto(js, "String", 6);
5156 if (is_wrapper_ctor_target(js, js->this_val, string_proto)) {
5157 set_slot(js->this_val, SLOT_PRIMITIVE, sval);
5158
5159 ant_offset_t byte_len;
5160 ant_offset_t str_off = vstr(js, sval, &byte_len);
5161 const char *str_data = (const char *)(uintptr_t)(str_off);
5162
5163 js_setprop(js, js->this_val, js->length_str, tov((double)utf16_strlen(str_data, byte_len)));
5164 js_set_descriptor(js, js_as_obj(js->this_val), "length", 6, 0);
5165 }
5166
5167 return sval;
5168}
5169
5170static ant_value_t builtin_Number_isNaN(ant_t *js, ant_value_t *args, int nargs) {
5171 if (nargs == 0) return mkval(T_BOOL, 0);
5172 ant_value_t arg = args[0];
5173
5174 if (vtype(arg) != T_NUM) return mkval(T_BOOL, 0);
5175
5176 double val = tod(arg);
5177 return mkval(T_BOOL, isnan(val) ? 1 : 0);
5178}
5179
5180static ant_value_t builtin_Number_isFinite(ant_t *js, ant_value_t *args, int nargs) {
5181 if (nargs == 0) return mkval(T_BOOL, 0);
5182 ant_value_t arg = args[0];
5183
5184 if (vtype(arg) != T_NUM) return mkval(T_BOOL, 0);
5185
5186 double val = tod(arg);
5187 return mkval(T_BOOL, isfinite(val) ? 1 : 0);
5188}
5189
5190static ant_value_t builtin_global_isNaN(ant_t *js, ant_value_t *args, int nargs) {
5191 if (nargs == 0) return mkval(T_BOOL, 1);
5192 double val = js_to_number(js, args[0]);
5193 return mkval(T_BOOL, isnan(val) ? 1 : 0);
5194}
5195
5196static ant_value_t builtin_global_isFinite(ant_t *js, ant_value_t *args, int nargs) {
5197 if (nargs == 0) return mkval(T_BOOL, 0);
5198 double val = js_to_number(js, args[0]);
5199 return mkval(T_BOOL, isfinite(val) ? 1 : 0);
5200}
5201
5202static ant_value_t builtin_eval(ant_t *js, ant_value_t *args, int nargs) {
5203 if (nargs == 0) return js_mkundef();
5204 ant_value_t code = args[0];
5205 if (vtype(code) != T_STR) return code;
5206 ant_offset_t code_len = 0;
5207 ant_offset_t code_off = vstr(js, code, &code_len);
5208 const char *code_str = (const char *)(uintptr_t)(code_off);
5209 return js_eval_bytecode_eval_with_strict(js, code_str, (size_t)code_len, false);
5210}
5211
5212static ant_value_t builtin_Number_isInteger(ant_t *js, ant_value_t *args, int nargs) {
5213 if (nargs == 0) return mkval(T_BOOL, 0);
5214 ant_value_t arg = args[0];
5215
5216 if (vtype(arg) != T_NUM) return mkval(T_BOOL, 0);
5217
5218 double val = tod(arg);
5219 if (!isfinite(val)) return mkval(T_BOOL, 0);
5220 return mkval(T_BOOL, (val == floor(val)) ? 1 : 0);
5221}
5222
5223static ant_value_t builtin_Number_isSafeInteger(ant_t *js, ant_value_t *args, int nargs) {
5224 if (nargs == 0) return mkval(T_BOOL, 0);
5225 ant_value_t arg = args[0];
5226
5227 if (vtype(arg) != T_NUM) return mkval(T_BOOL, 0);
5228
5229 double val = tod(arg);
5230 if (!isfinite(val)) return mkval(T_BOOL, 0);
5231 if (val != floor(val)) return mkval(T_BOOL, 0);
5232
5233 return mkval(T_BOOL, (val >= -9007199254740991.0 && val <= 9007199254740991.0) ? 1 : 0);
5234}
5235
5236static ant_value_t builtin_Number(ant_t *js, ant_value_t *args, int nargs) {
5237 ant_value_t nval = tov(nargs > 0 ? js_to_number(js, args[0]) : 0.0);
5238 ant_value_t number_proto = js_get_ctor_proto(js, "Number", 6);
5239 if (is_wrapper_ctor_target(js, js->this_val, number_proto)) {
5240 set_slot(js->this_val, SLOT_PRIMITIVE, nval);
5241 }
5242 return nval;
5243}
5244
5245static ant_value_t builtin_Boolean(ant_t *js, ant_value_t *args, int nargs) {
5246 ant_value_t bval = mkval(T_BOOL, nargs > 0 && js_truthy(js, args[0]) ? 1 : 0);
5247 ant_value_t boolean_proto = js_get_ctor_proto(js, "Boolean", 7);
5248 if (is_wrapper_ctor_target(js, js->this_val, boolean_proto)) {
5249 set_slot(js->this_val, SLOT_PRIMITIVE, bval);
5250 }
5251 return bval;
5252}
5253
5254static ant_value_t builtin_Object(ant_t *js, ant_value_t *args, int nargs) {
5255 if (nargs == 0 || vtype(args[0]) == T_NULL || vtype(args[0]) == T_UNDEF) {
5256 ant_value_t obj_proto = js->sym.object_proto;
5257 if (is_unboxed_obj(js, js->this_val, obj_proto)) return js->this_val;
5258 return js_mkobj(js);
5259 }
5260
5261 ant_value_t arg = args[0];
5262 uint8_t t = vtype(arg);
5263
5264 if (t == T_OBJ || t == T_ARR || t == T_FUNC) return arg;
5265 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) {
5266 ant_value_t wrapper = js_mkobj(js);
5267 if (is_err(wrapper)) return wrapper;
5268 set_slot(wrapper, SLOT_PRIMITIVE, arg);
5269 ant_value_t proto = get_prototype_for_type(js, t);
5270 if (vtype(proto) == T_OBJ) js_set_proto_init(wrapper, proto);
5271 return wrapper;
5272 }
5273
5274 return arg;
5275}
5276
5277static ant_value_t builtin_function_empty(ant_t *, ant_value_t *, int);
5278
5279static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int nargs, bool is_async, bool is_generator) {
5280 if (nargs == 0) {
5281 ant_value_t func_obj = mkobj(js, 0);
5282 if (is_err(func_obj)) return func_obj;
5283
5284 set_func_code_ptr(js, func_obj, "(){}", 4);
5285 if (is_async && is_generator) {
5286 set_slot(func_obj, SLOT_ASYNC, js_true);
5287 ant_value_t async_generator_proto = get_slot(js_glob(js), SLOT_ASYNC_GENERATOR_PROTO);
5288 if (vtype(async_generator_proto) == T_FUNC) js_set_proto_init(func_obj, async_generator_proto);
5289 }
5290
5291 else if (is_async) {
5292 set_slot(func_obj, SLOT_ASYNC, js_true);
5293 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO);
5294 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto);
5295 }
5296
5297 else if (is_generator) {
5298 ant_value_t generator_proto = get_slot(js_glob(js), SLOT_GENERATOR_PROTO);
5299 if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto);
5300 }
5301
5302 else {
5303 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO);
5304 ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto);
5305 if (is_object_type(instance_proto)) js_set_proto_init(func_obj, instance_proto);
5306 }
5307
5308 set_slot(func_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty));
5309 ant_value_t func = js_obj_to_func(func_obj);
5310
5311 if (!is_async || is_generator) {
5312 ant_value_t proto_setup = is_generator
5313 ? setup_func_prototype_property(js, func, false)
5314 : setup_func_prototype(js, func);
5315 if (is_err(proto_setup)) return proto_setup;
5316 }
5317
5318 if (is_generator) {
5319 ant_value_t prototype = js_get(js, func, "prototype");
5320 ant_value_t parent_proto = is_async ? js->sym.async_generator_proto : js->sym.generator_proto;
5321 if (is_object_type(prototype) && is_object_type(parent_proto)) js_set_proto_wb(js, prototype, parent_proto);
5322 }
5323
5324 return func;
5325 }
5326
5327 size_t total_len = 1;
5328
5329 for (int i = 0; i < nargs - 1; i++) {
5330 args[i] = coerce_to_str(js, args[i]);
5331 if (is_err(args[i])) return args[i];
5332 total_len += vstrlen(js, args[i]);
5333 if (i < nargs - 2) total_len += 1;
5334 }
5335
5336 total_len += 2;
5337
5338 ant_value_t body = coerce_to_str(js, args[nargs - 1]);
5339 if (is_err(body)) return body;
5340 total_len += vstrlen(js, body);
5341 total_len += 1;
5342
5343 char *code_buf = (char *)malloc(total_len + 1);
5344 if (!code_buf) return js_mkerr(js, "oom");
5345 size_t pos = 0;
5346
5347 code_buf[pos++] = '(';
5348 for (int i = 0; i < nargs - 1; i++) {
5349 ant_offset_t param_len, param_off = vstr(js, args[i], ¶m_len);
5350 memcpy(code_buf + pos, (const void *)(uintptr_t)param_off, param_len);
5351 pos += param_len;
5352 if (i < nargs - 2) code_buf[pos++] = ',';
5353 }
5354 code_buf[pos++] = ')';
5355 code_buf[pos++] = '{';
5356 ant_offset_t body_len, body_off = vstr(js, body, &body_len);
5357 memcpy(code_buf + pos, (const void *)(uintptr_t)body_off, body_len);
5358 pos += body_len;
5359 code_buf[pos++] = '}';
5360 code_buf[pos] = '\0';
5361
5362 ant_value_t func_obj = mkobj(js, 0);
5363 if (is_err(func_obj)) { free(code_buf); return func_obj; }
5364
5365 sv_func_t *compiled = sv_compile_function(js, code_buf, pos, is_async, is_generator);
5366 if (!compiled) {
5367 free(code_buf);
5368 return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid function body");
5369 }
5370
5371 sv_closure_t *closure = js_closure_alloc(js);
5372 if (!closure) { free(code_buf); return js_mkerr(js, "oom"); }
5373 closure->func = compiled;
5374 closure->bound_this = js_mkundef();
5375 closure->call_flags = 0;
5376 closure->func_obj = func_obj;
5377
5378 size_t params_len = (size_t)(pos - 2) - (size_t)body_len - 2;
5379 const char *async_prefix = is_async ? "async " : "";
5380 const char *generator_marker = is_generator ? "*" : "";
5381 size_t async_len = is_async ? 6 : 0;
5382 size_t generator_len = is_generator ? 1 : 0;
5383 size_t display_len = async_len + 19 + generator_len + params_len + 5 + (size_t)body_len + 2;
5384 char *display = (char *)malloc(display_len + 1);
5385 if (!display) { free(code_buf); return js_mkerr(js, "oom"); }
5386 size_t n = 0;
5387
5388 memcpy(display + n, async_prefix, async_len); n += async_len;
5389 memcpy(display + n, "function", 8); n += 8;
5390 memcpy(display + n, generator_marker, generator_len); n += generator_len;
5391 memcpy(display + n, " anonymous(", 11); n += 11;
5392 memcpy(display + n, code_buf + 1, params_len); n += params_len;
5393 memcpy(display + n, "\n) {\n", 5); n += 5;
5394 memcpy(display + n, code_buf + 1 + params_len + 2, (size_t)body_len); n += (size_t)body_len;
5395 memcpy(display + n, "\n}", 2); n += 2;
5396
5397 display[n] = '\0';
5398 set_func_code(js, func_obj, display, display_len);
5399
5400 free(display);
5401 free(code_buf);
5402
5403 if (is_async && is_generator) {
5404 set_slot(func_obj, SLOT_ASYNC, js_true);
5405 ant_value_t async_generator_proto = get_slot(js_glob(js), SLOT_ASYNC_GENERATOR_PROTO);
5406 if (vtype(async_generator_proto) == T_FUNC) js_set_proto_init(func_obj, async_generator_proto);
5407 }
5408
5409 else if (is_async) {
5410 set_slot(func_obj, SLOT_ASYNC, js_true);
5411 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO);
5412 if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto);
5413 }
5414
5415 else if (is_generator) {
5416 ant_value_t generator_proto = get_slot(js_glob(js), SLOT_GENERATOR_PROTO);
5417 if (vtype(generator_proto) == T_FUNC) js_set_proto_init(func_obj, generator_proto);
5418 }
5419
5420 else {
5421 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO);
5422 ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto);
5423 if (is_object_type(instance_proto)) js_set_proto_init(func_obj, instance_proto);
5424 }
5425
5426 ant_value_t func = mkval(T_FUNC, (uintptr_t)closure);
5427 if (!is_async || is_generator) {
5428 ant_value_t proto_setup = is_generator
5429 ? setup_func_prototype_property(js, func, false)
5430 : setup_func_prototype(js, func);
5431 if (is_err(proto_setup)) return proto_setup;
5432 }
5433
5434 if (is_generator) {
5435 ant_value_t prototype = js_get(js, func, "prototype");
5436 ant_value_t parent_proto = is_async ? js->sym.async_generator_proto : js->sym.generator_proto;
5437 if (is_object_type(prototype) && is_object_type(parent_proto)) js_set_proto_wb(js, prototype, parent_proto);
5438 }
5439
5440 return func;
5441}
5442
5443static ant_value_t builtin_Function(ant_t *js, ant_value_t *args, int nargs) {
5444 return build_dynamic_function(js, args, nargs, false, false);
5445}
5446
5447static ant_value_t builtin_AsyncFunction(ant_t *js, ant_value_t *args, int nargs) {
5448 return build_dynamic_function(js, args, nargs, true, false);
5449}
5450
5451static ant_value_t builtin_AsyncGeneratorFunction(ant_t *js, ant_value_t *args, int nargs) {
5452 if (nargs == 0) {
5453 ant_value_t empty = js_mkstr(js, "", 0);
5454 if (is_err(empty)) return empty;
5455 return build_dynamic_function(js, &empty, 1, true, true);
5456 }
5457 return build_dynamic_function(js, args, nargs, true, true);
5458}
5459
5460static ant_value_t builtin_GeneratorFunction(ant_t *js, ant_value_t *args, int nargs) {
5461 if (nargs == 0) {
5462 ant_value_t empty = js_mkstr(js, "", 0);
5463 if (is_err(empty)) return empty;
5464 return build_dynamic_function(js, &empty, 1, false, true);
5465 }
5466 return build_dynamic_function(js, args, nargs, false, true);
5467}
5468
5469static ant_value_t builtin_function_empty(ant_t *js, ant_value_t *args, int nargs) {
5470 return js_mkundef();
5471}
5472
5473static ant_value_t builtin_function_call(ant_t *js, ant_value_t *args, int nargs) {
5474 ant_value_t func = js->this_val;
5475 if (vtype(func) != T_FUNC && vtype(func) != T_CFUNC) {
5476 return js_mkerr(js, "call requires a function");
5477 }
5478
5479 ant_value_t this_arg = (nargs > 0) ? args[0] : js_mkundef();
5480 ant_value_t *call_args = NULL;
5481
5482 int call_nargs = (nargs > 1) ? nargs - 1 : 0;
5483 if (call_nargs > 0) call_args = &args[1];
5484
5485 return sv_vm_call_explicit_this(js->vm, js, func, this_arg, call_args, call_nargs);
5486}
5487
5488int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args) {
5489 int len = (int) get_array_length(js, arr);
5490 if (len <= 0) return 0;
5491
5492 ant_value_t *args_out = (ant_value_t *)ant_calloc(sizeof(ant_value_t) * len);
5493 if (!args_out) return 0;
5494
5495 for (int i = 0; i < len; i++) {
5496 args_out[i] = arr_get(js, arr, (ant_offset_t)i);
5497 }
5498
5499 *out_args = args_out;
5500 return len;
5501}
5502
5503static ant_value_t builtin_function_toString(ant_t *js, ant_value_t *args, int nargs) {
5504 ant_value_t func = js->this_val;
5505 uint8_t t = vtype(func);
5506
5507 if (t != T_FUNC && t != T_CFUNC) {
5508 return js_mkerr_typed(js, JS_ERR_TYPE, "Function.prototype.toString requires that 'this' be a Function");
5509 }
5510
5511 // TODO: make dry
5512 if (t == T_CFUNC) {
5513 ant_offset_t name_len = 0;
5514 const char *name = get_func_name(js, func, &name_len);
5515 if (name && name_len > 0) {
5516 size_t total = 9 + name_len + 21 + 1;
5517 char *buf = ant_calloc(total);
5518 size_t n = 0;
5519 n += cpy(buf + n, total - n, "function ", 9);
5520 n += cpy(buf + n, total - n, name, name_len);
5521 n += cpy(buf + n, total - n, "() { [native code] }", 20);
5522 ant_value_t result = js_mkstr(js, buf, n);
5523 free(buf);
5524 return result;
5525 }
5526 return ANT_STRING("function() { [native code] }");
5527 }
5528
5529 ant_value_t func_obj = js_func_obj(func);
5530 ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC);
5531
5532 // TODO: make dry
5533 if (vtype(cfunc_slot) == T_CFUNC) {
5534 ant_offset_t name_len = 0;
5535 const char *name = get_func_name(js, func, &name_len);
5536 if (name && name_len > 0) {
5537 size_t total = 9 + name_len + 21 + 1;
5538 char *buf = ant_calloc(total);
5539 size_t n = 0;
5540 n += cpy(buf + n, total - n, "function ", 9);
5541 n += cpy(buf + n, total - n, name, name_len);
5542 n += cpy(buf + n, total - n, "() { [native code] }", 20);
5543 ant_value_t result = js_mkstr(js, buf, n);
5544 free(buf);
5545 return result;
5546 }
5547 return ANT_STRING("function() { [native code] }");
5548 }
5549
5550 ant_value_t code_val = get_slot(func_obj, SLOT_CODE);
5551 ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN);
5552
5553 if (vtype(code_val) == T_NTARG && vtype(len_val) == T_NUM) {
5554 const char *code = (const char *)(uintptr_t)vdata(code_val);
5555 size_t code_len = (size_t)tod(len_val);
5556
5557 if (code && code_len > 0) {
5558 ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC);
5559 sv_closure_t *closure = js_func_closure(func);
5560
5561 bool is_async = (async_slot == js_true);
5562 bool is_arrow = (closure->call_flags & SV_CALL_IS_ARROW) != 0;
5563
5564 if (is_arrow) {
5565 const char *paren_end = memchr(code, ')', code_len);
5566 if (!paren_end) goto fallback_arrow;
5567
5568 size_t params_len = paren_end - code + 1;
5569 const char *body = paren_end + 1;
5570 size_t body_len = code_len - params_len;
5571
5572 size_t len = (is_async ? 6 : 0) + params_len + 4 + body_len + 1;
5573 char *buf = ant_calloc(len);
5574 size_t n = 0;
5575
5576 if (is_async) n += cpy(buf + n, REMAIN(n, len), "async ", 6);
5577 n += cpy(buf + n, REMAIN(n, len), code, params_len);
5578 n += cpy(buf + n, REMAIN(n, len), " => ", 4);
5579 n += cpy(buf + n, REMAIN(n, len), body, body_len);
5580
5581 ant_value_t result = js_mkstr(js, buf, n);
5582 free(buf);
5583 return result;
5584 fallback_arrow:;
5585 }
5586
5587 return js_mkstr(js, code, code_len);
5588 }
5589 }
5590
5591 sv_closure_t *cl = js_func_closure(func);
5592 if (cl->func != NULL) {
5593 sv_func_t *fn = cl->func;
5594 if (fn && fn->source && fn->source_end > fn->source_start) {
5595 int start = fn->source_start;
5596 int end = fn->source_end;
5597 if (start >= 0 && end <= fn->source_len && end > start)
5598 return js_mkstr(js, fn->source + start, (size_t)(end - start));
5599 }
5600 }
5601
5602 char buf[256];
5603 size_t len = strfunc(js, func, buf, sizeof(buf));
5604 return js_mkstr(js, buf, len);
5605}
5606
5607static ant_value_t builtin_function_apply(ant_t *js, ant_value_t *args, int nargs) {
5608 ant_value_t func = js->this_val;
5609 if (vtype(func) != T_FUNC && vtype(func) != T_CFUNC) {
5610 return js_mkerr_typed(js, JS_ERR_TYPE, "Function.prototype.apply requires that 'this' be a Function");
5611 }
5612
5613 ant_value_t this_arg = (nargs > 0) ? args[0] : js_mkundef();
5614 ant_value_t *call_args = NULL;
5615 int call_nargs = 0;
5616
5617 if (nargs > 1) {
5618 ant_value_t arg_array = args[1];
5619 uint8_t t = vtype(arg_array);
5620 if (t == T_ARR || t == T_OBJ) {
5621 call_nargs = extract_array_args(js, arg_array, &call_args);
5622 } else if (t != T_UNDEF && t != T_NULL) {}
5623 }
5624
5625 ant_value_t result = sv_vm_call_explicit_this(js->vm, js, func, this_arg, call_args, call_nargs);
5626 if (call_args) free(call_args);
5627
5628 return result;
5629}
5630
5631static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs) {
5632 ant_value_t func = js->this_val;
5633
5634 if (vtype(func) != T_FUNC && vtype(func) != T_CFUNC) {
5635 return js_mkerr_typed(js, JS_ERR_TYPE, "bind requires a function");
5636 }
5637
5638 ant_value_t this_arg = (nargs > 0) ? args[0] : js_mkundef();
5639 int bound_argc = (nargs > 1) ? nargs - 1 : 0;
5640 ant_value_t *bound_args = (bound_argc > 0) ? &args[1] : NULL;
5641
5642 int orig_length = 0;
5643 ant_value_t target_func_obj;
5644
5645 if (vtype(func) == T_CFUNC) orig_length = 0;
5646 else {
5647 target_func_obj = js_func_obj(func);
5648 ant_value_t len_val = lkp_interned_val(js, target_func_obj, js->intern.length);
5649 if (vtype(len_val) == T_NUM) orig_length = (int) tod(len_val);
5650 }
5651
5652 int bound_length = orig_length - bound_argc;
5653 if (bound_length < 0) bound_length = 0;
5654
5655 if (vtype(func) == T_CFUNC) {
5656 ant_value_t bound_func = mkobj(js, 0);
5657 if (is_err(bound_func)) return bound_func;
5658
5659 set_slot(bound_func, SLOT_CFUNC, func);
5660
5661 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO);
5662 if (vtype(func_proto) == T_FUNC) js_set_proto_init(bound_func, func_proto);
5663
5664 ant_value_t bound = js_obj_to_func_ex(bound_func, bound_argc > 0 ? SV_CALL_HAS_BOUND_ARGS : 0);
5665 sv_closure_t *bc = js_func_closure(bound);
5666 bc->bound_this = this_arg;
5667 if (bound_argc > 0) {
5668 bc->bound_argv = malloc(sizeof(ant_value_t) * (size_t)bound_argc);
5669 memcpy(bc->bound_argv, bound_args, sizeof(ant_value_t) * (size_t)bound_argc);
5670 bc->bound_argc = bound_argc;
5671 }
5672
5673 js_setprop(js, bound_func, js->length_str, tov((double) bound_length));
5674 ant_value_t proto_setup = setup_func_prototype(js, bound);
5675
5676 if (is_err(proto_setup)) return proto_setup;
5677 js_mark_constructor(bound_func, js_is_constructor(func));
5678
5679 return bound;
5680 }
5681
5682 ant_value_t func_obj = js_func_obj(func);
5683 ant_value_t bound_func = mkobj(js, 0);
5684 if (is_err(bound_func)) return bound_func;
5685
5686 ant_value_t code_val = get_slot(func_obj, SLOT_CODE);
5687 if (vtype(code_val) == T_STR || vtype(code_val) == T_NTARG) {
5688 set_slot(bound_func, SLOT_CODE, code_val);
5689 set_slot(bound_func, SLOT_CODE_LEN, get_slot(func_obj, SLOT_CODE_LEN));
5690 }
5691
5692 sv_closure_t *orig = js_func_closure(func);
5693 sv_closure_t *bound_closure = js_closure_alloc(js);
5694 if (!bound_closure) return js_mkerr(js, "oom");
5695
5696 bound_closure->func = orig->func;
5697 bound_closure->call_flags = orig->call_flags;
5698 bound_closure->upvalues = NULL;
5699 bound_closure->bound_this = this_arg;
5700 bound_closure->bound_args = js_mkundef();
5701 bound_closure->super_val = orig->super_val;
5702 bound_closure->func_obj = bound_func;
5703
5704 if (orig->func && orig->func->upvalue_count > 0 && orig->upvalues) {
5705 size_t upvalue_bytes = sizeof(sv_upvalue_t *) * (size_t)orig->func->upvalue_count;
5706 bound_closure->upvalues = malloc(upvalue_bytes);
5707 if (!bound_closure->upvalues) return js_mkerr(js, "oom");
5708 memcpy(bound_closure->upvalues, orig->upvalues, upvalue_bytes);
5709 }
5710
5711 if (bound_argc > 0)
5712 bound_closure->call_flags |= SV_CALL_HAS_BOUND_ARGS;
5713
5714 ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC);
5715 if (vtype(async_slot) == T_BOOL && vdata(async_slot) == 1) {
5716 set_slot(bound_func, SLOT_ASYNC, js_true);
5717 }
5718
5719 ant_value_t target_proto = get_proto(js, func);
5720 if (is_object_type(target_proto) || vtype(target_proto) == T_NULL) {
5721 js_set_proto_init(bound_func, target_proto);
5722 } else if (vtype(async_slot) == T_BOOL && vdata(async_slot) == 1) {
5723 ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO);
5724 if (vtype(async_proto) == T_FUNC) js_set_proto_init(bound_func, async_proto);
5725 } else {
5726 ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO);
5727 if (vtype(func_proto) == T_FUNC) js_set_proto_init(bound_func, func_proto);
5728 }
5729
5730 ant_value_t data_slot = get_slot(func_obj, SLOT_DATA);
5731 if (vtype(data_slot) != T_UNDEF) {
5732 set_slot(bound_func, SLOT_DATA, data_slot);
5733 }
5734
5735 set_slot(bound_func, SLOT_TARGET_FUNC, func);
5736
5737 if (bound_argc > 0) {
5738 ant_value_t bound_arr = mkarr(js);
5739 for (int i = 0; i < bound_argc; i++) arr_set(js, bound_arr, (ant_offset_t)i, bound_args[i]);
5740 bound_closure->bound_args = bound_arr;
5741 bound_closure->bound_argv = malloc(sizeof(ant_value_t) * (size_t)bound_argc);
5742 memcpy(bound_closure->bound_argv, bound_args, sizeof(ant_value_t) * (size_t)bound_argc);
5743 bound_closure->bound_argc = bound_argc;
5744 }
5745
5746 ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC);
5747 if (vtype(cfunc_slot) == T_CFUNC) {
5748 set_slot(bound_func, SLOT_CFUNC, cfunc_slot);
5749 }
5750
5751 js_setprop(js, bound_func, js->length_str, tov((double) bound_length));
5752
5753 ant_value_t bound = mkval(T_FUNC, (uintptr_t)bound_closure);
5754 ant_value_t proto_setup = setup_func_prototype(js, bound);
5755
5756 if (is_err(proto_setup)) return proto_setup;
5757 js_mark_constructor(bound_func, js_is_constructor(func));
5758
5759 return bound;
5760}
5761
5762static ant_value_t builtin_Array(ant_t *js, ant_value_t *args, int nargs) {
5763 ant_value_t arr = mkarr(js);
5764 if (is_err(arr)) return arr;
5765
5766 if (nargs == 1 && vtype(args[0]) == T_NUM) {
5767 ant_value_t err = validate_array_length(js, args[0]);
5768 if (is_err(err)) return err;
5769 ant_offset_t new_len = (ant_offset_t)tod(args[0]);
5770 ant_offset_t doff = get_dense_buf(arr);
5771 if (doff && new_len <= 1024)
5772 if (new_len > dense_capacity(doff)) doff = dense_grow(js, arr, new_len);
5773 if (new_len > 0) array_mark_may_have_holes(arr);
5774 array_len_set(js, arr, new_len);
5775 } else if (nargs > 0) for (int i = 0; i < nargs; i++)
5776 arr_set(js, arr, (ant_offset_t)i, args[i]);
5777
5778 ant_value_t array_proto = get_ctor_proto(js, "Array", 5);
5779 ant_value_t instance_proto = js_instance_proto_from_new_target(js, array_proto);
5780
5781 if (is_object_type(instance_proto)) js_set_proto_init(arr, instance_proto);
5782 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) {
5783 set_slot(arr, SLOT_CTOR, js->new_target);
5784 }
5785
5786 return arr;
5787}
5788
5789static ant_value_t builtin_error_captureStackTrace(ant_t *js, ant_value_t *args, int nargs) {
5790 if (nargs < 1 || !is_object_type(args[0])) {
5791 return js_mkerr(js, "argument must be an object");
5792 }
5793
5794 ant_value_t target = args[0];
5795 ant_value_t error_ctor = lkp_val(js, js->global, "Error", 5);
5796 ant_value_t prep = js_mkundef();
5797
5798 if (vtype(error_ctor) == T_FUNC || vtype(error_ctor) == T_CFUNC) {
5799 prep = lkp_val(js, js_func_obj(error_ctor), "prepareStackTrace", 17);
5800 }
5801
5802 if (vtype(prep) == T_FUNC || vtype(prep) == T_CFUNC) {
5803 ant_value_t callsites = js_build_callsite_array(js);
5804 ant_value_t prep_args[2] = { target, callsites };
5805 ant_value_t result = sv_vm_call(js->vm, js, prep, js_mkundef(), prep_args, 2, NULL, false);
5806 if (js->thrown_exists) return js_mkundef();
5807 js_set(js, target, "stack", result);
5808 js_set_descriptor(js, js_as_obj(target), "stack", 5, JS_DESC_W | JS_DESC_C);
5809 } else js_capture_stack(js, target);
5810
5811 return js_mkundef();
5812}
5813
5814static ant_value_t builtin_error_isError(ant_t *js, ant_value_t *args, int nargs) {
5815 if (nargs < 1) return js_false;
5816 ant_value_t val = args[0];
5817 if (!is_object_type(val)) return js_false;
5818 return get_slot(val, SLOT_ERROR_BRAND) == js_true ? js_true : js_false;
5819}
5820
5821static ant_value_t builtin_Error(ant_t *js, ant_value_t *args, int nargs) {
5822 bool is_new = (vtype(js->new_target) != T_UNDEF);
5823 ant_value_t this_val = js->this_val;
5824
5825 ant_value_t target = is_new ? js->new_target : js->current_func;
5826 ant_value_t name = ANT_STRING("Error");
5827
5828 if (vtype(target) == T_FUNC) {
5829 ant_value_t n = lkp_val(js, js_func_obj(target), "name", 4);
5830 if (vtype(n) != T_UNDEF) name = n;
5831 }
5832
5833 if (!is_new) {
5834 this_val = js_mkobj(js);
5835 ant_value_t proto = lkp_interned_val(js, js_func_obj(js->current_func), js->intern.prototype);
5836 if (vtype(proto) != T_UNDEF) js_set_proto_init(this_val, proto);
5837 else js_set_proto_init(this_val, get_ctor_proto(js, "Error", 5));
5838 }
5839
5840 if (nargs > 0) {
5841 ant_value_t msg = args[0];
5842 if (vtype(msg) != T_STR) {
5843 const char *str = js_str(js, msg);
5844 msg = js_mkstr(js, str, strlen(str));
5845 }
5846 js_mkprop_fast(js, this_val, "message", 7, msg);
5847 }
5848
5849 if (nargs > 1 && vtype(args[1]) == T_OBJ) {
5850 ant_offset_t cause_off = lkp(js, args[1], "cause", 5);
5851 if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, propref_load(js, cause_off));
5852 }
5853
5854 js_mkprop_fast(js, this_val, "name", 4, name);
5855 set_slot(this_val, SLOT_ERROR_BRAND, js_true);
5856 js_capture_stack(js, this_val);
5857
5858 return this_val;
5859}
5860
5861static ant_value_t builtin_Error_toString(ant_t *js, ant_value_t *args, int nargs) {
5862 ant_value_t this_val = js_getthis(js);
5863
5864 ant_value_t name = js_get(js, this_val, "name");
5865 if (vtype(name) == T_UNDEF) name = js_mkstr(js, "Error", 5);
5866 else if (vtype(name) != T_STR) {
5867 const char *s = js_str(js, name);
5868 name = js_mkstr(js, s, strlen(s));
5869 }
5870
5871 ant_value_t msg = js_get(js, this_val, "message");
5872 if (vtype(msg) == T_UNDEF) msg = js_mkstr(js, "", 0);
5873 else if (vtype(msg) != T_STR) {
5874 const char *s = js_str(js, msg);
5875 msg = js_mkstr(js, s, strlen(s));
5876 }
5877
5878 ant_offset_t name_len, msg_len;
5879 ant_offset_t name_off = vstr(js, name, &name_len);
5880 ant_offset_t msg_off = vstr(js, msg, &msg_len);
5881
5882 const char *name_str = (const char *)(uintptr_t)(name_off);
5883 const char *msg_str = (const char *)(uintptr_t)(msg_off);
5884
5885 if (name_len == 0) return msg;
5886 if (msg_len == 0) return name;
5887
5888 size_t total = (size_t)(name_len + 2 + msg_len);
5889 char *buf = malloc(total + 1);
5890 if (!buf) return js_mkerr(js, "out of memory");
5891
5892 memcpy(buf, name_str, (size_t)name_len);
5893 buf[name_len] = ':';
5894 buf[name_len + 1] = ' ';
5895 memcpy(buf + name_len + 2, msg_str, (size_t)msg_len);
5896 buf[total] = '\0';
5897
5898 ant_value_t result = js_mkstr(js, buf, total);
5899 free(buf);
5900 return result;
5901}
5902
5903static ant_value_t builtin_AggregateError(ant_t *js, ant_value_t *args, int nargs) {
5904 bool is_new = (vtype(js->new_target) != T_UNDEF);
5905 ant_value_t this_val = js->this_val;
5906
5907 if (!is_new) {
5908 this_val = js_mkobj(js);
5909 ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), js->intern.prototype, 9);
5910 if (proto_off) js_set_proto_init(this_val, propref_load(js, proto_off));
5911 else js_set_proto_init(this_val, get_ctor_proto(js, "AggregateError", 14));
5912 }
5913
5914 ant_value_t errors = nargs > 0 ? args[0] : mkarr(js);
5915 if (vtype(errors) != T_ARR) errors = mkarr(js);
5916 js_mkprop_fast(js, this_val, "errors", 6, errors);
5917
5918 if (nargs > 1 && vtype(args[1]) != T_UNDEF) {
5919 ant_value_t msg = args[1];
5920 if (vtype(msg) != T_STR) {
5921 const char *str = js_str(js, msg);
5922 msg = js_mkstr(js, str, strlen(str));
5923 }
5924 js_mkprop_fast(js, this_val, "message", 7, msg);
5925 }
5926
5927 if (nargs > 2 && vtype(args[2]) == T_OBJ) {
5928 ant_offset_t cause_off = lkp(js, args[2], "cause", 5);
5929 if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, propref_load(js, cause_off));
5930 }
5931
5932 js_mkprop_fast(js, this_val, "name", 4, ANT_STRING("AggregateError"));
5933 set_slot(this_val, SLOT_ERROR_BRAND, js_true);
5934
5935 return this_val;
5936}
5937
5938static ant_value_t builtin_SuppressedError(ant_t *js, ant_value_t *args, int nargs) {
5939 bool is_new = (vtype(js->new_target) != T_UNDEF);
5940 ant_value_t this_val = js->this_val;
5941
5942 if (!is_new) {
5943 this_val = js_mkobj(js);
5944 ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), js->intern.prototype, 9);
5945 if (proto_off) js_set_proto_init(this_val, propref_load(js, proto_off));
5946 else js_set_proto_init(this_val, get_ctor_proto(js, "SuppressedError", 15));
5947 }
5948
5949 ant_value_t error = nargs > 0 ? args[0] : js_mkundef();
5950 ant_value_t suppressed = nargs > 1 ? args[1] : js_mkundef();
5951
5952 js_mkprop_fast(js, this_val, "error", 5, error);
5953 js_mkprop_fast(js, this_val, "suppressed", 10, suppressed);
5954
5955 if (nargs > 2 && vtype(args[2]) != T_UNDEF) {
5956 ant_value_t msg = args[2];
5957 if (vtype(msg) != T_STR) {
5958 const char *str = js_str(js, msg);
5959 msg = js_mkstr(js, str, strlen(str));
5960 }
5961 js_mkprop_fast(js, this_val, "message", 7, msg);
5962 }
5963
5964 js_mkprop_fast(js, this_val, "name", 4, ANT_STRING("SuppressedError"));
5965 set_slot(this_val, SLOT_ERROR_BRAND, js_true);
5966 js_capture_stack(js, this_val);
5967
5968 return this_val;
5969}
5970
5971static ant_value_t disposable_stack_init(ant_t *js, ant_value_t obj, int brand, ant_value_t proto) {
5972 if (vtype(obj) != T_OBJ) obj = js_mkobj(js);
5973 if (is_err(obj)) return obj;
5974 if (is_object_type(proto)) js_set_proto_init(obj, proto);
5975
5976 ant_value_t entries = js_mkarr(js);
5977 if (is_err(entries)) return entries;
5978 set_slot(obj, SLOT_BRAND, js_mknum((double)brand));
5979 set_slot(obj, SLOT_ENTRIES, entries);
5980 set_slot(obj, SLOT_SETTLED, js_false);
5981
5982 return obj;
5983}
5984
5985static ant_value_t builtin_DisposableStack(ant_t *js, ant_value_t *args, int nargs) {
5986 if (vtype(js->new_target) == T_UNDEF) {
5987 return js_mkerr_typed(js, JS_ERR_TYPE, "DisposableStack constructor requires 'new'");
5988 }
5989
5990 ant_value_t proto = js_get_ctor_proto(js, "DisposableStack", 15);
5991 ant_value_t instance_proto = js_instance_proto_from_new_target(js, proto);
5992
5993 return disposable_stack_init(js, js->this_val, BRAND_DISPOSABLE_STACK, instance_proto);
5994}
5995
5996static ant_value_t builtin_AsyncDisposableStack(ant_t *js, ant_value_t *args, int nargs) {
5997 if (vtype(js->new_target) == T_UNDEF) {
5998 return js_mkerr_typed(js, JS_ERR_TYPE, "AsyncDisposableStack constructor requires 'new'");
5999 }
6000
6001 ant_value_t proto = js_get_ctor_proto(js, "AsyncDisposableStack", 20);
6002 ant_value_t instance_proto = js_instance_proto_from_new_target(js, proto);
6003
6004 return disposable_stack_init(js, js->this_val, BRAND_ASYNC_DISPOSABLE_STACK, instance_proto);
6005}
6006
6007static bool disposable_stack_has_brand(ant_value_t obj, int brand) {
6008 if (!is_object_type(obj)) return false;
6009 ant_value_t actual = get_slot(js_as_obj(obj), SLOT_BRAND);
6010 return vtype(actual) == T_NUM && (int)js_getnum(actual) == brand;
6011}
6012
6013static ant_value_t disposable_stack_entries_checked(ant_t *js, ant_value_t stack, int brand, const char *name) {
6014 if (!disposable_stack_has_brand(stack, brand)) {
6015 return js_mkerr_typed(js, JS_ERR_TYPE, "%s method called on incompatible receiver", name);
6016 }
6017
6018 if (get_slot(js_as_obj(stack), SLOT_SETTLED) == js_true) {
6019 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is already disposed", name);
6020 }
6021
6022 ant_value_t entries = get_slot(js_as_obj(stack), SLOT_ENTRIES);
6023 if (vtype(entries) != T_ARR) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid %s", name);
6024 return entries;
6025}
6026
6027static ant_value_t disposable_stack_push_record(
6028 ant_t *js, ant_value_t stack, int brand, const char *name,
6029 sv_disposal_record_kind_t kind, ant_value_t value, ant_value_t method
6030) {
6031 if (!is_callable(method)) {
6032 return js_mkerr_typed(js, JS_ERR_TYPE, "%s requires a callable disposer", name);
6033 }
6034
6035 GC_ROOT_SAVE(root_mark, js);
6036 GC_ROOT_PIN(js, stack);
6037 GC_ROOT_PIN(js, value);
6038 GC_ROOT_PIN(js, method);
6039
6040 ant_value_t entries = disposable_stack_entries_checked(js, stack, brand, name);
6041 GC_ROOT_PIN(js, entries);
6042 if (is_err(entries)) {
6043 GC_ROOT_RESTORE(js, root_mark);
6044 return entries;
6045 }
6046
6047 ant_value_t record = js_mkarr(js);
6048 GC_ROOT_PIN(js, record);
6049 if (is_err(record)) {
6050 GC_ROOT_RESTORE(js, root_mark);
6051 return record;
6052 }
6053
6054 js_arr_push(js, record, js_mknum((double)kind));
6055 js_arr_push(js, record, value);
6056 js_arr_push(js, record, method);
6057 js_arr_push(js, entries, record);
6058
6059 GC_ROOT_RESTORE(js, root_mark);
6060 return js_mkundef();
6061}
6062
6063static ant_value_t builtin_DisposableStack_defer(ant_t *js, ant_value_t *args, int nargs) {
6064 ant_value_t fn = nargs > 0 ? args[0] : js_mkundef();
6065 ant_value_t result = disposable_stack_push_record(
6066 js, js->this_val, BRAND_DISPOSABLE_STACK,
6067 "DisposableStack", SV_DISPOSAL_RECORD_DEFER, js_mkundef(), fn
6068 );
6069 return is_err(result) ? result : js_mkundef();
6070}
6071
6072static ant_value_t builtin_DisposableStack_adopt(ant_t *js, ant_value_t *args, int nargs) {
6073 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
6074 ant_value_t fn = nargs > 1 ? args[1] : js_mkundef();
6075 ant_value_t result = disposable_stack_push_record(
6076 js, js->this_val, BRAND_DISPOSABLE_STACK,
6077 "DisposableStack", SV_DISPOSAL_RECORD_ADOPT, value, fn
6078 );
6079 return is_err(result) ? result : value;
6080}
6081
6082static ant_value_t builtin_AsyncDisposableStack_defer(ant_t *js, ant_value_t *args, int nargs) {
6083 ant_value_t fn = nargs > 0 ? args[0] : js_mkundef();
6084 ant_value_t result = disposable_stack_push_record(
6085 js, js->this_val, BRAND_ASYNC_DISPOSABLE_STACK,
6086 "AsyncDisposableStack", SV_DISPOSAL_RECORD_DEFER, js_mkundef(), fn
6087 );
6088 return is_err(result) ? result : js_mkundef();
6089}
6090
6091static ant_value_t builtin_AsyncDisposableStack_adopt(ant_t *js, ant_value_t *args, int nargs) {
6092 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
6093 ant_value_t fn = nargs > 1 ? args[1] : js_mkundef();
6094 ant_value_t result = disposable_stack_push_record(
6095 js, js->this_val, BRAND_ASYNC_DISPOSABLE_STACK,
6096 "AsyncDisposableStack", SV_DISPOSAL_RECORD_ADOPT, value, fn
6097 );
6098 return is_err(result) ? result : value;
6099}
6100
6101static ant_value_t disposable_stack_use(
6102 ant_t *js, ant_value_t stack, int brand, const char *name,
6103 ant_value_t resource, ant_value_t dispose_sym, ant_value_t fallback_sym
6104) {
6105 ant_value_t entries = disposable_stack_entries_checked(js, stack, brand, name);
6106 if (is_err(entries)) return entries;
6107
6108 if (vtype(resource) == T_NULL || vtype(resource) == T_UNDEF) return resource;
6109
6110 ant_value_t method = js_get_sym(js, resource, dispose_sym);
6111 if ((vtype(method) == T_UNDEF || vtype(method) == T_NULL) && vtype(fallback_sym) == T_SYMBOL) {
6112 method = js_get_sym(js, resource, fallback_sym);
6113 }
6114 if (!is_callable(method)) {
6115 return js_mkerr_typed(js, JS_ERR_TYPE, "%s resource is not disposable", name);
6116 }
6117
6118 ant_value_t result = disposable_stack_push_record(
6119 js, stack, brand, name, SV_DISPOSAL_RECORD_USE, resource, method
6120 );
6121 return is_err(result) ? result : resource;
6122}
6123
6124static ant_value_t builtin_DisposableStack_use(ant_t *js, ant_value_t *args, int nargs) {
6125 ant_value_t resource = nargs > 0 ? args[0] : js_mkundef();
6126 return disposable_stack_use(
6127 js, js->this_val, BRAND_DISPOSABLE_STACK, "DisposableStack", resource, get_dispose_sym(), js_mkundef()
6128 );
6129}
6130
6131static ant_value_t builtin_AsyncDisposableStack_use(ant_t *js, ant_value_t *args, int nargs) {
6132 ant_value_t resource = nargs > 0 ? args[0] : js_mkundef();
6133 return disposable_stack_use(
6134 js, js->this_val, BRAND_ASYNC_DISPOSABLE_STACK, "AsyncDisposableStack",
6135 resource, get_asyncDispose_sym(), get_dispose_sym()
6136 );
6137}
6138
6139static ant_value_t disposable_stack_move(ant_t *js, int brand, const char *name) {
6140 GC_ROOT_SAVE(root_mark, js);
6141 ant_value_t stack = js->this_val;
6142 GC_ROOT_PIN(js, stack);
6143
6144 ant_value_t entries = disposable_stack_entries_checked(js, stack, brand, name);
6145 GC_ROOT_PIN(js, entries);
6146 if (is_err(entries)) {
6147 GC_ROOT_RESTORE(js, root_mark);
6148 return entries;
6149 }
6150
6151 ant_value_t proto = js_get_ctor_proto(js, name, strlen(name));
6152 GC_ROOT_PIN(js, proto);
6153 ant_value_t moved = disposable_stack_init(js, js_mkundef(), brand, proto);
6154 GC_ROOT_PIN(js, moved);
6155 if (is_err(moved)) {
6156 GC_ROOT_RESTORE(js, root_mark);
6157 return moved;
6158 }
6159
6160 set_slot(moved, SLOT_ENTRIES, entries);
6161 set_slot(js_as_obj(stack), SLOT_ENTRIES, js_mkarr(js));
6162 set_slot(js_as_obj(stack), SLOT_SETTLED, js_true);
6163
6164 GC_ROOT_RESTORE(js, root_mark);
6165 return moved;
6166}
6167
6168static ant_value_t builtin_DisposableStack_move(ant_t *js, ant_value_t *args, int nargs) {
6169 (void)args; (void)nargs;
6170 return disposable_stack_move(js, BRAND_DISPOSABLE_STACK, "DisposableStack");
6171}
6172
6173static ant_value_t builtin_AsyncDisposableStack_move(ant_t *js, ant_value_t *args, int nargs) {
6174 (void)args; (void)nargs;
6175 return disposable_stack_move(js, BRAND_ASYNC_DISPOSABLE_STACK, "AsyncDisposableStack");
6176}
6177
6178static ant_value_t builtin_DisposableStack_dispose(ant_t *js, ant_value_t *args, int nargs) {
6179 ant_value_t stack = js->this_val;
6180 if (!disposable_stack_has_brand(stack, BRAND_DISPOSABLE_STACK)) {
6181 return js_mkerr_typed(js, JS_ERR_TYPE, "DisposableStack method called on incompatible receiver");
6182 }
6183 if (get_slot(js_as_obj(stack), SLOT_SETTLED) == js_true) return js_mkundef();
6184
6185 GC_ROOT_SAVE(root_mark, js);
6186 GC_ROOT_PIN(js, stack);
6187 ant_value_t entries = get_slot(js_as_obj(stack), SLOT_ENTRIES);
6188
6189 GC_ROOT_PIN(js, entries);
6190 ant_value_t completion = js_mkundef();
6191
6192 GC_ROOT_PIN(js, completion);
6193 set_slot(js_as_obj(stack), SLOT_SETTLED, js_true);
6194
6195 ant_offset_t len = vtype(entries) == T_ARR ? js_arr_len(js, entries) : 0;
6196 if (len > 0) completion = sv_dispose_records_sync(js, entries, len, &completion, false);
6197
6198 if (is_err(completion)) {
6199 GC_ROOT_RESTORE(js, root_mark);
6200 return completion;
6201 }
6202
6203 set_slot(js_as_obj(stack), SLOT_ENTRIES, js_mkarr(js));
6204 if (vtype(completion) != T_UNDEF) {
6205 ant_value_t thrown = js_throw(js, completion);
6206 GC_ROOT_RESTORE(js, root_mark);
6207 return thrown;
6208 }
6209
6210 GC_ROOT_RESTORE(js, root_mark);
6211 return js_mkundef();
6212}
6213
6214static ant_value_t builtin_AsyncDisposableStack_disposeAsync(ant_t *js, ant_value_t *args, int nargs) {
6215 ant_value_t stack = js->this_val;
6216 ant_value_t result_promise = js_mkpromise(js);
6217 if (is_err(result_promise)) return result_promise;
6218
6219 if (!disposable_stack_has_brand(stack, BRAND_ASYNC_DISPOSABLE_STACK)) {
6220 ant_value_t error = js_make_error_silent(
6221 js, JS_ERR_TYPE, "AsyncDisposableStack method called on incompatible receiver"
6222 );
6223 js_reject_promise(js, result_promise, error);
6224 return result_promise;
6225 }
6226
6227 if (get_slot(js_as_obj(stack), SLOT_SETTLED) == js_true) {
6228 js_resolve_promise(js, result_promise, js_mkundef());
6229 return result_promise;
6230 }
6231
6232 GC_ROOT_SAVE(root_mark, js);
6233 GC_ROOT_PIN(js, stack);
6234 GC_ROOT_PIN(js, result_promise);
6235
6236 ant_value_t entries = get_slot(js_as_obj(stack), SLOT_ENTRIES);
6237 GC_ROOT_PIN(js, entries);
6238 set_slot(js_as_obj(stack), SLOT_SETTLED, js_true);
6239
6240 ant_value_t state = js_mkobj(js);
6241 GC_ROOT_PIN(js, state);
6242 if (is_err(state)) {
6243 GC_ROOT_RESTORE(js, root_mark);
6244 return state;
6245 }
6246
6247 set_slot(state, SLOT_ENTRIES, entries);
6248 set_slot(state, SLOT_DATA, result_promise);
6249 set_slot(state, SLOT_AUX, js_mkundef());
6250 set_slot(state, SLOT_ITER_STATE, js_mknum((double)(vtype(entries) == T_ARR ? js_arr_len(js, entries) : 0)));
6251 set_slot(js_as_obj(stack), SLOT_ENTRIES, js_mkarr(js));
6252
6253 ant_value_t result = sv_async_dispose_continue(js, state, false, js_mkundef());
6254 GC_ROOT_RESTORE(js, root_mark);
6255
6256 return result;
6257}
6258
6259
6260typedef ant_value_t (*dynamic_kv_mapper_fn)(
6261 ant_t *js,
6262 ant_value_t key,
6263 ant_value_t val
6264);
6265
6266static ant_value_t iterate_dynamic_keys(ant_t *js, ant_value_t obj, dynamic_kv_mapper_fn mapper) {
6267 ant_object_t *ptr = js_obj_ptr(obj);
6268 if (!ptr || !ptr->exotic_keys || !ptr->exotic_ops || !ptr->exotic_ops->getter) return mkarr(js);
6269 ant_value_t keys_arr = ptr->exotic_keys(js, obj);
6270 ant_value_t arr = mkarr(js);
6271 ant_offset_t len = get_array_length(js, keys_arr);
6272
6273 for (ant_offset_t i = 0; i < len; i++) {
6274 ant_value_t key_val = arr_get(js, keys_arr, i);
6275 if (vtype(key_val) != T_STR) continue;
6276 ant_offset_t klen; ant_offset_t str_off = vstr(js, key_val, &klen);
6277 const char *key = (const char *)(uintptr_t)(str_off);
6278 ant_value_t val = ptr->exotic_ops->getter(js, obj, key, klen);
6279 js_arr_push(js, arr, mapper ? mapper(js, key_val, val) : val);
6280 }
6281
6282 return mkval(T_ARR, vdata(arr));
6283}
6284
6285static ant_value_t builtin_object_is(ant_t *js, ant_value_t *args, int nargs) {
6286 if (nargs < 2) return js_false;
6287
6288 ant_value_t x = args[0];
6289 ant_value_t y = args[1];
6290
6291 uint8_t tx = vtype(x);
6292 uint8_t ty = vtype(y);
6293 if (tx != ty) return js_false;
6294
6295 if (tx == T_UNDEF || tx == T_NULL) return js_true;
6296
6297 if (tx == T_NUM) {
6298 double dx = tod(x);
6299 double dy = tod(y);
6300 if (isnan(dx) && isnan(dy)) return js_true;
6301 if (dx == 0.0 && dy == 0.0) {
6302 bool x_neg = (1.0 / dx) < 0;
6303 bool y_neg = (1.0 / dy) < 0;
6304 return x_neg == y_neg ? js_true : js_false;
6305 }
6306 return dx == dy ? js_true : js_false;
6307 }
6308
6309 if (tx == T_BOOL) return vdata(x) == vdata(y) ? js_true : js_false;
6310 return strict_eq_values(js, x, y) ? js_true : js_false;
6311}
6312
6313enum obj_enum_mode {
6314 OBJ_ENUM_KEYS,
6315 OBJ_ENUM_VALUES,
6316 OBJ_ENUM_ENTRIES
6317};
6318
6319static ant_value_t map_to_entry(ant_t *js, ant_value_t key, ant_value_t val) {
6320 ant_value_t pair = mkarr(js);
6321 arr_set(js, pair, 0, key);
6322 arr_set(js, pair, 1, val);
6323 return mkval(T_ARR, vdata(pair));
6324}
6325
6326static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mode) {
6327 if (vtype(obj) == T_CFUNC) {
6328 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6329 if (vtype(promoted) != T_FUNC) return mkarr(js);
6330 obj = promoted;
6331 }
6332 bool is_arr = (vtype(obj) == T_ARR);
6333 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj);
6334
6335 ant_object_t *ptr = js_obj_ptr(obj);
6336 if (!ptr || !ptr->shape) return mkarr(js);
6337 if (ptr->is_exotic && ptr->exotic_keys) {
6338 if (mode == OBJ_ENUM_KEYS) return ptr->exotic_keys(js, obj);
6339 if (ptr->exotic_ops && ptr->exotic_ops->getter) {
6340 dynamic_kv_mapper_fn mapper = (mode == OBJ_ENUM_ENTRIES) ? map_to_entry : NULL;
6341 return iterate_dynamic_keys(js, obj, mapper);
6342 }
6343 }
6344
6345 ant_value_t arr = mkarr(js);
6346 ant_offset_t idx = 0;
6347
6348 if (is_arr) {
6349 ant_offset_t doff = get_dense_buf(obj);
6350 ant_offset_t dense_len = doff ? dense_iterable_length(js, obj) : 0;
6351
6352 for (ant_offset_t i = 0; i < dense_len; i++) {
6353 ant_value_t v = dense_get(doff, i);
6354 if (is_empty_slot(v)) continue;
6355 char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i);
6356 ant_value_t key_val = js_mkstr(js, idxstr, idxlen);
6357
6358 if (mode == OBJ_ENUM_KEYS) arr_set(js, arr, idx, key_val);
6359 else if (mode == OBJ_ENUM_VALUES) arr_set(js, arr, idx, v);
6360 else arr_set(js, arr, idx, map_to_entry(js, key_val, v));
6361
6362 idx++;
6363 }
6364 }
6365
6366 uint32_t shape_count = ant_shape_count(ptr->shape);
6367 for (uint32_t i = 0; i < shape_count; i++) {
6368 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
6369 if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue;
6370 if (i >= ptr->prop_count) continue;
6371
6372 const char *key = prop->key.interned;
6373 ant_offset_t klen = (ant_offset_t)strlen(key);
6374 ant_value_t val = ant_object_prop_get_unchecked(ptr, i);
6375
6376 if (is_arr && is_array_index(key, klen)) {
6377 ant_offset_t doff = get_dense_buf(obj);
6378 if (doff) {
6379 unsigned long pidx = 0;
6380 for (ant_offset_t ci = 0; ci < klen; ci++) pidx = pidx * 10 + (key[ci] - '0');
6381 if (pidx < dense_iterable_length(js, obj)) continue;
6382 }}
6383
6384 bool should_include = (ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) != 0;
6385 if (should_include && ptr->is_exotic) {
6386 descriptor_entry_t *desc = lookup_descriptor(js_as_obj(obj), key, (size_t)klen);
6387 if (desc) should_include = desc->enumerable;
6388 }
6389 if (!should_include) continue;
6390
6391 ant_value_t key_val = js_mkstr(js, key, (size_t)klen);
6392 if (mode == OBJ_ENUM_KEYS) arr_set(js, arr, idx, key_val);
6393 else if (mode == OBJ_ENUM_VALUES) arr_set(js, arr, idx, val);
6394 else arr_set(js, arr, idx, map_to_entry(js, key_val, val));
6395
6396 idx++;
6397 }
6398
6399 return mkval(T_ARR, vdata(arr));
6400}
6401
6402// TODO: reduce nesting
6403static ant_value_t proxy_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mode) {
6404 GC_ROOT_SAVE(root_mark, js);
6405 GC_ROOT_PIN(js, obj);
6406
6407 ant_proxy_state_t *data = get_proxy_data(obj);
6408 if (!data) {
6409 GC_ROOT_RESTORE(js, root_mark);
6410 return mkarr(js);
6411 }
6412 if (data->revoked) {
6413 GC_ROOT_RESTORE(js, root_mark);
6414 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot perform 'ownKeys' on a proxy that has been revoked");
6415 }
6416
6417 ant_value_t keys = mkarr(js);
6418 GC_ROOT_PIN(js, keys);
6419
6420 ant_offset_t trap_off = lkp(js, data->handler, "ownKeys", 7);
6421 if (!trap_off) {
6422 keys = object_enum(js, data->target, OBJ_ENUM_KEYS);
6423 } else {
6424 ant_value_t trap = propref_load(js, trap_off);
6425 uint8_t ft = vtype(trap);
6426 if (ft != T_FUNC && ft != T_CFUNC) {
6427 keys = object_enum(js, data->target, OBJ_ENUM_KEYS);
6428 } else {
6429 ant_value_t trap_args[1] = { data->target };
6430 ant_value_t result = sv_vm_call(js->vm, js, trap, data->handler, trap_args, 1, NULL, false);
6431 if (is_err(result)) {
6432 GC_ROOT_RESTORE(js, root_mark);
6433 return result;
6434 }
6435 if (vtype(result) != T_ARR) {
6436 GC_ROOT_RESTORE(js, root_mark);
6437 return js_mkerr_typed(js, JS_ERR_TYPE, "ownKeys trap must return an array");
6438 }
6439
6440 ant_offset_t len = get_array_length(js, result);
6441 for (ant_offset_t i = 0; i < len; i++) {
6442 ant_value_t ki = arr_get(js, result, i);
6443 if (vtype(ki) != T_STR && vtype(ki) != T_SYMBOL) {
6444 GC_ROOT_RESTORE(js, root_mark);
6445 return js_mkerr_typed(js, JS_ERR_TYPE, "ownKeys trap result must contain only strings or symbols");
6446 }
6447 for (ant_offset_t j = 0; j < i; j++) {
6448 ant_value_t kj = arr_get(js, result, j);
6449 if (vtype(ki) != vtype(kj)) continue;
6450 if (vtype(ki) == T_SYMBOL) {
6451 if (vdata(ki) == vdata(kj)) {
6452 GC_ROOT_RESTORE(js, root_mark);
6453 return js_mkerr_typed(js, JS_ERR_TYPE, "ownKeys trap result must not contain duplicate entries");
6454 }
6455 continue;
6456 }
6457
6458 ant_offset_t ki_len;
6459 ant_offset_t ki_off = vstr(js, ki, &ki_len);
6460 ant_offset_t kj_len;
6461 ant_offset_t kj_off = vstr(js, kj, &kj_len);
6462 if (ki_len == kj_len &&
6463 memcmp((const void *)(uintptr_t)ki_off, (const void *)(uintptr_t)kj_off, ki_len) == 0) {
6464 GC_ROOT_RESTORE(js, root_mark);
6465 return js_mkerr_typed(js, JS_ERR_TYPE, "ownKeys trap result must not contain duplicate entries");
6466 }
6467 }
6468 }
6469 keys = result;
6470 }
6471 }
6472
6473 ant_value_t out = mkarr(js);
6474 GC_ROOT_PIN(js, out);
6475
6476 ant_offset_t key_count = get_array_length(js, keys);
6477 for (ant_offset_t i = 0; i < key_count; i++) {
6478 GC_ROOT_SAVE(iter_mark, js);
6479
6480 ant_value_t key = arr_get(js, keys, i);
6481 GC_ROOT_PIN(js, key);
6482 if (vtype(key) != T_STR) {
6483 GC_ROOT_RESTORE(js, iter_mark);
6484 continue;
6485 }
6486
6487 ant_value_t desc = proxy_get_own_property_descriptor(js, obj, key);
6488 GC_ROOT_PIN(js, desc);
6489 if (is_err(desc)) {
6490 GC_ROOT_RESTORE(js, iter_mark);
6491 GC_ROOT_RESTORE(js, root_mark);
6492 return desc;
6493 }
6494 if (vtype(desc) == T_UNDEF) {
6495 GC_ROOT_RESTORE(js, iter_mark);
6496 continue;
6497 }
6498
6499 bool enumerable = false;
6500 ant_offset_t enumerable_off = lkp(js, desc, "enumerable", 10);
6501 if (enumerable_off != 0) {
6502 enumerable = js_truthy(js, propref_load(js, enumerable_off));
6503 }
6504 if (!enumerable) {
6505 GC_ROOT_RESTORE(js, iter_mark);
6506 continue;
6507 }
6508
6509 if (mode == OBJ_ENUM_KEYS) {
6510 js_arr_push(js, out, key);
6511 GC_ROOT_RESTORE(js, iter_mark);
6512 continue;
6513 }
6514
6515 ant_value_t value = proxy_get_val(js, obj, key);
6516 GC_ROOT_PIN(js, value);
6517 if (is_err(value)) {
6518 GC_ROOT_RESTORE(js, iter_mark);
6519 GC_ROOT_RESTORE(js, root_mark);
6520 return value;
6521 }
6522
6523 if (mode == OBJ_ENUM_VALUES) {
6524 js_arr_push(js, out, value);
6525 } else {
6526 ant_value_t entry = map_to_entry(js, key, value);
6527 GC_ROOT_PIN(js, entry);
6528 js_arr_push(js, out, entry);
6529 }
6530
6531 GC_ROOT_RESTORE(js, iter_mark);
6532 }
6533
6534 GC_ROOT_RESTORE(js, root_mark);
6535 return out;
6536}
6537
6538static ant_value_t builtin_object_keys(ant_t *js, ant_value_t *args, int nargs) {
6539 if (nargs == 0) return mkarr(js);
6540 ant_value_t obj = args[0];
6541 if (vtype(obj) == T_CFUNC) {
6542 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6543 if (vtype(promoted) != T_FUNC) return mkarr(js);
6544 obj = promoted;
6545 }
6546 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js);
6547
6548 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_KEYS);
6549
6550 return object_enum(js, obj, OBJ_ENUM_KEYS);
6551}
6552
6553static ant_value_t for_in_keys_add(ant_t *js, ant_value_t out, ant_value_t seen, ant_value_t key) {
6554 GC_ROOT_SAVE(root_mark, js);
6555 GC_ROOT_PIN(js, out);
6556 GC_ROOT_PIN(js, seen);
6557 GC_ROOT_PIN(js, key);
6558
6559 if (vtype(key) != T_STR) return js_mkundef();
6560
6561 ant_offset_t key_len = 0;
6562 ant_offset_t key_off = vstr(js, key, &key_len);
6563 const char *key_ptr = (const char *)(uintptr_t)(key_off);
6564
6565 if (lkp(js, seen, key_ptr, key_len) != 0) goto done;
6566
6567 ant_value_t mark = setprop_cstr(js, seen, key_ptr, key_len, js_true);
6568 if (is_err(mark)) {
6569 GC_ROOT_RESTORE(js, root_mark);
6570 return mark;
6571 }
6572
6573 if (vtype(out) == T_ARR) js_arr_push(js, out, key);
6574done:
6575 GC_ROOT_RESTORE(js, root_mark);
6576 return js_mkundef();
6577}
6578
6579static ant_value_t for_in_keys_add_string_indices(ant_t *js, ant_value_t out, ant_value_t seen, ant_value_t str) {
6580 GC_ROOT_SAVE(root_mark, js);
6581 GC_ROOT_PIN(js, out);
6582 GC_ROOT_PIN(js, seen);
6583 GC_ROOT_PIN(js, str);
6584
6585 ant_offset_t slen = vstrlen(js, str);
6586 for (ant_offset_t i = 0; i < slen; i++) {
6587 char idx[16];
6588 size_t idx_len = uint_to_str(idx, sizeof(idx), (uint64_t)i);
6589
6590 ant_value_t key = js_mkstr(js, idx, idx_len);
6591 GC_ROOT_PIN(js, key);
6592
6593 ant_value_t r = for_in_keys_add(js, out, seen, key);
6594 if (is_err(r)) { GC_ROOT_RESTORE(js, root_mark); return r; }
6595 }
6596
6597 GC_ROOT_RESTORE(js, root_mark);
6598 return js_mkundef();
6599}
6600
6601static inline ant_value_t for_in_keys_collect_chain(
6602 ant_t *js, ant_value_t out, ant_value_t seen, ant_value_t obj
6603) {
6604 ant_value_t cur = obj;
6605 GC_ROOT_PIN(js, cur);
6606
6607 for (int depth = 0; is_object_type(cur) && depth < MAX_PROTO_CHAIN_DEPTH; depth++) {
6608 GC_ROOT_SAVE(iter_mark, js);
6609 ant_value_t as_cur = (vtype(cur) == T_FUNC) ? js_func_obj(cur) : cur;
6610 ant_object_t *cur_ptr = js_obj_ptr(as_cur);
6611 ant_value_t key, r, proto;
6612 ant_offset_t doff, dense_len, klen;
6613 bool is_arr;
6614
6615 if (!cur_ptr) goto next_proto;
6616 if (!cur_ptr->is_exotic || !cur_ptr->exotic_keys) goto shape_props;
6617
6618 {
6619 ant_value_t ekeys = cur_ptr->exotic_keys(js, as_cur);
6620 GC_ROOT_PIN(js, ekeys);
6621 if (vtype(ekeys) != T_ARR) goto next_proto;
6622 ant_offset_t elen = js_arr_len(js, ekeys);
6623 for (ant_offset_t i = 0; i < elen; i++) {
6624 key = js_arr_get(js, ekeys, i);
6625 GC_ROOT_PIN(js, key);
6626 r = for_in_keys_add(js, out, seen, key);
6627 if (is_err(r)) goto err;
6628 }
6629 }
6630 goto next_proto;
6631
6632shape_props:
6633 if (!cur_ptr->shape) goto next_proto;
6634 is_arr = (vtype(as_cur) == T_ARR);
6635 if (!is_arr) goto shape_iter;
6636
6637 doff = get_dense_buf(as_cur);
6638 dense_len = doff ? dense_iterable_length(js, as_cur) : 0;
6639 for (ant_offset_t i = 0; i < dense_len; i++) {
6640 if (is_empty_slot(dense_get(doff, i))) continue;
6641 char idxstr[16];
6642 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i);
6643 key = js_mkstr(js, idxstr, idxlen);
6644 GC_ROOT_PIN(js, key);
6645 r = for_in_keys_add(js, out, seen, key);
6646 if (is_err(r)) goto err;
6647 }
6648
6649shape_iter:
6650 for (uint32_t i = 0; i < ant_shape_count(cur_ptr->shape); i++) {
6651 const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, i);
6652 if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue;
6653 if (i >= cur_ptr->prop_count) continue;
6654
6655 const char *kstr = prop->key.interned;
6656 klen = (ant_offset_t)strlen(kstr);
6657 if (is_arr && is_array_index(kstr, klen)) {
6658 doff = get_dense_buf(as_cur);
6659 if (doff) {
6660 unsigned long pidx = 0;
6661 for (ant_offset_t ci = 0; ci < klen; ci++) pidx = pidx * 10 + (kstr[ci] - '0');
6662 if (pidx < (unsigned long)dense_iterable_length(js, as_cur)) continue;
6663 }}
6664
6665 bool enumerable = (ant_shape_get_attrs(cur_ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) != 0;
6666 if (cur_ptr->is_exotic) {
6667 descriptor_entry_t *desc = lookup_descriptor(js_as_obj(as_cur), kstr, (size_t)klen);
6668 if (desc) enumerable = desc->enumerable;
6669 }
6670
6671 key = js_mkstr(js, kstr, (size_t)klen);
6672 GC_ROOT_PIN(js, key);
6673 r = for_in_keys_add(js, enumerable ? out : js_mkundef(), seen, key);
6674 if (is_err(r)) goto err;
6675 }
6676
6677next_proto:
6678 GC_ROOT_RESTORE(js, iter_mark);
6679 proto = js_get_proto(js, cur);
6680 if (!is_object_type(proto)) break;
6681 cur = proto;
6682 continue;
6683err:
6684 GC_ROOT_RESTORE(js, iter_mark);
6685 return r;
6686 }
6687
6688 return out;
6689}
6690
6691ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj) {
6692 GC_ROOT_SAVE(root_mark, js);
6693 uint8_t t = vtype(obj);
6694 ant_value_t out = mkarr(js);
6695 ant_value_t result = out;
6696
6697 GC_ROOT_PIN(js, obj);
6698 GC_ROOT_PIN(js, out);
6699
6700 if (t == T_NULL || t == T_UNDEF) goto done;
6701
6702 ant_value_t seen = mkobj(js, 0);
6703 GC_ROOT_PIN(js, seen);
6704
6705 if (t == T_STR) {
6706 result = for_in_keys_add_string_indices(js, out, seen, obj);
6707 if (is_err(result)) goto done;
6708 result = out;
6709 goto done;
6710 }
6711
6712 if (t == T_CFUNC) {
6713 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6714 if (vtype(promoted) != T_FUNC) goto done;
6715 obj = promoted;
6716 t = T_FUNC;
6717 }
6718
6719 if (t == T_OBJ) {
6720 ant_value_t prim = get_slot(obj, SLOT_PRIMITIVE);
6721 GC_ROOT_PIN(js, prim);
6722 if (vtype(prim) == T_STR) {
6723 result = for_in_keys_add_string_indices(js, out, seen, prim);
6724 if (is_err(result)) goto done;
6725 result = out;
6726 }
6727 }
6728
6729 if (t != T_OBJ && t != T_ARR && t != T_FUNC) goto done;
6730 result = for_in_keys_collect_chain(js, out, seen, obj);
6731
6732done:
6733 GC_ROOT_RESTORE(js, root_mark);
6734 return result;
6735}
6736
6737static ant_value_t builtin_object_values(ant_t *js, ant_value_t *args, int nargs) {
6738 if (nargs == 0) return mkarr(js);
6739 ant_value_t obj = args[0];
6740 if (vtype(obj) == T_CFUNC) {
6741 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6742 if (vtype(promoted) != T_FUNC) return mkarr(js);
6743 obj = promoted;
6744 }
6745 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js);
6746 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_VALUES);
6747 return object_enum(js, obj, OBJ_ENUM_VALUES);
6748}
6749
6750static ant_value_t builtin_object_entries(ant_t *js, ant_value_t *args, int nargs) {
6751 if (nargs == 0) return mkarr(js);
6752 ant_value_t obj = args[0];
6753 if (vtype(obj) == T_CFUNC) {
6754 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6755 if (vtype(promoted) != T_FUNC) return mkarr(js);
6756 obj = promoted;
6757 }
6758 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js);
6759 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_ENTRIES);
6760 return object_enum(js, obj, OBJ_ENUM_ENTRIES);
6761}
6762
6763static ant_value_t builtin_object_getPrototypeOf(ant_t *js, ant_value_t *args, int nargs) {
6764 if (nargs == 0) return js_mkerr(js, "Object.getPrototypeOf requires an argument");
6765 ant_value_t obj = args[0];
6766 uint8_t t = vtype(obj);
6767
6768 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT)
6769 return get_prototype_for_type(js, t);
6770 if (t == T_CFUNC) {
6771 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
6772 if (vtype(promoted) == T_FUNC) return get_proto(js, promoted);
6773 return get_prototype_for_type(js, t);
6774 }
6775
6776 if (is_object_type(obj)) {
6777 if (is_proxy(obj)) return proxy_get_prototype_of(js, obj);
6778 return get_proto(js, obj);
6779 }
6780
6781 return js_mknull();
6782}
6783
6784static ant_value_t builtin_object_setPrototypeOf(ant_t *js, ant_value_t *args, int nargs) {
6785 if (nargs < 2) return js_mkerr(js, "Object.setPrototypeOf requires 2 arguments");
6786
6787 ant_value_t obj = args[0];
6788 ant_value_t proto = args[1];
6789
6790 uint8_t t = vtype(obj);
6791 if (t == T_CFUNC) {
6792 obj = js_cfunc_promote(js, obj);
6793 t = T_FUNC;
6794 }
6795
6796 if (t != T_OBJ && t != T_ARR && t != T_FUNC) {
6797 return js_mkerr(js, "Object.setPrototypeOf: first argument must be an object");
6798 }
6799
6800 uint8_t pt = vtype(proto);
6801 if (pt == T_CFUNC) {
6802 proto = js_cfunc_promote(js, proto);
6803 pt = T_FUNC;
6804 }
6805
6806 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC && pt != T_NULL) {
6807 return js_mkerr(js, "Object.setPrototypeOf: prototype must be an object or null");
6808 }
6809
6810 if (pt != T_NULL && proto_chain_contains(js, proto, obj))
6811 return js_mkerr(js, "Cyclic __proto__ value");
6812
6813 set_proto(js, obj, proto);
6814 return obj;
6815}
6816
6817static ant_value_t builtin_proto_getter(ant_t *js, ant_value_t *args, int nargs) {
6818 ant_value_t this_val = js->this_val;
6819 uint8_t t = vtype(this_val);
6820
6821 if (t == T_UNDEF || t == T_NULL) {
6822 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot read property '__proto__' of %s", typestr(t));
6823 }
6824
6825 if (t == T_OBJ || t == T_ARR || t == T_FUNC) {
6826 return get_proto(js, this_val);
6827 }
6828
6829 return get_prototype_for_type(js, t);
6830}
6831
6832static ant_value_t builtin_proto_setter(ant_t *js, ant_value_t *args, int nargs) {
6833 ant_value_t this_val = js->this_val;
6834 uint8_t t = vtype(this_val);
6835
6836 if (t == T_UNDEF || t == T_NULL) {
6837 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property '__proto__' of %s", typestr(t));
6838 }
6839
6840 if (t != T_OBJ && t != T_ARR && t != T_FUNC) {
6841 return js_mkundef();
6842 }
6843
6844 if (nargs == 0) return js_mkundef();
6845
6846 ant_value_t proto = args[0];
6847 uint8_t pt = vtype(proto);
6848
6849 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC && pt != T_NULL) {
6850 return js_mkundef();
6851 }
6852
6853 if (pt != T_NULL && proto_chain_contains(js, proto, this_val))
6854 return js_mkundef();
6855
6856 set_proto(js, this_val, proto);
6857 return js_mkundef();
6858}
6859
6860static ant_value_t legacy_accessor_this_obj(ant_t *js, ant_value_t this_val) {
6861 uint8_t t = vtype(this_val);
6862
6863 if (t == T_UNDEF || t == T_NULL) return js_mkerr_typed(
6864 js, JS_ERR_TYPE, "Cannot convert undefined or null to object"
6865 );
6866
6867 if (t == T_CFUNC) return js_cfunc_promote(js, this_val);
6868 if (t == T_OBJ || t == T_ARR || t == T_FUNC) return js_as_obj(this_val);
6869
6870 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) {
6871 ant_value_t boxed = builtin_Object(js, &this_val, 1);
6872 if (is_err(boxed)) return boxed;
6873 if (is_object_type(boxed)) return js_as_obj(boxed);
6874 }
6875
6876 return js_mkerr_typed(js, JS_ERR_TYPE, "Legacy accessor methods require an object receiver");
6877}
6878
6879static bool legacy_accessor_key(
6880 ant_t *js, ant_value_t key,
6881 ant_value_t *key_val_out,
6882 const char **key_str_out,
6883 ant_offset_t *key_len_out,
6884 ant_offset_t *sym_off_out
6885) {
6886 if (key_val_out) *key_val_out = key;
6887 if (key_str_out) *key_str_out = NULL;
6888 if (key_len_out) *key_len_out = 0;
6889 if (sym_off_out) *sym_off_out = 0;
6890
6891 if (vtype(key) == T_SYMBOL) {
6892 if (sym_off_out) *sym_off_out = (ant_offset_t)vdata(key);
6893 return true;
6894 }
6895
6896 ant_value_t key_val = (vtype(key) == T_STR) ? key : coerce_to_str(js, key);
6897 if (is_err(key_val)) return false;
6898 if (key_val_out) *key_val_out = key_val;
6899
6900 ant_offset_t key_len = 0;
6901 ant_offset_t key_off = vstr(js, key_val, &key_len);
6902
6903 if (key_str_out) *key_str_out = (const char *)(uintptr_t)key_off;
6904 if (key_len_out) *key_len_out = key_len;
6905
6906 return true;
6907}
6908
6909static ant_value_t builtin_object___defineGetter__(ant_t *js, ant_value_t *args, int nargs) {
6910 ant_value_t obj = legacy_accessor_this_obj(js, js->this_val);
6911 if (is_err(obj)) return obj;
6912 if (nargs < 2) return js_mkundef();
6913
6914 ant_value_t getter = args[1];
6915 uint8_t gt = vtype(getter);
6916
6917 if (gt != T_FUNC && gt != T_CFUNC) {
6918 return js_mkerr_typed(js, JS_ERR_TYPE, "Object.prototype.__defineGetter__: Expecting function");
6919 }
6920
6921 ant_value_t key_val = js_mkundef();
6922 const char *key_str = NULL;
6923 ant_offset_t key_len = 0;
6924 ant_offset_t sym_off = 0;
6925
6926 if (!legacy_accessor_key(js, args[0], &key_val, &key_str, &key_len, &sym_off)) {
6927 return key_val;
6928 }
6929
6930 if (is_proxy(js_as_obj(obj))) {
6931 GC_ROOT_SAVE(root_mark, js);
6932 GC_ROOT_PIN(js, obj);
6933 GC_ROOT_PIN(js, key_val);
6934 GC_ROOT_PIN(js, getter);
6935
6936 ant_value_t desc = js_mkobj(js);
6937 if (is_err(desc)) {
6938 GC_ROOT_RESTORE(js, root_mark);
6939 return desc;
6940 }
6941
6942 GC_ROOT_PIN(js, desc);
6943 js_setprop(js, desc, js_mkstr(js, "get", 3), getter);
6944 js_setprop(js, desc, js_mkstr(js, "enumerable", 10), js_true);
6945 js_setprop(js, desc, js_mkstr(js, "configurable", 12), js_true);
6946
6947 ant_value_t define_args[3] = { obj, key_val, desc };
6948 ant_value_t result = builtin_object_defineProperty(js, define_args, 3);
6949
6950 GC_ROOT_RESTORE(js, root_mark);
6951 if (is_err(result)) return result;
6952
6953 return js_mkundef();
6954 }
6955
6956 if (vtype(key_val) == T_SYMBOL) {
6957 js_set_sym_getter_desc(js, js_as_obj(obj), key_val, getter, JS_DESC_E | JS_DESC_C);
6958 } else js_set_getter_desc(js, js_as_obj(obj), key_str, (size_t)key_len, getter, JS_DESC_E | JS_DESC_C);
6959
6960 return js_mkundef();
6961}
6962
6963static ant_value_t builtin_object___defineSetter__(ant_t *js, ant_value_t *args, int nargs) {
6964 ant_value_t obj = legacy_accessor_this_obj(js, js->this_val);
6965 if (is_err(obj)) return obj;
6966 if (nargs < 2) return js_mkundef();
6967
6968 ant_value_t setter = args[1];
6969 uint8_t st = vtype(setter);
6970
6971 if (st != T_FUNC && st != T_CFUNC) {
6972 return js_mkerr_typed(js, JS_ERR_TYPE, "Object.prototype.__defineSetter__: Expecting function");
6973 }
6974
6975 ant_value_t key_val = js_mkundef();
6976 const char *key_str = NULL;
6977 ant_offset_t key_len = 0;
6978 ant_offset_t sym_off = 0;
6979
6980 if (!legacy_accessor_key(js, args[0], &key_val, &key_str, &key_len, &sym_off)) {
6981 return key_val;
6982 }
6983
6984 if (is_proxy(js_as_obj(obj))) {
6985 GC_ROOT_SAVE(root_mark, js);
6986 GC_ROOT_PIN(js, obj);
6987 GC_ROOT_PIN(js, key_val);
6988 GC_ROOT_PIN(js, setter);
6989
6990 ant_value_t desc = js_mkobj(js);
6991 if (is_err(desc)) {
6992 GC_ROOT_RESTORE(js, root_mark);
6993 return desc;
6994 }
6995
6996 GC_ROOT_PIN(js, desc);
6997 js_setprop(js, desc, js_mkstr(js, "set", 3), setter);
6998 js_setprop(js, desc, js_mkstr(js, "enumerable", 10), js_true);
6999 js_setprop(js, desc, js_mkstr(js, "configurable", 12), js_true);
7000
7001 ant_value_t define_args[3] = { obj, key_val, desc };
7002 ant_value_t result = builtin_object_defineProperty(js, define_args, 3);
7003
7004 GC_ROOT_RESTORE(js, root_mark);
7005 if (is_err(result)) return result;
7006
7007 return js_mkundef();
7008 }
7009
7010 if (vtype(key_val) == T_SYMBOL) {
7011 js_set_sym_setter_desc(js, js_as_obj(obj), key_val, setter, JS_DESC_E | JS_DESC_C);
7012 } else js_set_setter_desc(js, js_as_obj(obj), key_str, (size_t)key_len, setter, JS_DESC_E | JS_DESC_C);
7013
7014 return js_mkundef();
7015}
7016
7017static ant_value_t legacy_lookup_accessor_from_descriptor(ant_t *js, ant_value_t desc, bool want_getter) {
7018 if (vtype(desc) != T_OBJ) return js_mkundef();
7019 ant_offset_t off = lkp_interned(js, desc, want_getter ? js->intern.get : js->intern.set, 3);
7020 if (off == 0) return js_mkundef();
7021 return propref_load(js, off);
7022}
7023
7024static ant_value_t legacy_lookup_accessor(ant_t *js, ant_value_t this_val, ant_value_t key, bool want_getter) {
7025 ant_value_t obj = legacy_accessor_this_obj(js, this_val);
7026 if (is_err(obj)) return obj;
7027
7028 ant_value_t key_val = js_mkundef();
7029 const char *key_str = NULL;
7030 ant_offset_t key_len = 0;
7031 ant_offset_t sym_off = 0;
7032
7033 if (!legacy_accessor_key(js, key, &key_val, &key_str, &key_len, &sym_off)) {
7034 return key_val;
7035 }
7036
7037 for (ant_value_t cur = obj; is_object_type(cur); ) {
7038 if (is_proxy(cur)) {
7039 ant_value_t desc = proxy_get_own_property_descriptor(js, cur, key_val);
7040 if (is_err(desc)) return desc;
7041 if (vtype(desc) == T_OBJ) return legacy_lookup_accessor_from_descriptor(js, desc, want_getter);
7042 cur = proxy_get_prototype_of(js, cur);
7043 if (is_err(cur)) return cur;
7044 continue;
7045 }
7046
7047 prop_meta_t meta;
7048 bool has_meta = (vtype(key_val) == T_SYMBOL)
7049 ? lookup_symbol_prop_meta(cur, sym_off, &meta)
7050 : lookup_string_prop_meta(js, cur, key_str, (size_t)key_len, &meta);
7051 if (has_meta) {
7052 if (want_getter) return meta.has_getter ? meta.getter : js_mkundef();
7053 return meta.has_setter ? meta.setter : js_mkundef();
7054 }
7055
7056 if (vtype(key_val) == T_SYMBOL) {
7057 if (lkp_sym(js, cur, sym_off) != 0) return js_mkundef();
7058 } else {
7059 if (array_obj_ptr(cur) && is_length_key(key_str, key_len)) return js_mkundef();
7060 if (array_obj_ptr(cur) && is_array_index(key_str, key_len)) {
7061 unsigned long idx = 0;
7062 if (
7063 parse_array_index(key_str, key_len, get_array_length(js, cur), &idx) &&
7064 arr_has(js, cur, (ant_offset_t)idx)
7065 ) return js_mkundef();
7066 }
7067 if (lkp(js, cur, key_str, key_len) != 0) return js_mkundef();
7068 }
7069 cur = get_proto(js, cur);
7070 }
7071
7072 return js_mkundef();
7073}
7074
7075static ant_value_t builtin_object___lookupGetter__(ant_t *js, ant_value_t *args, int nargs) {
7076 if (nargs < 1) return js_mkundef();
7077 return legacy_lookup_accessor(js, js->this_val, args[0], true);
7078}
7079
7080static ant_value_t builtin_object___lookupSetter__(ant_t *js, ant_value_t *args, int nargs) {
7081 if (nargs < 1) return js_mkundef();
7082 return legacy_lookup_accessor(js, js->this_val, args[0], false);
7083}
7084
7085static ant_value_t builtin_object_create(ant_t *js, ant_value_t *args, int nargs) {
7086 if (nargs == 0) return js_mkerr(js, "Object.create requires a prototype argument");
7087
7088 ant_value_t proto = args[0];
7089 uint8_t pt = vtype(proto);
7090
7091 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC && pt != T_NULL) {
7092 return js_mkerr(js, "Object.create: prototype must be an object or null");
7093 }
7094
7095 ant_value_t obj = js_mkobj(js);
7096 if (pt == T_NULL) {
7097 js_set_proto_init(obj, js_mknull());
7098 } else js_set_proto_init(obj, proto);
7099
7100 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
7101 ant_value_t props = args[1];
7102 ant_iter_t iter = js_prop_iter_begin(js, props);
7103
7104 const char *key = NULL;
7105 size_t klen = 0;
7106 ant_value_t descriptor = js_mkundef();
7107
7108 while (js_prop_iter_next(&iter, &key, &klen, &descriptor)) {
7109 if (vtype(descriptor) != T_OBJ) continue;
7110 ant_offset_t val_off = lkp(js, descriptor, "value", 5);
7111 if (val_off == 0) continue;
7112 ant_value_t val = propref_load(js, val_off);
7113 ant_value_t key_str = js_mkstr(js, key, klen);
7114 js_setprop(js, obj, key_str, val);
7115 }
7116
7117 js_prop_iter_end(&iter);
7118 }
7119
7120 return obj;
7121}
7122
7123static ant_value_t builtin_object_hasOwn(ant_t *js, ant_value_t *args, int nargs) {
7124 if (nargs < 2) return mkval(T_BOOL, 0);
7125
7126 ant_value_t obj = args[0];
7127 uint8_t t = vtype(obj);
7128
7129 if (t == T_NULL || t == T_UNDEF) {
7130 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert undefined or null to object");
7131 }
7132
7133 ant_value_t key = args[1];
7134 if (vtype(key) != T_STR) {
7135 key = js_tostring_val(js, key);
7136 if (is_err(key)) return key;
7137 }
7138
7139 if (t == T_CFUNC) {
7140 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
7141 if (vtype(promoted) == T_FUNC) {
7142 obj = promoted;
7143 t = T_FUNC;
7144 } else {
7145 ant_offset_t key_len = 0;
7146 ant_offset_t key_off = vstr(js, key, &key_len);
7147 ant_value_t value = js_mkundef();
7148 return mkval(T_BOOL, js_cfunc_try_get_own(js, obj, (char *)(uintptr_t)(key_off), (size_t)key_len, &value) ? 1 : 0);
7149 }
7150 }
7151
7152 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0);
7153 ant_value_t as_obj = js_as_obj(obj);
7154 if (is_proxy(as_obj)) return proxy_has_own(js, as_obj, key);
7155
7156 ant_offset_t key_len, key_off = vstr(js, key, &key_len);
7157 const char *key_str = (char *)(uintptr_t)(key_off);
7158
7159 ant_offset_t off = lkp(js, as_obj, key_str, key_len);
7160 return mkval(T_BOOL, off != 0 ? 1 : 0);
7161}
7162
7163static ant_value_t builtin_object_groupBy(ant_t *js, ant_value_t *args, int nargs) {
7164 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Object.groupBy requires 2 arguments");
7165
7166 ant_value_t items = args[0];
7167 ant_value_t callback = args[1];
7168
7169 if (vtype(callback) != T_FUNC && vtype(callback) != T_CFUNC)
7170 return js_mkerr_typed(js, JS_ERR_TYPE, "callback is not a function");
7171
7172 ant_value_t result = js_mkobj(js);
7173 js_set_proto_init(result, js_mknull());
7174
7175 ant_offset_t len = get_array_length(js, items);
7176 for (ant_offset_t i = 0; i < len; i++) {
7177 ant_value_t val = arr_get(js, items, i);
7178 ant_value_t cb_args[2] = { val, tov((double)i) };
7179 ant_value_t key = sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 2, NULL, false);
7180 if (is_err(key)) return key;
7181
7182 ant_value_t key_str = js_tostring_val(js, key);
7183 if (is_err(key_str)) return key_str;
7184
7185 ant_offset_t klen;
7186 ant_offset_t koff = vstr(js, key_str, &klen);
7187 const char *kptr = (char *)(uintptr_t)(koff);
7188
7189 ant_offset_t grp_off = lkp(js, result, kptr, klen);
7190 ant_value_t group;
7191 if (grp_off) {
7192 group = propref_load(js, grp_off);
7193 } else {
7194 group = mkarr(js);
7195 js_setprop(js, result, key_str, group);
7196 }
7197 js_arr_push(js, group, val);
7198 }
7199
7200 return result;
7201}
7202
7203// TODO: decompose this huge function into small pieces
7204static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, int nargs) {
7205 if (nargs < 3) return js_mkerr(js, "Object.defineProperty requires 3 arguments");
7206
7207 ant_value_t obj = args[0];
7208 ant_value_t prop = args[1];
7209 ant_value_t descriptor = args[2];
7210 uint8_t t = vtype(obj);
7211
7212 if (t == T_CFUNC) {
7213 obj = js_cfunc_promote(js, obj);
7214 args[0] = obj;
7215 t = T_FUNC;
7216 }
7217
7218 if (!is_object_type(obj)) {
7219 return js_mkerr(js, "Object.defineProperty called on non-object");
7220 }
7221
7222 bool sym_key = (vtype(prop) == T_SYMBOL);
7223 if (!sym_key && vtype(prop) != T_STR) {
7224 char buf[64];
7225 size_t len = tostr(js, prop, buf, sizeof(buf));
7226 prop = js_mkstr(js, buf, len);
7227 }
7228
7229 if (vtype(descriptor) != T_OBJ) {
7230 return js_mkerr(js, "Property descriptor must be an object");
7231 }
7232
7233 ant_value_t as_obj = js_as_obj(obj);
7234 if (is_proxy(as_obj)) {
7235 ant_value_t proxy_result = proxy_define_property(js, as_obj, prop, descriptor);
7236 if (is_err(proxy_result)) return proxy_result;
7237 if (!js_truthy(js, proxy_result))
7238 return js_mkerr_typed(js, JS_ERR_TYPE, "'defineProperty' on proxy: trap returned falsy");
7239 return obj;
7240 }
7241
7242 ant_offset_t prop_len = 0;
7243 const char *prop_str = NULL;
7244 ant_offset_t sym_off = 0;
7245
7246 if (sym_key) {
7247 sym_off = (ant_offset_t)vdata(prop);
7248 const char *desc = js_sym_desc(prop);
7249 prop_str = desc ? desc : "symbol";
7250 prop_len = (ant_offset_t)strlen(prop_str);
7251 } else {
7252 ant_offset_t prop_off = vstr(js, prop, &prop_len);
7253 prop_str = (char *)(uintptr_t)(prop_off);
7254 if (streq(prop_str, prop_len, STR_PROTO, STR_PROTO_LEN))
7255 return js_mkerr(js, "Cannot define " STR_PROTO " property");
7256 }
7257
7258 bool has_value = false, has_get = false, has_set = false;
7259 bool has_writable = false, has_enumerable = false, has_configurable = false;
7260 ant_value_t value = js_mkundef();
7261 bool writable = false, enumerable = false, configurable = false;
7262
7263 ant_offset_t value_off = lkp(js, descriptor, "value", 5);
7264 if (value_off != 0) {
7265 has_value = true;
7266 value = propref_load(js, value_off);
7267 }
7268
7269 ant_offset_t get_off = lkp_interned(js, descriptor, js->intern.get, 3);
7270 if (get_off != 0) {
7271 has_get = true;
7272 ant_value_t getter = propref_load(js, get_off);
7273 if (vtype(getter) != T_FUNC && vtype(getter) != T_UNDEF) {
7274 return js_mkerr(js, "Getter must be a function");
7275 }
7276 }
7277
7278 ant_offset_t set_off = lkp_interned(js, descriptor, js->intern.set, 3);
7279 if (set_off != 0) {
7280 has_set = true;
7281 ant_value_t setter = propref_load(js, set_off);
7282 if (vtype(setter) != T_FUNC && vtype(setter) != T_UNDEF) {
7283 return js_mkerr(js, "Setter must be a function");
7284 }
7285 }
7286
7287 ant_offset_t writable_off = lkp(js, descriptor, "writable", 8);
7288 if (writable_off != 0) {
7289 has_writable = true;
7290 ant_value_t w_val = propref_load(js, writable_off);
7291 writable = js_truthy(js, w_val);
7292 }
7293
7294 ant_offset_t enumerable_off = lkp(js, descriptor, "enumerable", 10);
7295 if (enumerable_off != 0) {
7296 has_enumerable = true;
7297 ant_value_t e_val = propref_load(js, enumerable_off);
7298 enumerable = js_truthy(js, e_val);
7299 }
7300
7301 ant_offset_t configurable_off = lkp(js, descriptor, "configurable", 12);
7302 if (configurable_off != 0) {
7303 has_configurable = true;
7304 ant_value_t c_val = propref_load(js, configurable_off);
7305 configurable = js_truthy(js, c_val);
7306 }
7307
7308 if ((has_value || has_writable) && (has_get || has_set)) {
7309 return js_mkerr(js, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute");
7310 }
7311
7312 ant_object_t *arr_ptr = (!sym_key && is_length_key(prop_str, prop_len))
7313 ? array_obj_ptr(as_obj)
7314 : NULL;
7315 if (arr_ptr) {
7316 if (has_get || has_set) return js_mkerr(js, "Invalid property descriptor. Cannot use accessors for array length");
7317 if ((has_enumerable && enumerable) || (has_configurable && configurable))
7318 return js_mkerr(js, "Cannot redefine array length attributes");
7319
7320 ant_offset_t new_len = (ant_offset_t)arr_ptr->u.array.len;
7321 if (has_value) {
7322 ant_value_t len_err = validate_array_length(js, value);
7323 if (is_err(len_err)) return len_err;
7324 new_len = (ant_offset_t)tod(value);
7325 }
7326
7327 ant_offset_t doff = get_dense_buf(as_obj);
7328 if (doff) {
7329 ant_offset_t cap = dense_capacity(doff);
7330 ant_offset_t cur_len = get_array_length(js, as_obj);
7331 ant_offset_t clear_to = (cur_len < cap) ? cur_len : cap;
7332 if (new_len < clear_to) {
7333 for (ant_offset_t i = new_len; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY);
7334 }
7335 }
7336
7337 if (new_len > get_array_length(js, as_obj)) array_mark_may_have_holes(as_obj);
7338 array_len_set(js, as_obj, new_len);
7339
7340 return obj;
7341 }
7342
7343 ant_offset_t existing_off = sym_key ? lkp_sym(js, as_obj, sym_off) : lkp(js, as_obj, prop_str, prop_len);
7344 prop_meta_t existing_sym_meta;
7345
7346 bool has_existing_sym_meta = sym_key && lookup_symbol_prop_meta(as_obj, sym_off, &existing_sym_meta);
7347 bool has_existing_prop = (existing_off > 0) || has_existing_sym_meta;
7348 ant_object_t *obj_ptr = js_obj_ptr(as_obj);
7349
7350 if (!has_existing_prop) {
7351 if (obj_ptr && obj_ptr->frozen)
7352 return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str);
7353 if (obj_ptr && obj_ptr->sealed)
7354 return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str);
7355 if (obj_ptr && !obj_ptr->extensible)
7356 return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str);
7357 }
7358
7359 if (has_existing_sym_meta) {
7360 if (!has_writable) writable = existing_sym_meta.writable;
7361 if (!has_enumerable) enumerable = existing_sym_meta.enumerable;
7362 if (!has_configurable) configurable = existing_sym_meta.configurable;
7363 } else if (existing_off > 0) {
7364 ant_prop_ref_t *existing_ref = propref_get(js, existing_off);
7365 if (existing_ref && existing_ref->obj && existing_ref->obj->shape) {
7366 const ant_shape_prop_t *existing_prop =
7367 ant_shape_prop_at(existing_ref->obj->shape, existing_ref->slot);
7368 if (existing_prop) {
7369 uint8_t existing_attrs = existing_prop->attrs;
7370 if (!has_writable) writable = (existing_attrs & ANT_PROP_ATTR_WRITABLE) != 0;
7371 if (!has_enumerable) enumerable = (existing_attrs & ANT_PROP_ATTR_ENUMERABLE) != 0;
7372 if (!has_configurable) configurable = (existing_attrs & ANT_PROP_ATTR_CONFIGURABLE) != 0;
7373 }
7374 }
7375 }
7376
7377 if (has_get || has_set) {
7378 int desc_flags =
7379 (enumerable ? JS_DESC_E : 0) |
7380 (configurable ? JS_DESC_C : 0);
7381
7382 ant_value_t getter = has_get ? propref_load(js, get_off) : js_mkundef();
7383 ant_value_t setter = has_set ? propref_load(js, set_off) : js_mkundef();
7384
7385 if (sym_key) {
7386 if (has_get) js_set_sym_getter_desc(js, as_obj, prop, getter, desc_flags);
7387 if (has_set) js_set_sym_setter_desc(js, as_obj, prop, setter, desc_flags);
7388 } else {
7389 if (has_get && has_set) js_set_accessor_desc(js, as_obj, prop_str, prop_len, getter, setter, desc_flags);
7390 else if (has_get) js_set_getter_desc(js, as_obj, prop_str, prop_len, getter, desc_flags);
7391 else js_set_setter_desc(js, as_obj, prop_str, prop_len, setter, desc_flags);
7392 }
7393 } else {
7394 int desc_flags =
7395 (writable ? JS_DESC_W : 0) |
7396 (enumerable ? JS_DESC_E : 0) |
7397 (configurable ? JS_DESC_C : 0);
7398
7399 uint8_t attrs = 0;
7400 if (writable) attrs |= ANT_PROP_ATTR_WRITABLE;
7401 if (enumerable) attrs |= ANT_PROP_ATTR_ENUMERABLE;
7402 if (configurable) attrs |= ANT_PROP_ATTR_CONFIGURABLE;
7403
7404 if (existing_off > 0) {
7405 bool is_frozen = obj_ptr ? obj_ptr->frozen : false;
7406 bool is_nonconfig = is_nonconfig_prop(js, existing_off) || is_frozen;
7407 bool is_readonly = is_const_prop(js, existing_off) || is_frozen;
7408
7409 if (is_nonconfig) {
7410 if (configurable) return js_mkerr(js,
7411 "Cannot redefine property %.*s: cannot change configurable from false to true",
7412 (int)prop_len, prop_str
7413 );
7414
7415 if (is_readonly && has_writable && writable) return js_mkerr(js,
7416 "Cannot redefine property %.*s: cannot change writable from false to true",
7417 (int)prop_len, prop_str
7418 );
7419 }
7420
7421 if (is_nonconfig && is_readonly && has_value)
7422 return js_mkerr(js, "Cannot assign to read-only property '%.*s'", (int)prop_len, prop_str);
7423 if (has_value) js_saveval(js, existing_off, value);
7424 if (!sym_key) js_set_descriptor(js, as_obj, prop_str, prop_len, desc_flags);
7425
7426 ant_prop_ref_t *ref = propref_get(js, existing_off);
7427 if (ref && ref->obj) {
7428 if (!js_obj_ensure_unique_shape(ref->obj)) return js_mkerr(js, "oom");
7429 if (sym_key) ant_shape_set_attrs_symbol(ref->obj->shape, sym_off, attrs);
7430 else ant_shape_set_attrs_interned(ref->obj->shape, intern_string(prop_str, prop_len), attrs);
7431 ant_shape_clear_accessor_slot(ref->obj->shape, ref->slot);
7432 }
7433 } else {
7434 if (!has_value) value = js_mkundef();
7435 ant_value_t prop_key = sym_key ? prop : js_mkstr(js, prop_str, prop_len);
7436 uint8_t prop_attrs = ANT_PROP_ATTR_ENUMERABLE
7437 | (writable ? ANT_PROP_ATTR_WRITABLE : 0)
7438 | (configurable ? ANT_PROP_ATTR_CONFIGURABLE : 0);
7439 mkprop(js, as_obj, prop_key, value, prop_attrs);
7440 if (!sym_key) js_set_descriptor(js, as_obj, prop_str, prop_len, desc_flags);
7441
7442 if (obj_ptr && obj_ptr->shape) {
7443 if (!js_obj_ensure_unique_shape(obj_ptr)) return js_mkerr(js, "oom");
7444 if (sym_key) {
7445 ant_shape_set_attrs_symbol(obj_ptr->shape, sym_off, attrs);
7446 int32_t slot = ant_shape_lookup_symbol(obj_ptr->shape, sym_off);
7447 if (slot >= 0) ant_shape_clear_accessor_slot(obj_ptr->shape, (uint32_t)slot);
7448 } else {
7449 const char *interned = intern_string(prop_str, prop_len);
7450 if (interned) {
7451 ant_shape_set_attrs_interned(obj_ptr->shape, interned, attrs);
7452 int32_t slot = ant_shape_lookup_interned(obj_ptr->shape, interned);
7453 if (slot >= 0) ant_shape_clear_accessor_slot(obj_ptr->shape, (uint32_t)slot);
7454 }
7455 }
7456 }
7457 }
7458 }
7459
7460 if (!sym_key) array_define_or_set_index(js, as_obj, prop_str, (size_t)prop_len);
7461
7462 return obj;
7463}
7464
7465typedef struct {
7466 bool thrown_exists;
7467 ant_value_t thrown_value;
7468 ant_value_t thrown_stack;
7469} js_exception_state_t;
7470
7471static inline js_exception_state_t js_save_exception(ant_t *js) {
7472 js_exception_state_t saved = {
7473 .thrown_exists = js->thrown_exists,
7474 .thrown_value = js_mkundef(),
7475 .thrown_stack = js_mkundef(),
7476 };
7477 if (saved.thrown_exists) {
7478 saved.thrown_value = js->thrown_value;
7479 saved.thrown_stack = js->thrown_stack;
7480 }
7481 return saved;
7482}
7483
7484static inline void js_restore_exception(ant_t *js, const js_exception_state_t *saved) {
7485 js->thrown_exists = saved->thrown_exists;
7486 js->thrown_value = saved->thrown_value;
7487 js->thrown_stack = saved->thrown_stack;
7488}
7489
7490static ant_value_t strobj_call_custom_inspect(ant_t *js, ant_value_t obj) {
7491 ant_value_t inspect_sym = get_inspect_sym();
7492 if (vtype(inspect_sym) != T_SYMBOL) return js_mkundef();
7493
7494 ant_value_t inspect_fn = lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(inspect_sym));
7495 if (!is_callable(inspect_fn)) return js_mkundef();
7496
7497 js_exception_state_t saved = js_save_exception(js);
7498 ant_value_t depth_arg = js_mknum((double)(MAX_STRINGIFY_DEPTH - stringify_depth));
7499 ant_value_t result;
7500
7501 if (vtype(inspect_fn) == T_CFUNC) {
7502 ant_value_t saved_this = js->this_val;
7503 js->this_val = obj;
7504 result = js_as_cfunc(inspect_fn)(js, &depth_arg, 1);
7505 js->this_val = saved_this;
7506 } else result = sv_vm_call(js->vm, js, inspect_fn, obj, &depth_arg, 1, NULL, false);
7507
7508 if (is_err(result) || js->thrown_exists || vtype(result) != T_STR) {
7509 js_restore_exception(js, &saved);
7510 return js_mkundef();
7511 }
7512
7513 js_restore_exception(js, &saved);
7514 return result;
7515}
7516
7517ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode) {
7518 js_exception_state_t saved = js_save_exception(js);
7519 ant_value_t args[3] = { obj, prop, descriptor };
7520 ant_value_t result = builtin_object_defineProperty(js, args, 3);
7521
7522 if (!reflect_mode) return result;
7523 if (is_err(result)) {
7524 js_restore_exception(js, &saved);
7525 return js_false;
7526 }
7527 return js_true;
7528}
7529
7530static ant_value_t builtin_object_defineProperties(ant_t *js, ant_value_t *args, int nargs) {
7531 if (nargs < 2) return js_mkerr(js, "Object.defineProperties requires 2 arguments");
7532
7533 ant_value_t obj = args[0];
7534 ant_value_t props = args[1];
7535
7536 uint8_t t = vtype(obj);
7537 if (t != T_OBJ && t != T_ARR && t != T_FUNC) {
7538 return js_mkerr(js, "Object.defineProperties called on non-object");
7539 }
7540
7541 if (vtype(props) != T_OBJ) {
7542 return js_mkerr(js, "Property descriptors must be an object");
7543 }
7544
7545 ant_iter_t iter = js_prop_iter_begin(js, props);
7546 const char *key = NULL;
7547 size_t key_len = 0;
7548 ant_value_t descriptor = js_mkundef();
7549
7550 while (js_prop_iter_next(&iter, &key, &key_len, &descriptor)) {
7551 ant_value_t prop_key = js_mkstr(js, key, key_len);
7552 ant_value_t define_args[3] = { obj, prop_key, descriptor };
7553 ant_value_t result = builtin_object_defineProperty(js, define_args, 3);
7554 if (is_err(result)) {
7555 js_prop_iter_end(&iter);
7556 return result;
7557 }
7558 }
7559 js_prop_iter_end(&iter);
7560
7561 return obj;
7562}
7563
7564bool js_is_own_enumerable_prop(
7565 ant_t *js, ant_value_t source, ant_object_t *source_ptr,
7566 const ant_iter_key_t *key
7567) {
7568 if (!key) return false;
7569
7570 if (key->is_symbol) {
7571 if (!source_ptr || source_ptr->is_exotic) {
7572 prop_meta_t meta;
7573 return !lookup_symbol_prop_meta(js_as_obj(source), key->sym_off, &meta) || meta.enumerable;
7574 }
7575 return (ant_shape_get_attrs(source_ptr->shape, key->slot) & ANT_PROP_ATTR_ENUMERABLE) != 0;
7576 }
7577
7578 if (!key->str) return false;
7579
7580 if (!source_ptr || source_ptr->is_exotic) {
7581 descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), key->str, key->key_len);
7582 return !desc || desc->enumerable;
7583 }
7584
7585 return (ant_shape_get_attrs(source_ptr->shape, key->slot) & ANT_PROP_ATTR_ENUMERABLE) != 0;
7586}
7587
7588static ant_value_t builtin_object_assign(ant_t *js, ant_value_t *args, int nargs) {
7589 if (nargs == 0) return js_mkerr(js, "Object.assign requires at least 1 argument");
7590
7591 ant_value_t target = args[0];
7592 uint8_t t = vtype(target);
7593
7594 if (t == T_NULL || t == T_UNDEF) {
7595 return js_mkerr(js, "Cannot convert undefined or null to object");
7596 }
7597
7598 if (t != T_OBJ && t != T_ARR && t != T_FUNC) {
7599 target = js_mkobj(js);
7600 }
7601
7602 ant_value_t as_obj = js_as_obj(target);
7603
7604 for (int i = 1; i < nargs; i++) {
7605 ant_value_t source = args[i];
7606 uint8_t st = vtype(source);
7607
7608 if (st == T_NULL || st == T_UNDEF) continue;
7609 if (st != T_OBJ && st != T_ARR && st != T_FUNC) continue;
7610
7611 ant_iter_t iter = js_prop_iter_begin(js, source);
7612 ant_object_t *source_ptr = js_obj_ptr(js_as_obj(source));
7613
7614 ant_iter_key_t key = {0};
7615 ant_value_t val = js_mkundef();
7616
7617 while (js_prop_iter_next_key(&iter, &key, &val)) {
7618 if (!js_is_own_enumerable_prop(js, source, source_ptr, &key)) continue;
7619 ant_value_t prop_key = key.is_symbol ? mkval(T_SYMBOL, key.sym_off) : js_mkstr(js, key.str, key.key_len);
7620 js_setprop(js, as_obj, prop_key, val);
7621 }
7622
7623 js_prop_iter_end(&iter);
7624 }
7625
7626 return target;
7627}
7628
7629ant_value_t builtin_object_freeze(ant_t *js, ant_value_t *args, int nargs) {
7630 if (nargs == 0) return js_mkundef();
7631
7632 ant_value_t obj = args[0];
7633 uint8_t t = vtype(obj);
7634
7635 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj;
7636 ant_value_t as_obj = js_as_obj(obj);
7637
7638 ant_object_t *ptr = js_obj_ptr(as_obj);
7639 if (!ptr || !ptr->shape) return obj;
7640 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
7641
7642 uint32_t count = ant_shape_count(ptr->shape);
7643 for (uint32_t i = 0; i < count; i++) {
7644 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
7645 if (!prop) continue;
7646
7647 uint8_t attrs = ant_shape_get_attrs(ptr->shape, i);
7648 attrs &= (uint8_t)~ANT_PROP_ATTR_WRITABLE;
7649 attrs &= (uint8_t)~ANT_PROP_ATTR_CONFIGURABLE;
7650
7651 if (prop->type == ANT_SHAPE_KEY_STRING) {
7652 const char *key = prop->key.interned;
7653 ant_shape_set_attrs_interned(ptr->shape, key, attrs);
7654 } else ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs);
7655 }
7656
7657 ptr->frozen = 1;
7658 return obj;
7659}
7660
7661static ant_value_t builtin_object_isFrozen(ant_t *js, ant_value_t *args, int nargs) {
7662 if (nargs == 0) return js_true;
7663
7664 ant_value_t obj = args[0];
7665 uint8_t t = vtype(obj);
7666
7667 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true;
7668 ant_value_t as_obj = js_as_obj(obj);
7669
7670 ant_object_t *ptr = js_obj_ptr(as_obj);
7671 return js_bool(ptr && ptr->frozen);
7672}
7673
7674static ant_value_t builtin_object_seal(ant_t *js, ant_value_t *args, int nargs) {
7675 if (nargs == 0) return js_mkundef();
7676
7677 ant_value_t obj = args[0];
7678 uint8_t t = vtype(obj);
7679
7680 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj;
7681 ant_value_t as_obj = js_as_obj(obj);
7682
7683 ant_object_t *ptr = js_obj_ptr(as_obj);
7684 if (!ptr || !ptr->shape) return obj;
7685 if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom");
7686 ptr->sealed = 1;
7687
7688 uint32_t count = ant_shape_count(ptr->shape);
7689 for (uint32_t i = 0; i < count; i++) {
7690 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
7691 if (!prop) continue;
7692
7693 uint8_t attrs = ant_shape_get_attrs(ptr->shape, i);
7694 attrs &= (uint8_t)~ANT_PROP_ATTR_CONFIGURABLE;
7695
7696 if (prop->type == ANT_SHAPE_KEY_STRING) {
7697 const char *key = prop->key.interned;
7698 ant_shape_set_attrs_interned(ptr->shape, key, attrs);
7699 } else {
7700 ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs);
7701 }
7702 }
7703
7704 return obj;
7705}
7706
7707static ant_value_t builtin_object_isSealed(ant_t *js, ant_value_t *args, int nargs) {
7708 if (nargs == 0) return js_true;
7709
7710 ant_value_t obj = args[0];
7711 uint8_t t = vtype(obj);
7712
7713 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true;
7714 ant_value_t as_obj = js_as_obj(obj);
7715
7716 ant_object_t *ptr = js_obj_ptr(as_obj);
7717 if (ptr && (ptr->sealed || ptr->frozen)) return js_true;
7718
7719 return js_false;
7720}
7721
7722typedef struct {
7723 ant_value_t result;
7724} object_from_entries_iter_ctx_t;
7725
7726static iter_action_t object_from_entries_iter_cb(ant_t *js, ant_value_t entry, void *ctx, ant_value_t *out) {
7727 object_from_entries_iter_ctx_t *state = ctx;
7728 GC_ROOT_SAVE(root_mark, js);
7729 GC_ROOT_PIN(js, state->result);
7730 GC_ROOT_PIN(js, entry);
7731
7732 if (vtype(entry) != T_ARR && vtype(entry) != T_OBJ) {
7733 *out = js_mkerr(js, "Object.fromEntries iterable values must be entry objects");
7734 GC_ROOT_RESTORE(js, root_mark);
7735 return ITER_ERROR;
7736 }
7737
7738 ant_value_t key = arr_get(js, entry, 0);
7739 GC_ROOT_PIN(js, key);
7740 if (is_undefined(key)) {
7741 *out = js_mkerr(js, "Object.fromEntries iterable values must contain a key");
7742 GC_ROOT_RESTORE(js, root_mark);
7743 return ITER_ERROR;
7744 }
7745
7746 ant_value_t val = arr_get(js, entry, 1);
7747 GC_ROOT_PIN(js, val);
7748
7749 if (vtype(key) != T_STR && vtype(key) != T_SYMBOL) {
7750 char buf[64];
7751 size_t n = tostr(js, key, buf, sizeof(buf));
7752 key = js_mkstr(js, buf, n);
7753 GC_ROOT_PIN(js, key);
7754 }
7755
7756 js_setprop(js, state->result, key, val);
7757 *out = state->result;
7758 GC_ROOT_RESTORE(js, root_mark);
7759 return ITER_CONTINUE;
7760}
7761
7762static ant_value_t builtin_object_fromEntries(ant_t *js, ant_value_t *args, int nargs) {
7763 if (nargs == 0) return js_mkerr(js, "Object.fromEntries requires an iterable argument");
7764
7765 GC_ROOT_SAVE(root_mark, js);
7766 ant_value_t iterable = args[0];
7767 GC_ROOT_PIN(js, iterable);
7768
7769 object_from_entries_iter_ctx_t ctx = {
7770 .result = js_mkobj(js),
7771 };
7772 GC_ROOT_PIN(js, ctx.result);
7773
7774 ant_value_t iter_result = iter_foreach(js, iterable, object_from_entries_iter_cb, &ctx);
7775 if (is_err(iter_result)) {
7776 if (vtype(iterable) == T_ARR || vtype(iterable) == T_OBJ || vtype(iterable) == T_FUNC) {
7777 GC_ROOT_RESTORE(js, root_mark);
7778 return iter_result;
7779 }
7780 ant_value_t err = js_mkerr(js, "Object.fromEntries requires an iterable");
7781 GC_ROOT_RESTORE(js, root_mark);
7782 return err;
7783 }
7784
7785 ant_value_t result = ctx.result;
7786 GC_ROOT_RESTORE(js, root_mark);
7787 return result;
7788}
7789
7790static ant_value_t builtin_object_getOwnPropertyDescriptor(ant_t *js, ant_value_t *args, int nargs) {
7791 if (nargs < 1) return js_mkundef();
7792
7793 ant_value_t obj = args[0];
7794 ant_value_t key = args[1];
7795 uint8_t t = vtype(obj);
7796
7797 if (t == T_CFUNC) {
7798 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
7799 if (vtype(promoted) == T_FUNC) {
7800 obj = promoted;
7801 t = T_FUNC;
7802 }
7803 }
7804
7805 if (t == T_CFUNC) {
7806 bool is_sym = (vtype(key) == T_SYMBOL);
7807 if (is_sym) return js_mkundef();
7808
7809 const char *key_str = NULL;
7810 ant_offset_t key_len = 0;
7811 if (vtype(key) == T_STR) {
7812 ant_offset_t key_off = vstr(js, key, &key_len);
7813 key_str = (const char *)(uintptr_t)key_off;
7814 } else {
7815 char buf[64];
7816 size_t n = tostr(js, key, buf, sizeof(buf));
7817 key = js_mkstr(js, buf, n);
7818 ant_offset_t key_off = vstr(js, key, &key_len);
7819 key_str = (const char *)(uintptr_t)key_off;
7820 }
7821
7822 ant_value_t value = js_mkundef();
7823 if (!js_cfunc_try_get_own(js, obj, key_str, (size_t)key_len, &value)) return js_mkundef();
7824
7825 ant_value_t result = js_mkobj(js);
7826 js_setprop(js, result, js_mkstr(js, "value", 5), value);
7827 js_setprop(js, result, js_mkstr(js, "writable", 8), js_false);
7828 js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_false);
7829 js_setprop(js, result, js_mkstr(js, "configurable", 12), js_true);
7830
7831 return result;
7832 }
7833
7834 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_mkundef();
7835
7836 const char *key_str;
7837 ant_offset_t key_len;
7838
7839 bool is_sym = (vtype(key) == T_SYMBOL);
7840 if (is_sym) {
7841 const char *d = js_sym_desc(key);
7842 key_str = d ? d : "symbol";
7843 key_len = (ant_offset_t)strlen(key_str);
7844 } else if (vtype(key) == T_STR) {
7845 ant_offset_t key_off = vstr(js, key, &key_len);
7846 key_str = (char *)(uintptr_t)(key_off);
7847 } else {
7848 char buf[64];
7849 size_t n = tostr(js, key, buf, sizeof(buf));
7850 key = js_mkstr(js, buf, n);
7851 ant_offset_t key_off = vstr(js, key, &key_len);
7852 key_str = (char *)(uintptr_t)(key_off);
7853 }
7854
7855 ant_value_t as_obj = js_as_obj(obj);
7856 if (is_proxy(as_obj)) {
7857 return proxy_get_own_property_descriptor(js, as_obj, key);
7858 }
7859
7860 bool is_arr_obj = array_obj_ptr(as_obj) != NULL;
7861 bool is_arr_length = !is_sym && is_arr_obj && is_length_key(key_str, key_len);
7862 bool has_arr_index = false;
7863 ant_value_t arr_index_val = js_mkundef();
7864
7865 if (!is_sym && is_arr_obj && is_array_index(key_str, key_len)) {
7866 unsigned long idx;
7867 if (
7868 parse_array_index(key_str, key_len, get_array_length(js, as_obj), &idx) &&
7869 arr_has(js, as_obj, (ant_offset_t)idx)
7870 ) { has_arr_index = true; arr_index_val = arr_get(js, as_obj, (ant_offset_t)idx); }
7871 }
7872
7873 ant_offset_t sym_off = is_sym ? (ant_offset_t)vdata(key) : 0;
7874 prop_meta_t sym_meta; prop_meta_t str_meta;
7875
7876 bool has_sym_meta = is_sym ? lookup_symbol_prop_meta(as_obj, sym_off, &sym_meta) : false;
7877 bool has_str_meta = is_sym ? false : lookup_string_prop_meta(js, as_obj, key_str, (size_t)key_len, &str_meta);
7878
7879 ant_offset_t prop_off = is_sym ? lkp_sym(js, as_obj, sym_off) : lkp(js, as_obj, key_str, key_len);
7880 if (prop_off == 0 && !(is_sym ? has_sym_meta : has_str_meta) && !is_arr_length && !has_arr_index) {
7881 return js_mkundef();
7882 }
7883
7884 bool has_getter = false;
7885 bool has_setter = false;
7886
7887 ant_value_t result = js_mkobj(js);
7888 ant_value_t getter = js_mkundef();
7889 ant_value_t setter = js_mkundef();
7890
7891 bool writable = true;
7892 bool enumerable = true;
7893 bool configurable = true;
7894
7895 if (is_sym && has_sym_meta) {
7896 has_getter = sym_meta.has_getter;
7897 has_setter = sym_meta.has_setter;
7898 getter = sym_meta.getter;
7899 setter = sym_meta.setter;
7900 writable = sym_meta.writable;
7901 enumerable = sym_meta.enumerable;
7902 configurable = sym_meta.configurable;
7903 } else if (!is_sym && has_str_meta) {
7904 has_getter = str_meta.has_getter;
7905 has_setter = str_meta.has_setter;
7906 getter = str_meta.getter;
7907 setter = str_meta.setter;
7908 writable = str_meta.writable;
7909 enumerable = str_meta.enumerable;
7910 configurable = str_meta.configurable;
7911 }
7912
7913 if (has_getter || has_setter) {
7914 if (has_getter) js_setprop(js, result, js_mkstr(js, "get", 3), getter);
7915 if (has_setter) js_setprop(js, result, js_mkstr(js, "set", 3), setter);
7916 js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(enumerable));
7917 js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(configurable));
7918 } else {
7919 ant_value_t prop_val = js_mkundef();
7920 bool has_value_out = false;
7921 if (prop_off != 0) {
7922 prop_val = propref_load(js, prop_off);
7923 has_value_out = true;
7924 } else if (has_arr_index) {
7925 prop_val = arr_index_val;
7926 has_value_out = true;
7927 } else if (is_arr_length) {
7928 prop_val = tov((double)get_array_length(js, as_obj));
7929 has_value_out = true;
7930 }
7931 if (has_value_out) {
7932 if (vtype(prop_val) == T_CFUNC) prop_val = js_cfunc_promote(js, prop_val);
7933 js_setprop(js, result, js_mkstr(js, "value", 5), prop_val);
7934 }
7935 js_setprop(js, result, js_mkstr(js, "writable", 8), js_bool(writable));
7936 js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(enumerable));
7937 js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(configurable));
7938 }
7939
7940 return result;
7941}
7942
7943static inline bool own_prop_names_is_dense_shadow(
7944 ant_t *js, ant_value_t obj,
7945 const char *key, ant_offset_t key_len
7946) {
7947 ant_offset_t doff = get_dense_buf(obj);
7948 if (!doff) return false;
7949
7950 ant_offset_t dense_len = dense_iterable_length(js, obj);
7951 if (dense_len <= 0 || !is_array_index(key, key_len)) return false;
7952
7953 unsigned long dense_idx = 0;
7954 return parse_array_index(key, (size_t)key_len, dense_len, &dense_idx);
7955}
7956
7957static ant_value_t builtin_object_getOwnPropertyNames(ant_t *js, ant_value_t *args, int nargs) {
7958 if (nargs == 0) return mkarr(js);
7959 ant_value_t obj = args[0];
7960
7961 if (vtype(obj) == T_CFUNC) {
7962 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
7963 if (vtype(promoted) == T_FUNC) obj = promoted;
7964 }
7965
7966 if (vtype(obj) == T_CFUNC) {
7967 ant_value_t arr = mkarr(js);
7968 ant_offset_t idx = 0;
7969 arr_set(js, arr, idx++, js->length_str);
7970
7971 if (vtype(js_cfunc_name_value(js, obj)) == T_STR) arr_set(js, arr, idx++, ANT_STRING("name"));
7972 if (js_cfunc_has_prototype(obj)) arr_set(js, arr, idx++, ANT_STRING("prototype"));
7973
7974 return mkval(T_ARR, vdata(arr));
7975 }
7976
7977 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js);
7978 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj);
7979
7980 ant_object_t *ptr = js_obj_ptr(obj);
7981 if (!ptr || !ptr->shape) return mkarr(js);
7982 bool is_arr_obj = (vtype(obj) == T_ARR);
7983
7984 ant_value_t arr = mkarr(js);
7985 ant_offset_t idx = 0;
7986
7987 if (is_arr_obj) {
7988 for (ant_offset_t i = 0;; i++) {
7989 ant_offset_t doff = get_dense_buf(obj);
7990 if (!doff) break;
7991
7992 ant_offset_t dense_len = dense_iterable_length(js, obj);
7993 if (i >= dense_len) break;
7994
7995 ant_value_t v = dense_get(doff, i);
7996 if (is_empty_slot(v)) continue;
7997
7998 char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i);
7999 arr_set(js, arr, idx++, js_mkstr(js, idxstr, idxlen));
8000 }}
8001
8002 uint32_t count = ant_shape_count(ptr->shape);
8003 for (uint32_t i = 0; i < count; i++) {
8004 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
8005 if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue;
8006 const char *key = prop->key.interned;
8007 ant_offset_t klen = (ant_offset_t)strlen(key);
8008
8009 if (is_arr_obj && own_prop_names_is_dense_shadow(js, obj, key, klen)) continue;
8010 arr_set(js, arr, idx++, js_mkstr(js, key, (size_t)klen));
8011 }
8012
8013 if (is_arr_obj) arr_set(js, arr, idx++, js->length_str);
8014 return mkval(T_ARR, vdata(arr));
8015}
8016
8017static ant_value_t builtin_object_getOwnPropertySymbols(ant_t *js, ant_value_t *args, int nargs) {
8018 if (nargs == 0) return mkarr(js);
8019 ant_value_t obj = args[0];
8020
8021 if (vtype(obj) == T_CFUNC) {
8022 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
8023 if (vtype(promoted) == T_FUNC) obj = promoted;
8024 }
8025
8026 uint8_t t = vtype(obj);
8027 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkarr(js);
8028 if (t == T_FUNC) obj = js_func_obj(obj);
8029
8030 ant_object_t *ptr = js_obj_ptr(obj);
8031 ant_value_t arr = mkarr(js);
8032 ant_offset_t idx = 0;
8033 if (!ptr || !ptr->shape) return mkval(T_ARR, vdata(arr));
8034
8035 uint32_t count = ant_shape_count(ptr->shape);
8036 for (uint32_t i = 0; i < count; i++) {
8037 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
8038 if (!prop || prop->type != ANT_SHAPE_KEY_SYMBOL) continue;
8039 arr_set(js, arr, idx++, mkval(T_SYMBOL, prop->key.sym_off));
8040 }
8041
8042 return mkval(T_ARR, vdata(arr));
8043}
8044
8045static ant_value_t object_add_descriptors_for_keys(
8046 ant_t *js,
8047 ant_value_t result,
8048 ant_value_t obj,
8049 ant_value_t keys
8050) {
8051 ant_offset_t len = get_array_length(js, keys);
8052
8053 for (ant_offset_t i = 0; i < len; i++) {
8054 ant_value_t key = arr_get(js, keys, i);
8055 if (vtype(key) == T_UNDEF) continue;
8056
8057 ant_value_t desc_args[2] = { obj, key };
8058 ant_value_t desc = builtin_object_getOwnPropertyDescriptor(js, desc_args, 2);
8059 if (is_err(desc)) return desc;
8060 if (vtype(desc) == T_UNDEF) continue;
8061
8062 ant_value_t set_result = js_setprop(js, result, key, desc);
8063 if (is_err(set_result)) return set_result;
8064 }
8065
8066 return js_mkundef();
8067}
8068
8069static ant_value_t builtin_object_getOwnPropertyDescriptors(ant_t *js, ant_value_t *args, int nargs) {
8070 ant_value_t result = js_mkobj(js);
8071
8072 if (is_object_type(js->sym.object_proto)) js_set_proto_init(result, js->sym.object_proto);
8073 if (nargs == 0 || vtype(args[0]) == T_NULL || vtype(args[0]) == T_UNDEF) {
8074 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert undefined or null to object");
8075 }
8076
8077 ant_value_t obj = args[0];
8078 uint8_t t = vtype(obj);
8079
8080 if (t != T_OBJ && t != T_ARR && t != T_FUNC) {
8081 obj = builtin_Object(js, &obj, 1);
8082 if (is_err(obj)) return obj;
8083 t = vtype(obj);
8084 }
8085
8086 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return result;
8087 ant_value_t names = builtin_object_getOwnPropertyNames(js, &obj, 1);
8088 if (is_err(names)) return names;
8089
8090 ant_value_t err = object_add_descriptors_for_keys(js, result, obj, names);
8091 if (is_err(err)) return err;
8092
8093 ant_value_t symbols = builtin_object_getOwnPropertySymbols(js, &obj, 1);
8094 if (is_err(symbols)) return symbols;
8095
8096 err = object_add_descriptors_for_keys(js, result, obj, symbols);
8097 if (is_err(err)) return err;
8098
8099 return result;
8100}
8101
8102static ant_value_t builtin_object_isExtensible(ant_t *js, ant_value_t *args, int nargs) {
8103 if (nargs == 0) return js_true;
8104
8105 ant_value_t obj = args[0];
8106 uint8_t t = vtype(obj);
8107
8108 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true;
8109 ant_value_t as_obj = js_as_obj(obj);
8110
8111 ant_object_t *ptr = js_obj_ptr(as_obj);
8112 if (!ptr) return js_true;
8113 if (ptr->frozen || ptr->sealed) return js_false;
8114 return js_bool(ptr->extensible);
8115}
8116
8117static ant_value_t builtin_object_preventExtensions(ant_t *js, ant_value_t *args, int nargs) {
8118 if (nargs == 0) return js_mkundef();
8119
8120 ant_value_t obj = args[0];
8121 uint8_t t = vtype(obj);
8122
8123 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj;
8124 ant_value_t as_obj = js_as_obj(obj);
8125
8126 ant_object_t *ptr = js_obj_ptr(as_obj);
8127 if (ptr) ptr->extensible = 0;
8128 return obj;
8129}
8130
8131static ant_value_t builtin_object_hasOwnProperty(ant_t *js, ant_value_t *args, int nargs) {
8132 if (nargs < 1) return mkval(T_BOOL, 0);
8133
8134 ant_value_t obj = js->this_val;
8135 ant_value_t key = args[0];
8136
8137 uint8_t t = vtype(obj);
8138
8139 if (t == T_CFUNC) {
8140 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
8141 if (vtype(promoted) == T_FUNC) {
8142 obj = promoted;
8143 t = T_FUNC;
8144 } else {
8145 if (vtype(key) == T_SYMBOL) return mkval(T_BOOL, 0);
8146 if (vtype(key) != T_STR) {
8147 char buf[64];
8148 size_t n = tostr(js, key, buf, sizeof(buf));
8149 key = js_mkstr(js, buf, n);
8150 }
8151 ant_offset_t key_len = 0;
8152 ant_offset_t key_off = vstr(js, key, &key_len);
8153 ant_value_t value = js_mkundef();
8154 return mkval(T_BOOL, js_cfunc_try_get_own(js, obj, (char *)(uintptr_t)(key_off), (size_t)key_len, &value) ? 1 : 0);
8155 }
8156 }
8157
8158 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0);
8159 ant_value_t as_obj = js_as_obj(obj);
8160
8161 if (is_proxy(as_obj)) return proxy_has_own(js, as_obj, key);
8162 bool is_arr_obj = array_obj_ptr(as_obj) != NULL;
8163
8164 if (vtype(key) == T_SYMBOL) {
8165 ant_offset_t sym_off = (ant_offset_t)vdata(key);
8166 ant_offset_t off = lkp_sym(js, as_obj, sym_off);
8167 if (off != 0) return mkval(T_BOOL, 1);
8168 prop_meta_t meta;
8169 return mkval(T_BOOL, lookup_symbol_prop_meta(as_obj, sym_off, &meta) ? 1 : 0);
8170 }
8171
8172 const char *key_str = NULL;
8173 ant_offset_t key_len = 0;
8174 if (vtype(key) != T_STR) {
8175 char buf[64];
8176 size_t n = tostr(js, key, buf, sizeof(buf));
8177 key = js_mkstr(js, buf, n);
8178 }
8179 ant_offset_t key_off = vstr(js, key, &key_len);
8180 key_str = (char *)(uintptr_t)(key_off);
8181
8182 if (is_arr_obj && is_length_key(key_str, key_len)) return mkval(T_BOOL, 1);
8183 if (is_arr_obj && is_array_index(key_str, key_len)) {
8184 unsigned long idx;
8185 if (parse_array_index(key_str, key_len, get_array_length(js, as_obj), &idx)) {
8186 return mkval(T_BOOL, arr_has(js, as_obj, (ant_offset_t)idx) ? 1 : 0);
8187 }
8188 }
8189
8190 ant_offset_t off = lkp(js, as_obj, key_str, key_len);
8191 if (off != 0) return mkval(T_BOOL, 1);
8192 ant_object_t *ptr = js_obj_ptr(as_obj);
8193 if (ptr && ptr->is_exotic) {
8194 descriptor_entry_t *desc = lookup_descriptor(as_obj, key_str, key_len);
8195 return mkval(T_BOOL, (desc && (desc->has_getter || desc->has_setter)) ? 1 : 0);
8196 }
8197 return mkval(T_BOOL, 0);
8198}
8199
8200static bool proto_chain_contains_cycle_safe(ant_t *js, ant_value_t start, ant_value_t target);
8201
8202bool js_is_prototype_of(ant_t *js, ant_value_t proto_obj, ant_value_t obj) {
8203 uint8_t obj_type = vtype(obj);
8204 if (obj_type != T_OBJ && obj_type != T_ARR && obj_type != T_FUNC) return false;
8205 uint8_t proto_type = vtype(proto_obj);
8206 if (proto_type != T_OBJ && proto_type != T_ARR && proto_type != T_FUNC) return false;
8207 ant_value_t current = get_proto(js, obj);
8208 return proto_chain_contains_cycle_safe(js, current, proto_obj);
8209}
8210
8211ant_value_t builtin_object_isPrototypeOf(ant_t *js, ant_value_t *args, int nargs) {
8212 if (nargs < 1) return mkval(T_BOOL, 0);
8213 return mkval(T_BOOL, js_is_prototype_of(js, js->this_val, args[0]) ? 1 : 0);
8214}
8215
8216static ant_value_t builtin_object_propertyIsEnumerable(ant_t *js, ant_value_t *args, int nargs) {
8217 if (nargs < 1) return mkval(T_BOOL, 0);
8218
8219 ant_value_t obj = js->this_val;
8220 ant_value_t key = args[0];
8221
8222 uint8_t t = vtype(obj);
8223
8224 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0);
8225 ant_value_t as_obj = js_as_obj(obj);
8226 bool is_arr_obj = array_obj_ptr(as_obj) != NULL;
8227
8228 if (vtype(key) == T_SYMBOL) {
8229 ant_offset_t sym_off = (ant_offset_t)vdata(key);
8230 ant_offset_t off = lkp_sym(js, as_obj, sym_off);
8231 if (off == 0) return mkval(T_BOOL, 0);
8232 prop_meta_t meta;
8233 if (lookup_symbol_prop_meta(as_obj, sym_off, &meta))
8234 return mkval(T_BOOL, meta.enumerable ? 1 : 0);
8235 return mkval(T_BOOL, 1);
8236 }
8237
8238 const char *key_str = NULL;
8239 ant_offset_t key_len = 0;
8240 if (vtype(key) != T_STR) {
8241 char buf[64];
8242 size_t n = tostr(js, key, buf, sizeof(buf));
8243 key = js_mkstr(js, buf, n);
8244 }
8245 ant_offset_t key_off = vstr(js, key, &key_len);
8246 key_str = (char *)(uintptr_t)(key_off);
8247
8248 if (is_arr_obj && is_length_key(key_str, key_len)) {
8249 return mkval(T_BOOL, 0);
8250 }
8251
8252 if (is_arr_obj) {
8253 unsigned long idx;
8254 if (parse_array_index(key_str, key_len, get_array_length(js, as_obj), &idx)) {
8255 return mkval(T_BOOL, arr_has(js, as_obj, (ant_offset_t)idx) ? 1 : 0);
8256 }
8257 }
8258
8259 ant_offset_t off = lkp(js, as_obj, key_str, key_len);
8260 if (off == 0) return mkval(T_BOOL, 0);
8261
8262 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
8263 if (prop_meta && !js_obj_ptr(as_obj)->is_exotic) {
8264 bool enumerable = (prop_meta->attrs & ANT_PROP_ATTR_ENUMERABLE) != 0;
8265 return mkval(T_BOOL, enumerable ? 1 : 0);
8266 }
8267
8268 ant_object_t *ptr = js_obj_ptr(as_obj);
8269 if (ptr && ptr->is_exotic) {
8270 prop_meta_t meta;
8271 if (lookup_string_prop_meta(js, as_obj, key_str, (size_t)key_len, &meta))
8272 return mkval(T_BOOL, meta.enumerable ? 1 : 0);
8273 }
8274 return mkval(T_BOOL, 1);
8275}
8276
8277static ant_value_t builtin_object_toString(ant_t *js, ant_value_t *args, int nargs) {
8278 (void)args; (void)nargs;
8279 ant_value_t obj = js->this_val;
8280
8281 uint8_t t = vtype(obj);
8282
8283 const char *tag = NULL;
8284 ant_offset_t tag_len = 0;
8285
8286 ant_value_t tag_sym = get_toStringTag_sym();
8287 if (vtype(tag_sym) == T_SYMBOL) {
8288 ant_offset_t sym_off = (ant_offset_t)vdata(tag_sym);
8289 ant_offset_t tag_off = 0;
8290 if (is_object_type(obj)) {
8291 tag_off = lkp_sym_proto(js, obj, sym_off);
8292 } else {
8293 ant_value_t proto = get_prototype_for_type(js, t);
8294 if (is_object_type(proto)) {
8295 tag_off = lkp_sym_proto(js, proto, sym_off);
8296 }
8297 }
8298 if (tag_off != 0) {
8299 ant_value_t tag_val = propref_load(js, tag_off);
8300 if (vtype(tag_val) == T_STR) {
8301 ant_offset_t str_off = vstr(js, tag_val, &tag_len);
8302 tag = (const char *)(uintptr_t)(str_off);
8303 }
8304 }
8305 }
8306
8307 if (!tag) {
8308 if (is_object_type(obj) && get_slot(obj, SLOT_ERROR_BRAND) == js_true) {
8309 tag = "Error"; tag_len = 5;
8310 } else switch (t) {
8311 case T_UNDEF: tag = "Undefined"; tag_len = 9; break;
8312 case T_NULL: tag = "Null"; tag_len = 4; break;
8313 case T_BOOL: tag = "Boolean"; tag_len = 7; break;
8314 case T_NUM: tag = "Number"; tag_len = 6; break;
8315 case T_STR: tag = "String"; tag_len = 6; break;
8316 case T_ARR: tag = "Array"; tag_len = 5; break;
8317 case T_FUNC: tag = "Function"; tag_len = 8; break;
8318 case T_CFUNC: tag = "Function"; tag_len = 8; break;
8319 case T_ERR: tag = "Error"; tag_len = 5; break;
8320 case T_BIGINT: tag = "BigInt"; tag_len = 6; break;
8321 case T_PROMISE: tag = "Promise"; tag_len = 7; break;
8322 case T_OBJ: tag = "Object"; tag_len = 6; break;
8323 default: tag = "Unknown"; tag_len = 7; break;
8324 }}
8325
8326 char static_buf[64];
8327 string_builder_t sb;
8328
8329 string_builder_init(&sb, static_buf, sizeof(static_buf));
8330 string_builder_append(&sb, "[object ", 8);
8331 string_builder_append(&sb, tag, tag_len);
8332 string_builder_append(&sb, "]", 1);
8333
8334 return string_builder_finalize(js, &sb);
8335}
8336
8337static ant_value_t builtin_object_valueOf(ant_t *js, ant_value_t *args, int nargs) {
8338 return js->this_val;
8339}
8340
8341static ant_value_t builtin_object_toLocaleString(ant_t *js, ant_value_t *args, int nargs) {
8342 return js_call_toString(js, js->this_val);
8343}
8344
8345static inline ant_value_t require_callback(ant_t *js, ant_value_t *args, int nargs, const char *name) {
8346 if (nargs == 0 || !is_callable(args[0]))
8347 return js_mkerr(js, "%s requires a function argument", name);
8348 return args[0];
8349}
8350
8351static ant_value_t array_shallow_copy(ant_t *js, ant_value_t arr, ant_offset_t len) {
8352 ant_value_t result = mkarr(js);
8353 if (is_err(result)) return result;
8354
8355 ant_offset_t doff = get_dense_buf(arr);
8356 if (doff) {
8357 for (ant_offset_t i = 0; i < len; i++) {
8358 ant_value_t v = dense_get(doff, i);
8359 arr_set(js, result, i, v);
8360 }
8361 return result;
8362 }
8363
8364 ant_iter_t iter = js_prop_iter_begin(js, arr);
8365 const char *key;
8366 size_t key_len;
8367 ant_value_t val;
8368
8369 while (js_prop_iter_next(&iter, &key, &key_len, &val)) {
8370 if (key_len == 0 || key[0] > '9' || key[0] < '0') continue;
8371 js_mkprop_fast(js, result, key, key_len, val);
8372 }
8373
8374 js_prop_iter_end(&iter);
8375 array_len_set(js, result, len);
8376 return result;
8377}
8378
8379static ant_value_t builtin_array_push(ant_t *js, ant_value_t *args, int nargs) {
8380 ant_value_t arr = js->this_val;
8381 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
8382 return js_mkerr(js, "push called on non-array");
8383 }
8384
8385 if (is_proxy(arr)) {
8386 ant_offset_t off = lkp_interned(js, arr, js->intern.length, 6);
8387 ant_offset_t len = 0;
8388 if (off != 0) {
8389 ant_value_t len_val = propref_load(js, off);
8390 if (vtype(len_val) == T_NUM) len = (ant_offset_t) tod(len_val);
8391 }
8392 for (int i = 0; i < nargs; i++) {
8393 char idxstr[16];
8394 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len);
8395 ant_value_t key = js_mkstr(js, idxstr, idxlen);
8396 js_setprop(js, arr, key, args[i]);
8397 len++;
8398 }
8399
8400 ant_value_t len_val = tov((double) len);
8401 js_setprop(js, arr, js->length_str, len_val);
8402 return len_val;
8403 }
8404
8405 ant_offset_t len = get_array_length(js, arr);
8406
8407 ant_offset_t doff = get_dense_buf(arr);
8408 if (doff) {
8409 for (int i = 0; i < nargs; i++) {
8410 ant_offset_t cap = dense_capacity(doff);
8411 if (len >= cap) {
8412 doff = dense_grow(js, arr, len + 1);
8413 if (doff == 0) return js_mkerr(js, "oom");
8414 }
8415 if (is_empty_slot(args[i])) array_mark_may_have_holes(arr);
8416 dense_set(js, doff, len, args[i]);
8417 len++;
8418 }
8419 array_len_set(js, arr, len);
8420 return tov((double) len);
8421 }
8422
8423 for (int i = 0; i < nargs; i++) {
8424 char idxstr[16];
8425 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len);
8426 js_mkprop_fast(js, arr, idxstr, idxlen, args[i]); len++;
8427 }
8428
8429 ant_value_t new_len = tov((double) len);
8430 array_len_set(js, arr, len);
8431
8432 return new_len;
8433}
8434
8435void js_arr_push(ant_t *js, ant_value_t arr, ant_value_t val) {
8436 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) return;
8437
8438 ant_offset_t doff = get_dense_buf(arr);
8439 if (doff) {
8440 ant_offset_t len = get_array_length(js, arr);
8441 ant_offset_t cap = dense_capacity(doff);
8442 if (len >= cap) {
8443 doff = dense_grow(js, arr, len + 1);
8444 if (doff == 0) return;
8445 }
8446 if (is_empty_slot(val)) array_mark_may_have_holes(arr);
8447 dense_set(js, doff, len, val);
8448 array_len_set(js, arr, len + 1);
8449 return;
8450 }
8451
8452 ant_offset_t len = get_array_length(js, arr);
8453
8454 char idxstr[16];
8455 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len);
8456 js_mkprop_fast(js, arr, idxstr, idxlen, val);
8457 array_len_set(js, arr, len + 1);
8458}
8459
8460static ant_value_t builtin_array_pop(ant_t *js, ant_value_t *args, int nargs) {
8461 ant_value_t arr = js->this_val;
8462
8463 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
8464 return js_mkerr(js, "pop called on non-array");
8465 }
8466
8467 if (is_proxy(arr)) {
8468 ant_offset_t len = proxy_aware_length(js, arr);
8469 if (len == 0) {
8470 js_setprop(js, arr, js->length_str, tov(0.0));
8471 return js_mkundef();
8472 }
8473 len--;
8474 char idxstr[16];
8475 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len);
8476 ant_value_t result = proxy_aware_get_elem(js, arr, idxstr, idxlen);
8477 js_setprop(js, arr, js->length_str, tov((double) len));
8478 return result;
8479 }
8480
8481 ant_offset_t doff = get_dense_buf(arr);
8482 if (doff) {
8483 ant_offset_t len = get_array_length(js, arr);
8484 ant_offset_t dense_len = dense_iterable_length(js, arr);
8485 if (len == 0) return js_mkundef();
8486 if (len != dense_len) goto pop_slow;
8487 len--;
8488 ant_value_t result = (len < dense_len) ? dense_get(doff, len) : js_mkundef();
8489 if (is_empty_slot(result)) result = js_mkundef();
8490 if (len < dense_len) {
8491 dense_set(js, doff, len, T_EMPTY);
8492 }
8493 array_len_set(js, arr, len);
8494 return result;
8495 }
8496
8497 pop_slow:
8498 ant_offset_t len = get_array_length(js, arr);
8499
8500 if (len == 0) return js_mkundef();
8501 len--; char idxstr[16];
8502 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len);
8503
8504 ant_offset_t elem_off = 0;
8505
8506 if (elem_off == 0) elem_off = lkp(js, arr, idxstr, idxlen);
8507 ant_value_t result = js_mkundef();
8508 if (elem_off != 0) result = propref_load(js, elem_off);
8509
8510 array_len_set(js, arr, len);
8511
8512 return result;
8513}
8514
8515static ant_value_t builtin_array_slice(ant_t *js, ant_value_t *args, int nargs) {
8516 ant_value_t arr = js->this_val;
8517 ant_value_t string_val = unwrap_primitive(js, arr);
8518 bool string_like = (vtype(string_val) == T_STR);
8519
8520 if (!string_like && vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
8521 return js_mkerr(js, "slice called on non-array");
8522 }
8523
8524 ant_offset_t len = 0;
8525 ant_offset_t string_byte_len = 0;
8526 ant_offset_t string_off = 0;
8527
8528 if (string_like) {
8529 string_off = vstr(js, string_val, &string_byte_len);
8530 len = (ant_offset_t)utf16_strlen((const char *)(uintptr_t)string_off, string_byte_len);
8531 } else len = get_array_length(js, arr);
8532
8533 ant_offset_t start = 0, end = len;
8534 double dlen = D(len);
8535 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
8536 double d = tod(args[0]);
8537 if (d < 0) start = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen);
8538 else start = (ant_offset_t) (d > dlen ? dlen : d);
8539 }
8540
8541 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
8542 double d = tod(args[1]);
8543 if (d < 0) end = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen);
8544 else end = (ant_offset_t) (d > dlen ? dlen : d);
8545 }
8546
8547 if (start > end) start = end;
8548 ant_value_t result = array_alloc_like(js, arr);
8549
8550 if (is_err(result)) return result;
8551 ant_offset_t result_idx = 0;
8552
8553 for (ant_offset_t i = start; i < end; i++) {
8554 ant_value_t elem = js_mkundef();
8555 if (string_like) {
8556 uint32_t code_unit = utf16_code_unit_at((const char *)(uintptr_t)string_off, string_byte_len, i);
8557 if (code_unit == 0xFFFFFFFF) break;
8558 elem = js_string_from_utf16_code_unit(js, code_unit);
8559 } else elem = arr_get(js, arr, i);
8560 arr_set(js, result, result_idx, elem);
8561 result_idx++;
8562 }
8563
8564 return result;
8565}
8566
8567static ant_value_t builtin_array_join(ant_t *js, ant_value_t *args, int nargs) {
8568 ant_value_t arr = js->this_val;
8569 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
8570 return js_mkerr(js, "join called on non-array");
8571 }
8572 const char *sep = ",";
8573 ant_offset_t sep_len = 1;
8574
8575 if (nargs >= 1) {
8576 if (vtype(args[0]) == T_STR) {
8577 sep_len = 0;
8578 ant_offset_t sep_off = vstr(js, args[0], &sep_len);
8579 sep = (const char *)(uintptr_t)(sep_off);
8580 } else if (vtype(args[0]) != T_UNDEF) {
8581 const char *sep_str = js_str(js, args[0]);
8582 sep = sep_str;
8583 sep_len = (ant_offset_t) strlen(sep_str);
8584 }
8585 }
8586
8587 ant_offset_t len = get_array_length(js, arr);
8588
8589 if (len == 0) return js_mkstr(js, "", 0);
8590
8591 size_t capacity = 1024;
8592 size_t result_len = 0;
8593 char *result = (char *)ant_calloc(capacity);
8594 if (!result) return js_mkerr(js, "oom");
8595
8596 for (ant_offset_t i = 0; i < len; i++) {
8597 if (i > 0) {
8598 if (result_len + sep_len >= capacity) {
8599 capacity = (result_len + sep_len + 1) * 2;
8600 char *new_result = (char *)ant_realloc(result, capacity);
8601 if (!new_result) return js_mkerr(js, "oom");
8602 result = new_result;
8603 }
8604 memcpy(result + result_len, sep, sep_len);
8605 result_len += sep_len;
8606 }
8607
8608 {
8609 ant_value_t elem = arr_get(js, arr, i);
8610 uint8_t et = vtype(elem);
8611 if (et == T_NULL || et == T_UNDEF) continue;
8612
8613 const char *elem_str = NULL;
8614 size_t elem_len = 0;
8615 char numstr[64];
8616 ant_value_t str_val = js_mkundef();
8617
8618 if (et == T_STR) {
8619 ant_offset_t soff, slen;
8620 soff = vstr(js, elem, &slen);
8621 elem_str = (const char *)(uintptr_t)(soff);
8622 elem_len = slen;
8623 } else if (et == T_NUM) {
8624 elem_len = strnum(elem, numstr, sizeof(numstr));
8625 elem_str = numstr;
8626 } else if (et == T_BOOL) {
8627 elem_str = vdata(elem) ? "true" : "false";
8628 elem_len = strlen(elem_str);
8629 } else if (et == T_ARR || et == T_OBJ || et == T_FUNC || et == T_BIGINT) {
8630 str_val = to_string_val(js, elem);
8631
8632 if (is_err(str_val)) {
8633 free(result);
8634 return str_val;
8635 }
8636
8637 if (vtype(str_val) == T_STR) {
8638 ant_offset_t soff, slen;
8639 soff = vstr(js, str_val, &slen);
8640 elem_str = (const char *)(uintptr_t)(soff);
8641 elem_len = slen;
8642 }
8643 }
8644
8645
8646 if (elem_str && elem_len > 0) {
8647 if (result_len + elem_len >= capacity) {
8648 capacity = (result_len + elem_len + 1) * 2;
8649 char *new_result = (char *)ant_realloc(result, capacity);
8650 if (!new_result) { free(result); return js_mkerr(js, "oom"); }
8651 result = new_result;
8652 }
8653 memcpy(result + result_len, elem_str, elem_len);
8654 result_len += elem_len;
8655 }
8656 }
8657 }
8658
8659 ant_value_t ret = js_mkstr(js, result, result_len);
8660 free(result); return ret;
8661}
8662
8663typedef struct {
8664 ant_value_t search;
8665 double search_num;
8666 uint8_t search_type;
8667 uint8_t mode;
8668} array_includes_query_t;
8669
8670enum {
8671 ARRAY_INCLUDES_MATCH_GENERIC = 0,
8672 ARRAY_INCLUDES_MATCH_NUM = 1,
8673 ARRAY_INCLUDES_MATCH_NAN = 2,
8674 ARRAY_INCLUDES_MATCH_ID = 3,
8675};
8676
8677static inline array_includes_query_t array_includes_prepare_query(ant_value_t search) {
8678 array_includes_query_t q = {
8679 .search = search,
8680 .search_num = 0.0,
8681 .search_type = vtype(search),
8682 .mode = ARRAY_INCLUDES_MATCH_GENERIC,
8683 };
8684
8685 if (q.search_type == T_NUM) {
8686 q.search_num = tod(search);
8687 q.mode = isnan(q.search_num)
8688 ? ARRAY_INCLUDES_MATCH_NAN
8689 : ARRAY_INCLUDES_MATCH_NUM;
8690 } else if (
8691 q.search_type != T_STR &&
8692 q.search_type != T_BIGINT
8693 ) q.mode = ARRAY_INCLUDES_MATCH_ID;
8694
8695 return q;
8696}
8697
8698static inline bool array_includes_matches(ant_t *js, const array_includes_query_t *q, ant_value_t val) {
8699switch (q->mode) {
8700 case ARRAY_INCLUDES_MATCH_NUM: return vtype(val) == T_NUM && tod(val) == q->search_num;
8701 case ARRAY_INCLUDES_MATCH_NAN: return vtype(val) == T_NUM && isnan(tod(val));
8702 case ARRAY_INCLUDES_MATCH_ID: return vtype(val) == q->search_type && vdata(val) == vdata(q->search);
8703 default: return strict_eq_values(js, val, q->search);
8704}}
8705
8706static inline ant_offset_t array_includes_length_from_number(double len_num) {
8707 if (isnan(len_num) || len_num <= 0) return 0;
8708 if (len_num >= (double)UINT32_MAX) return UINT32_MAX;
8709 return (ant_offset_t)len_num;
8710}
8711
8712static inline ant_offset_t array_includes_start_index(ant_t *js, ant_value_t *args, int nargs, ant_offset_t len) {
8713 int64_t start = 0;
8714
8715 if (nargs >= 2 && vtype(args[1]) != T_UNDEF) {
8716 double from_index_num = js_to_number(js, args[1]);
8717 if (!isnan(from_index_num)) start = (int64_t)from_index_num;
8718 if (start < 0) {
8719 start += (int64_t)len;
8720 if (start < 0) start = 0;
8721 }}
8722
8723 if ((uint64_t)start >= (uint64_t)len) return len;
8724 return (ant_offset_t)start;
8725}
8726
8727static ant_value_t array_includes_length_value(ant_t *js, ant_value_t arr) {
8728 if (array_obj_ptr(arr) && !is_proxy(arr)) return tov((double)get_array_length(js, arr));
8729 if (is_proxy(arr)) return proxy_get(js, arr, "length", 6);
8730
8731 ant_offset_t off = lkp(js, arr, "length", 6);
8732 if (off != 0) {
8733 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
8734 if (prop_meta && prop_meta->has_getter) {
8735 ant_value_t accessor_result;
8736 if (try_accessor_getter(js, arr, "length", 6, &accessor_result)) return accessor_result;
8737 }
8738 return propref_load(js, off);
8739 }
8740
8741 if (lkp_proto(js, arr, "length", 6) == 0) return js_mkundef();
8742 return js_getprop_super(js, get_proto(js, arr), arr, "length");
8743}
8744
8745static ant_value_t array_includes_get_index_value(
8746 ant_t *js, ant_value_t arr, ant_offset_t idx, char *idxstr, size_t idxlen
8747) {
8748 if (is_proxy(arr)) return proxy_get(js, arr, idxstr, idxlen);
8749
8750 if (array_obj_ptr(arr)) {
8751 if (arr_has(js, arr, idx)) return arr_get(js, arr, idx);
8752 if (lkp_proto(js, arr, idxstr, idxlen) == 0) return js_mkundef();
8753 idxstr[idxlen] = '\0';
8754 return js_getprop_super(js, get_proto(js, arr), arr, idxstr);
8755 }
8756
8757 ant_offset_t off = lkp(js, arr, idxstr, idxlen);
8758 if (off != 0) {
8759 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
8760 if (prop_meta && prop_meta->has_getter) {
8761 ant_value_t accessor_result;
8762 if (try_accessor_getter(js, arr, idxstr, idxlen, &accessor_result)) return accessor_result;
8763 }
8764 return propref_load(js, off);
8765 }
8766
8767 if (lkp_proto(js, arr, idxstr, idxlen) == 0) return js_mkundef();
8768 idxstr[idxlen] = '\0';
8769 return js_getprop_super(js, get_proto(js, arr), arr, idxstr);
8770}
8771
8772static bool array_includes_get_dense_index_value(
8773 ant_value_t obj, ant_offset_t idx, ant_value_t *out
8774) {
8775 ant_value_t arr = (vtype(obj) == T_FUNC) ? js_func_obj(obj) : obj;
8776 if (vtype(arr) != T_ARR) return false;
8777
8778 ant_offset_t doff = get_dense_buf(arr);
8779 if (!doff || idx >= dense_capacity(doff)) return false;
8780
8781 ant_value_t val = dense_get(doff, idx);
8782 if (is_empty_slot(val)) return false;
8783
8784 *out = val;
8785 return true;
8786}
8787
8788static bool array_includes_get_proto_dense_index_value(
8789 ant_t *js, ant_value_t arr, ant_offset_t idx, ant_value_t *out
8790) {
8791 ant_value_t proto = get_proto(js, arr);
8792 for (int depth = 0; is_object_type(proto) && depth < MAX_PROTO_CHAIN_DEPTH; depth++) {
8793 if (array_includes_get_dense_index_value(proto, idx, out)) return true;
8794 proto = get_proto(js, proto);
8795 }
8796
8797 return false;
8798}
8799
8800static ant_value_t array_includes_get_array_index_value(
8801 ant_t *js, ant_value_t arr, ant_offset_t idx, char *idxstr, size_t idxlen
8802) {
8803 if (arr_has(js, arr, idx)) return arr_get(js, arr, idx);
8804 ant_value_t proto_dense_val = js_mkundef();
8805 if (array_includes_get_proto_dense_index_value(js, arr, idx, &proto_dense_val))
8806 return proto_dense_val;
8807 if (lkp_proto(js, arr, idxstr, idxlen) == 0) return js_mkundef();
8808 idxstr[idxlen] = '\0';
8809 return js_getprop_super(js, get_proto(js, arr), arr, idxstr);
8810}
8811
8812static bool array_includes_object_may_have_indexed_props(
8813 ant_t *js, ant_value_t obj, bool include_dense
8814) {
8815 if (!is_object_type(obj) || is_proxy(obj)) return is_proxy(obj);
8816
8817 ant_value_t as_obj = (vtype(obj) == T_FUNC) ? js_func_obj(obj) : obj;
8818 ant_object_t *ptr = js_obj_ptr(as_obj);
8819 if (!ptr) return false;
8820 if (ptr->is_exotic) return true;
8821
8822 if (include_dense && vtype(as_obj) == T_ARR) {
8823 ant_offset_t doff = get_dense_buf(as_obj);
8824 if (doff) {
8825 ant_offset_t dense_len = dense_capacity(doff);
8826 for (ant_offset_t i = 0; i < dense_len; i++)
8827 if (!is_empty_slot(dense_get(doff, i))) return true;
8828 }
8829 }
8830
8831 if (!ptr->shape) return false;
8832 uint32_t shape_count = ant_shape_count(ptr->shape);
8833 for (uint32_t i = 0; i < shape_count; i++) {
8834 const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i);
8835 if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue;
8836 if (i >= ptr->prop_count) continue;
8837
8838 const char *key = prop->key.interned;
8839 size_t key_len = strlen(key);
8840 if (is_array_index(key, (ant_offset_t)key_len)) return true;
8841 }
8842
8843 return false;
8844}
8845
8846static bool array_includes_can_skip_hole_lookups(ant_t *js, ant_value_t arr) {
8847 if (array_includes_object_may_have_indexed_props(js, arr, false)) return false;
8848
8849 ant_value_t proto = get_proto(js, arr);
8850 for (int depth = 0; is_object_type(proto) && depth < MAX_PROTO_CHAIN_DEPTH; depth++) {
8851 if (array_includes_object_may_have_indexed_props(js, proto, true)) return false;
8852 proto = get_proto(js, proto);
8853 }
8854
8855 return true;
8856}
8857
8858static ant_value_t array_includes_dense_fast(
8859 ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_offset_t len, ant_offset_t start
8860) {
8861 if (!array_obj_ptr(arr) || is_proxy(arr)) return js_mkundef();
8862
8863 ant_offset_t doff = get_dense_buf(arr);
8864 if (!doff) return js_mkundef();
8865
8866 ant_value_t *dense = dense_data(doff);
8867 if (!dense) return js_mkundef();
8868
8869 ant_offset_t dense_len = dense_iterable_length(js, arr);
8870 if (dense_len != len) return js_mkundef();
8871
8872 if (!array_may_have_holes(arr)) {
8873 for (ant_offset_t i = start; i < len; i++)
8874 if (array_includes_matches(js, query, dense[i])) return mkval(T_BOOL, 1);
8875 return mkval(T_BOOL, 0);
8876 }
8877
8878 for (ant_offset_t i = start; i < len; i++) {
8879 ant_value_t val = dense[i];
8880 if (is_empty_slot(val)) return js_mkundef();
8881 if (array_includes_matches(js, query, val)) return mkval(T_BOOL, 1);
8882 }
8883
8884 return mkval(T_BOOL, 0);
8885}
8886
8887static ant_value_t array_includes_array_slow(
8888 ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_offset_t len, ant_offset_t start
8889) {
8890 bool skip_hole_lookups =
8891 query->search_type != T_UNDEF &&
8892 array_includes_can_skip_hole_lookups(js, arr);
8893
8894 ant_value_t *dense = NULL;
8895 ant_offset_t dense_len = 0;
8896
8897 if (skip_hole_lookups) {
8898 if (!array_may_have_dense_elements(arr)) return mkval(T_BOOL, 0);
8899
8900 ant_offset_t doff = get_dense_buf(arr);
8901 if (doff) {
8902 dense = dense_data(doff);
8903 dense_len = dense_iterable_length(js, arr);
8904 }
8905
8906 if (dense) for (ant_offset_t i = start; i < dense_len; i++) {
8907 ant_value_t val = dense[i];
8908 if (is_empty_slot(val)) continue;
8909 if (array_includes_matches(js, query, val)) return mkval(T_BOOL, 1);
8910 }
8911
8912 return mkval(T_BOOL, 0);
8913 }
8914
8915 for (ant_offset_t i = start; i < len; i++) {
8916 ant_value_t val;
8917
8918 char idxstr[16];
8919 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i);
8920
8921 val = array_includes_get_array_index_value(js, arr, i, idxstr, idxlen);
8922 if (is_err(val)) return val;
8923
8924 if (array_includes_matches(js, query, val)) return mkval(T_BOOL, 1);
8925 }
8926
8927 return mkval(T_BOOL, 0);
8928}
8929
8930static ant_value_t array_includes_generic(
8931 ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_value_t *args, int nargs
8932) {
8933 ant_value_t len_val = array_includes_length_value(js, arr);
8934 if (is_err(len_val)) return len_val;
8935
8936 ant_offset_t len = array_includes_length_from_number(js_to_number(js, len_val));
8937 if (len == 0) return mkval(T_BOOL, 0);
8938
8939 ant_offset_t start = array_includes_start_index(js, args, nargs, len);
8940 if (start >= len) return mkval(T_BOOL, 0);
8941
8942 for (ant_offset_t i = start; i < len; i++) {
8943 char idxstr[16];
8944 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i);
8945
8946 ant_value_t val = array_includes_get_index_value(js, arr, i, idxstr, idxlen);
8947 if (is_err(val)) return val;
8948 if (array_includes_matches(js, query, val)) return mkval(T_BOOL, 1);
8949 }
8950
8951 return mkval(T_BOOL, 0);
8952}
8953
8954ant_value_t js_array_includes_call(ant_t *js, ant_value_t arr, ant_value_t *args, int nargs) {
8955 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
8956 return js_mkerr(js, "includes called on non-array");
8957
8958 array_includes_query_t query = array_includes_prepare_query(
8959 (nargs > 0) ? args[0] : js_mkundef()
8960 );
8961
8962 if (array_obj_ptr(arr) && !is_proxy(arr)) {
8963 ant_offset_t len = get_array_length(js, arr);
8964 if (len == 0) return mkval(T_BOOL, 0);
8965
8966 ant_offset_t start = array_includes_start_index(js, args, nargs, len);
8967 if (start >= len) return mkval(T_BOOL, 0);
8968
8969 ant_value_t fast = array_includes_dense_fast(js, arr, &query, len, start);
8970 if (vtype(fast) != T_UNDEF) return fast;
8971
8972 return array_includes_array_slow(js, arr, &query, len, start);
8973 }
8974
8975 return array_includes_generic(js, arr, &query, args, nargs);
8976}
8977
8978bool js_is_array_includes_builtin(ant_value_t func) {
8979 return vtype(func) == T_CFUNC && js_cfunc_same_entrypoint(func, builtin_array_includes);
8980}
8981
8982ant_value_t builtin_array_includes(ant_t *js, ant_value_t *args, int nargs) {
8983 return js_array_includes_call(js, js->this_val, args, nargs);
8984}
8985
8986static ant_value_t builtin_array_every(ant_t *js, ant_value_t *args, int nargs) {
8987 ant_value_t arr = js->this_val;
8988
8989 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
8990 return js_mkerr(js, "every called on non-array");
8991
8992 ant_value_t callback = require_callback(js, args, nargs, "every");
8993 if (is_err(callback)) return callback;
8994 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
8995
8996 ant_offset_t len = get_array_length(js, arr);
8997
8998 for (ant_offset_t i = 0; i < len; i++) {
8999 if (!arr_has(js, arr, i)) continue;
9000 ant_value_t val = arr_get(js, arr, i);
9001 ant_value_t call_args[3] = { val, tov((double)i), arr };
9002 ant_value_t result = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9003 if (is_err(result)) return result;
9004 if (!js_truthy(js, result)) return mkval(T_BOOL, 0);
9005 }
9006
9007 return mkval(T_BOOL, 1);
9008}
9009
9010static ant_value_t builtin_array_forEach(ant_t *js, ant_value_t *args, int nargs) {
9011 ant_value_t arr = js->this_val;
9012
9013 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9014 return js_mkerr(js, "forEach called on non-array");
9015
9016 ant_value_t callback = require_callback(js, args, nargs, "forEach");
9017 if (is_err(callback)) return callback;
9018 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9019
9020 ant_offset_t len = get_array_length(js, arr);
9021
9022 for (ant_offset_t i = 0; i < len; i++) {
9023 if (!arr_has(js, arr, i)) continue;
9024 ant_value_t val = arr_get(js, arr, i);
9025 ant_value_t call_args[3] = { val, tov((double)i), arr };
9026 ant_value_t result = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9027 if (is_err(result)) return result;
9028 }
9029
9030 return js_mkundef();
9031}
9032
9033static ant_value_t builtin_array_reverse(ant_t *js, ant_value_t *args, int nargs) {
9034 (void)args; (void)nargs;
9035 ant_value_t arr = js->this_val;
9036
9037 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9038 return js_mkerr(js, "reverse called on non-array");
9039
9040 if (is_proxy(arr)) {
9041 ant_offset_t len = proxy_aware_length(js, arr);
9042 if (len <= 1) return arr;
9043 ant_value_t read_from = proxy_read_target(js, arr);
9044 ant_offset_t lower = 0;
9045 while (lower < len / 2) {
9046 ant_offset_t upper_idx = len - lower - 1;
9047 bool lower_exists = arr_has(js, read_from, lower);
9048 bool upper_exists = arr_has(js, read_from, upper_idx);
9049 ant_value_t lower_val = lower_exists ? arr_get(js, read_from, lower) : js_mkundef();
9050 ant_value_t upper_val = upper_exists ? arr_get(js, read_from, upper_idx) : js_mkundef();
9051 if (lower_exists && upper_exists) {
9052 char s1[16]; size_t l1 = uint_to_str(s1, sizeof(s1), (unsigned)lower);
9053 js_setprop(js, arr, js_mkstr(js, s1, l1), upper_val);
9054 char s2[16]; size_t l2 = uint_to_str(s2, sizeof(s2), (unsigned)upper_idx);
9055 js_setprop(js, arr, js_mkstr(js, s2, l2), lower_val);
9056 } else if (upper_exists) {
9057 char s[16]; size_t l = uint_to_str(s, sizeof(s), (unsigned)lower);
9058 js_setprop(js, arr, js_mkstr(js, s, l), upper_val);
9059 } else if (lower_exists) {
9060 char s[16]; size_t l = uint_to_str(s, sizeof(s), (unsigned)upper_idx);
9061 js_setprop(js, arr, js_mkstr(js, s, l), lower_val);
9062 } lower++;
9063 } return arr;
9064 }
9065
9066 ant_offset_t len = get_array_length(js, arr);
9067 if (len <= 1) return arr;
9068
9069 ant_offset_t doff = get_dense_buf(arr);
9070 if (doff) {
9071 for (ant_offset_t i = 0; i < len / 2; i++) {
9072 ant_value_t a = dense_get(doff, i);
9073 ant_value_t b = dense_get(doff, len - 1 - i);
9074 dense_set(js, doff, i, b);
9075 dense_set(js, doff, len - 1 - i, a);
9076 }
9077 return arr;
9078 }
9079
9080 for (ant_offset_t lower = 0; lower < len / 2; lower++) {
9081 ant_offset_t upper = len - lower - 1;
9082 bool lower_exists = arr_has(js, arr, lower);
9083 bool upper_exists = arr_has(js, arr, upper);
9084 ant_value_t lower_val = lower_exists ? arr_get(js, arr, lower) : js_mkundef();
9085 ant_value_t upper_val = upper_exists ? arr_get(js, arr, upper) : js_mkundef();
9086
9087 if (lower_exists && upper_exists) {
9088 arr_set(js, arr, lower, upper_val);
9089 arr_set(js, arr, upper, lower_val);
9090 } else if (upper_exists) {
9091 arr_set(js, arr, lower, upper_val);
9092 arr_del(js, arr, upper);
9093 } else if (lower_exists) {
9094 arr_set(js, arr, upper, lower_val);
9095 arr_del(js, arr, lower);
9096 }
9097 }
9098 return arr;
9099}
9100
9101static ant_value_t builtin_array_map(ant_t *js, ant_value_t *args, int nargs) {
9102 ant_value_t arr = js->this_val;
9103
9104 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9105 return js_mkerr(js, "map called on non-array");
9106
9107 ant_value_t callback = require_callback(js, args, nargs, "map");
9108 if (is_err(callback)) return callback;
9109 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9110
9111 ant_offset_t len = get_array_length(js, arr);
9112 ant_value_t result = array_alloc_like(js, arr);
9113 if (is_err(result)) return result;
9114
9115 for (ant_offset_t i = 0; i < len; i++) {
9116 if (!arr_has(js, arr, i)) continue;
9117 ant_value_t val = arr_get(js, arr, i);
9118 ant_value_t call_args[3] = { val, tov((double)i), arr };
9119 ant_value_t mapped = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9120 if (is_err(mapped)) return mapped;
9121 arr_set(js, result, i, mapped);
9122 }
9123
9124 return result;
9125}
9126
9127static ant_value_t builtin_array_filter(ant_t *js, ant_value_t *args, int nargs) {
9128 ant_value_t arr = js->this_val;
9129
9130 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9131 return js_mkerr(js, "filter called on non-array");
9132
9133 ant_value_t callback = require_callback(js, args, nargs, "filter");
9134 if (is_err(callback)) return callback;
9135 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9136
9137 ant_offset_t len = get_array_length(js, arr);
9138 ant_value_t result = array_alloc_like(js, arr);
9139 if (is_err(result)) return result;
9140
9141 ant_offset_t result_idx = 0;
9142
9143 for (ant_offset_t i = 0; i < len; i++) {
9144 if (!arr_has(js, arr, i)) continue;
9145 ant_value_t val = arr_get(js, arr, i);
9146 ant_value_t call_args[3] = { val, tov((double)i), arr };
9147 ant_value_t test = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9148
9149 if (is_err(test)) return test;
9150 if (js_truthy(js, test)) { arr_set(js, result, result_idx, val); result_idx++; }
9151 }
9152
9153 return result;
9154}
9155
9156static ant_value_t builtin_array_reduce(ant_t *js, ant_value_t *args, int nargs) {
9157 ant_value_t arr = js->this_val;
9158
9159 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9160 return js_mkerr(js, "reduce called on non-array");
9161
9162 ant_value_t callback = require_callback(js, args, nargs, "reduce");
9163 if (is_err(callback)) return callback;
9164 bool has_initial = (nargs >= 2);
9165
9166 ant_offset_t len = get_array_length(js, arr);
9167
9168 ant_value_t accumulator = has_initial ? args[1] : js_mkundef();
9169 bool first = !has_initial;
9170
9171 for (ant_offset_t i = 0; i < len; i++) {
9172 if (!arr_has(js, arr, i)) continue;
9173 ant_value_t val = arr_get(js, arr, i);
9174 if (first) { accumulator = val; first = false; continue; }
9175 ant_value_t call_args[4] = { accumulator, val, tov((double)i), arr };
9176 accumulator = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 4, NULL, false);
9177 if (is_err(accumulator)) return accumulator;
9178 }
9179
9180 if (first) return js_mkerr(js, "reduce of empty array with no initial value");
9181 return accumulator;
9182}
9183
9184static inline void flat_helper(ant_t *js, ant_value_t arr, ant_value_t result, ant_offset_t *result_idx, int depth) {
9185 ant_offset_t len = get_array_length(js, arr);
9186 if (len == 0) return;
9187
9188 for (ant_offset_t i = 0; i < len; i++) {
9189 if (!arr_has(js, arr, i)) continue;
9190 ant_value_t val = arr_get(js, arr, i);
9191
9192 if (depth > 0 && vtype(val) == T_ARR) flat_helper(js, val, result, result_idx, depth - 1);
9193 else { arr_set(js, result, *result_idx, val); (*result_idx)++; }
9194 }
9195}
9196
9197static ant_value_t builtin_array_flat(ant_t *js, ant_value_t *args, int nargs) {
9198 ant_value_t arr = js->this_val;
9199 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9200 return js_mkerr(js, "flat called on non-array");
9201 }
9202
9203 int depth = 1;
9204 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
9205 depth = (int) tod(args[0]);
9206 if (depth < 0) depth = 0;
9207 }
9208
9209 ant_value_t result = array_alloc_like(js, arr);
9210 if (is_err(result)) return result;
9211 ant_offset_t result_idx = 0;
9212
9213 flat_helper(js, arr, result, &result_idx, depth);
9214 return result;
9215}
9216
9217static ant_value_t builtin_array_concat(ant_t *js, ant_value_t *args, int nargs) {
9218 ant_value_t arr = js->this_val;
9219 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9220 return js_mkerr(js, "concat called on non-array");
9221 }
9222
9223 ant_value_t result = array_alloc_like(js, arr);
9224 if (is_err(result)) return result;
9225
9226 ant_offset_t result_idx = 0;
9227 for (int a = -1; a < nargs; a++) {
9228 ant_value_t arg = (a < 0) ? arr : args[a];
9229 bool spreadable = false;
9230
9231 if (vtype(arg) == T_ARR || vtype(arg) == T_OBJ) {
9232 bool array_default_spreadable = (vtype(arg) == T_ARR);
9233 if (!array_default_spreadable && is_proxy(arg)) {
9234 ant_value_t target = proxy_read_target(js, arg);
9235 array_default_spreadable = (vtype(target) == T_ARR);
9236 }
9237
9238 ant_value_t spread_val = js_get_sym(js, arg, get_isConcatSpreadable_sym());
9239 if (is_err(spread_val)) return spread_val;
9240 if (vtype(spread_val) == T_UNDEF) spreadable = array_default_spreadable;
9241 else spreadable = js_truthy(js, spread_val);
9242 }
9243
9244 if (spreadable) {
9245 ant_offset_t arg_len = 0;
9246 ant_value_t len_val = js_get(js, arg, "length");
9247 if (is_err(len_val)) return len_val;
9248 if (vtype(len_val) == T_NUM && tod(len_val) > 0) arg_len = (ant_offset_t)tod(len_val);
9249
9250 for (ant_offset_t i = 0; i < arg_len; i++) {
9251 char idxstr[32];
9252 uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i);
9253 ant_value_t elem = js_get(js, arg, idxstr);
9254 if (is_err(elem)) return elem;
9255 arr_set(js, result, result_idx, elem);
9256 result_idx++;
9257 }
9258 } else {
9259 arr_set(js, result, result_idx, arg);
9260 result_idx++;
9261 }
9262 }
9263
9264 return result;
9265}
9266
9267static ant_value_t builtin_array_at(ant_t *js, ant_value_t *args, int nargs) {
9268 ant_value_t arr = js->this_val;
9269 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9270 return js_mkerr(js, "at called on non-array");
9271 }
9272
9273 if (nargs == 0 || vtype(args[0]) != T_NUM) return js_mkundef();
9274
9275 ant_offset_t len = get_array_length(js, arr);
9276
9277 int idx = (int) tod(args[0]);
9278 if (idx < 0) idx = (int)len + idx;
9279 if (idx < 0 || (ant_offset_t)idx >= len) return js_mkundef();
9280
9281 return arr_get(js, arr, (ant_offset_t)idx);
9282}
9283
9284static ant_value_t builtin_array_fill(ant_t *js, ant_value_t *args, int nargs) {
9285 ant_value_t arr = js->this_val;
9286 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9287 return js_mkerr(js, "fill called on non-array");
9288 }
9289
9290 ant_value_t value = nargs >= 1 ? args[0] : js_mkundef();
9291
9292 ant_offset_t len = proxy_aware_length(js, arr);
9293
9294 ant_offset_t start = 0, end = len;
9295 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
9296 int s = (int) tod(args[1]);
9297 if (s < 0) s = (int)len + s;
9298 if (s < 0) s = 0;
9299 start = (ant_offset_t) s;
9300 }
9301 if (nargs >= 3 && vtype(args[2]) == T_NUM) {
9302 int e = (int) tod(args[2]);
9303 if (e < 0) e = (int)len + e;
9304 if (e < 0) e = 0;
9305 end = (ant_offset_t) e;
9306 }
9307 if (start > len) start = len;
9308 if (end > len) end = len;
9309
9310 for (ant_offset_t i = start; i < end; i++) {
9311 arr_set(js, arr, i, value);
9312 }
9313
9314 return arr;
9315}
9316
9317static ant_value_t array_find_impl(ant_t *js, ant_value_t *args, int nargs, bool return_index, const char *name) {
9318 ant_value_t arr = js->this_val;
9319
9320 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9321 return js_mkerr(js, "%s called on non-array", name);
9322
9323 ant_value_t callback = require_callback(js, args, nargs, name);
9324 if (is_err(callback)) return callback;
9325 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9326
9327 ant_offset_t len = get_array_length(js, arr);
9328 if (len == 0) return return_index ? tov(-1) : js_mkundef();
9329
9330 for (ant_offset_t i = 0; i < len; i++) {
9331 ant_value_t val = arr_get(js, arr, i);
9332
9333 ant_value_t call_args[3] = { val, tov((double)i), arr };
9334 ant_value_t result = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9335
9336 if (is_err(result)) return result;
9337 if (js_truthy(js, result)) return return_index ? tov((double)i) : val;
9338 }
9339
9340 return return_index ? tov(-1) : js_mkundef();
9341}
9342
9343static ant_value_t builtin_array_find(ant_t *js, ant_value_t *args, int nargs) {
9344 return array_find_impl(js, args, nargs, false, "find");
9345}
9346
9347static ant_value_t builtin_array_findIndex(ant_t *js, ant_value_t *args, int nargs) {
9348 return array_find_impl(js, args, nargs, true, "findIndex");
9349}
9350
9351static ant_value_t array_find_last_impl(ant_t *js, ant_value_t *args, int nargs, bool return_index, const char *name) {
9352 ant_value_t arr = js->this_val;
9353
9354 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9355 return js_mkerr(js, "%s called on non-array", name);
9356
9357 ant_value_t callback = require_callback(js, args, nargs, name);
9358 if (is_err(callback)) return callback;
9359 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9360
9361 ant_offset_t len = get_array_length(js, arr);
9362 if (len == 0) return return_index ? tov(-1) : js_mkundef();
9363
9364 for (ant_offset_t i = len; i > 0; i--) {
9365 ant_value_t val = arr_get(js, arr, i - 1);
9366
9367 ant_value_t call_args[3] = { val, tov((double)(i - 1)), arr };
9368 ant_value_t result = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9369
9370 if (is_err(result)) return result;
9371 if (js_truthy(js, result)) return return_index ? tov((double)(i - 1)) : val;
9372 }
9373
9374 return return_index ? tov(-1) : js_mkundef();
9375}
9376
9377static ant_value_t builtin_array_findLast(ant_t *js, ant_value_t *args, int nargs) {
9378 return array_find_last_impl(js, args, nargs, false, "findLast");
9379}
9380
9381static ant_value_t builtin_array_findLastIndex(ant_t *js, ant_value_t *args, int nargs) {
9382 return array_find_last_impl(js, args, nargs, true, "findLastIndex");
9383}
9384
9385static ant_value_t builtin_array_flatMap(ant_t *js, ant_value_t *args, int nargs) {
9386 ant_value_t arr = js->this_val;
9387 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9388 return js_mkerr(js, "flatMap called on non-array");
9389 }
9390 if (nargs == 0 || vtype(args[0]) != T_FUNC) {
9391 return js_mkerr(js, "flatMap requires a function argument");
9392 }
9393
9394 ant_value_t callback = args[0];
9395 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9396 ant_offset_t len = get_array_length(js, arr);
9397
9398 ant_value_t result = mkarr(js);
9399 if (is_err(result)) return result;
9400 ant_offset_t result_idx = 0;
9401
9402 for (ant_offset_t i = 0; i < len; i++) {
9403 ant_value_t elem = arr_get(js, arr, i);
9404 ant_value_t call_args[3] = { elem, tov((double)i), arr };
9405 ant_value_t mapped = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9406 if (is_err(mapped)) return mapped;
9407 if (vtype(mapped) == T_ARR) flat_helper(js, mapped, result, &result_idx, 0);
9408 else arr_set(js, result, result_idx++, mapped);
9409 }
9410
9411 return mkval(T_ARR, vdata(result));
9412}
9413
9414static const char *js_tostring(ant_t *js, ant_value_t v) {
9415 if (vtype(v) == T_STR) {
9416 ant_offset_t slen, off = vstr(js, v, &slen);
9417 return (const char *)(uintptr_t)(off);
9418 }
9419 return js_str(js, v);
9420}
9421
9422static int js_compare_values(ant_t *js, ant_value_t a, ant_value_t b, ant_value_t compareFn) {
9423 uint8_t t = vtype(compareFn);
9424 if (t == T_FUNC || t == T_CFUNC) {
9425 ant_value_t call_args[2] = { a, b };
9426 ant_value_t result = sv_vm_call(js->vm, js, compareFn, js_mkundef(), call_args, 2, NULL, false);
9427 if (vtype(result) == T_NUM) return (int)tod(result);
9428 return 0;
9429 }
9430
9431 if (vtype(a) == T_STR && vtype(b) == T_STR) {
9432 ant_offset_t len_a, len_b;
9433 const char *sa = (const char *)(uintptr_t)(vstr(js, a, &len_a));
9434 const char *sb = (const char *)(uintptr_t)(vstr(js, b, &len_b));
9435 return strcmp(sa, sb);
9436 }
9437
9438 const char *sa = js_tostring(js, a);
9439 size_t len = strlen(sa);
9440
9441 char *copy = alloca(len + 1);
9442 memcpy(copy, sa, len + 1);
9443
9444 return strcmp(copy, js_tostring(js, b));
9445}
9446
9447static ant_value_t builtin_array_indexOf(ant_t *js, ant_value_t *args, int nargs) {
9448 ant_value_t arr = js->this_val;
9449 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9450 return js_mkerr(js, "indexOf called on non-array");
9451 }
9452 if (nargs == 0) return tov(-1);
9453
9454 ant_value_t search = args[0];
9455 ant_offset_t len = get_array_length(js, arr);
9456
9457 ant_offset_t start = 0;
9458 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
9459 int s = (int) tod(args[1]);
9460 if (s < 0) s = (int)len + s;
9461 if (s < 0) s = 0;
9462 start = (ant_offset_t) s;
9463 }
9464
9465 for (ant_offset_t i = start; i < len; i++) {
9466 ant_value_t elem = arr_get(js, arr, i);
9467 if (vtype(elem) == T_UNDEF && !arr_has(js, arr, i)) continue;
9468 if (strict_eq_values(js, elem, search)) return tov((double)i);
9469 }
9470 return tov(-1);
9471}
9472
9473static ant_value_t builtin_array_lastIndexOf(ant_t *js, ant_value_t *args, int nargs) {
9474 ant_value_t arr = js->this_val;
9475 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9476 return js_mkerr(js, "lastIndexOf called on non-array");
9477 }
9478 if (nargs == 0) return tov(-1);
9479
9480 ant_value_t search = args[0];
9481 ant_offset_t len = get_array_length(js, arr);
9482
9483 int start = (int)len - 1;
9484 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
9485 start = (int) tod(args[1]);
9486 if (start < 0) start = (int)len + start;
9487 }
9488 if (start >= (int)len) start = (int)len - 1;
9489
9490 for (int i = start; i >= 0; i--) {
9491 ant_value_t elem = arr_get(js, arr, (ant_offset_t)i);
9492 if (vtype(elem) == T_UNDEF && !arr_has(js, arr, (ant_offset_t)i)) continue;
9493 if (strict_eq_values(js, elem, search)) return tov((double)i);
9494 }
9495 return tov(-1);
9496}
9497
9498static ant_value_t builtin_array_reduceRight(ant_t *js, ant_value_t *args, int nargs) {
9499 ant_value_t arr = js->this_val;
9500 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9501 return js_mkerr(js, "reduceRight called on non-array");
9502 }
9503 if (nargs == 0 || vtype(args[0]) != T_FUNC) {
9504 return js_mkerr(js, "reduceRight requires a function argument");
9505 }
9506
9507 ant_value_t callback = args[0];
9508 ant_offset_t len = get_array_length(js, arr);
9509
9510 int start_idx = (int)len - 1;
9511 ant_value_t accumulator;
9512
9513 if (nargs >= 2) {
9514 accumulator = args[1];
9515 } else {
9516 if (len == 0) return js_mkerr(js, "reduceRight of empty array with no initial value");
9517 accumulator = arr_get(js, arr, len - 1);
9518 start_idx = (int)len - 2;
9519 }
9520
9521 for (int i = start_idx; i >= 0; i--) {
9522 ant_value_t elem = arr_get(js, arr, (ant_offset_t)i);
9523 ant_value_t call_args[4] = { accumulator, elem, tov((double)i), arr };
9524 accumulator = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 4, NULL, false);
9525 if (is_err(accumulator)) return accumulator;
9526 }
9527
9528 return accumulator;
9529}
9530
9531static ant_value_t builtin_array_shift(ant_t *js, ant_value_t *args, int nargs) {
9532 (void) args;
9533 (void) nargs;
9534 ant_value_t arr = js->this_val;
9535 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9536 return js_mkerr(js, "shift called on non-array");
9537 }
9538
9539 ant_offset_t len = proxy_aware_length(js, arr);
9540 if (len == 0) return js_mkundef();
9541
9542 ant_offset_t doff = get_dense_buf(arr);
9543 if (doff && !is_proxy(arr)) {
9544 ant_offset_t d_len = dense_iterable_length(js, arr);
9545 if (len != d_len) goto shift_slow;
9546 if (d_len == 0) return js_mkundef();
9547 ant_value_t *d = dense_data(doff);
9548 if (!d) return js_mkundef();
9549 ant_value_t first = dense_get(doff, 0);
9550 if (is_empty_slot(first)) first = js_mkundef();
9551 memmove(&d[0], &d[1], sizeof(ant_value_t) * (size_t)(d_len - 1));
9552 dense_set(js, doff, d_len - 1, T_EMPTY);
9553 array_len_set(js, arr, len - 1);
9554 return first;
9555 }
9556
9557 shift_slow:
9558 ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr;
9559 ant_value_t first = arr_get(js, read_from, 0);
9560
9561 for (ant_offset_t i = 1; i < len; i++) {
9562 if (arr_has(js, read_from, i)) {
9563 ant_value_t elem = arr_get(js, read_from, i);
9564 char dst[16];
9565 size_t dstlen = uint_to_str(dst, sizeof(dst), (unsigned)(i - 1));
9566 js_setprop(js, arr, js_mkstr(js, dst, dstlen), elem);
9567 }
9568 }
9569
9570 if (array_obj_ptr(arr)) array_len_set(js, arr, len - 1);
9571 else js_setprop(js, arr, js->length_str, tov((double)(len - 1)));
9572
9573 return first;
9574}
9575
9576static ant_value_t builtin_array_unshift(ant_t *js, ant_value_t *args, int nargs) {
9577 ant_value_t arr = js->this_val;
9578 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9579 return js_mkerr(js, "unshift called on non-array");
9580 }
9581
9582 ant_offset_t len = proxy_aware_length(js, arr);
9583
9584 ant_offset_t doff = get_dense_buf(arr);
9585 if (doff && !is_proxy(arr)) {
9586 ant_offset_t d_len = dense_iterable_length(js, arr);
9587 if (len != d_len) goto unshift_slow;
9588 ant_offset_t new_len = len + nargs;
9589 ant_offset_t cap = dense_capacity(doff);
9590 if (new_len > cap) {
9591 doff = dense_grow(js, arr, new_len);
9592 if (doff == 0) return js_mkerr(js, "oom");
9593 }
9594 ant_value_t *d = dense_data(doff);
9595 if (!d) return js_mkerr(js, "oom");
9596 memmove(&d[nargs], &d[0], sizeof(ant_value_t) * (size_t)d_len);
9597 for (int i = 0; i < nargs; i++)
9598 dense_set(js, doff, (ant_offset_t)i, args[i]);
9599 array_len_set(js, arr, new_len);
9600 return tov((double) new_len);
9601 }
9602
9603 unshift_slow:
9604 ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr;
9605
9606 for (int i = (int)len - 1; i >= 0; i--) {
9607 if (arr_has(js, read_from, (ant_offset_t)i)) {
9608 ant_value_t elem = arr_get(js, read_from, (ant_offset_t)i);
9609 char dst[16];
9610 size_t dstlen = uint_to_str(dst, sizeof(dst), (unsigned)(i + nargs));
9611 js_setprop(js, arr, js_mkstr(js, dst, dstlen), elem);
9612 }
9613 }
9614
9615 for (int i = 0; i < nargs; i++) {
9616 char idxstr[16];
9617 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i);
9618 ant_value_t key = js_mkstr(js, idxstr, idxlen);
9619 js_setprop(js, arr, key, args[i]);
9620 }
9621
9622 ant_offset_t new_len = len + nargs;
9623 if (array_obj_ptr(arr)) array_len_set(js, arr, new_len);
9624 else js_setprop(js, arr, js->length_str, tov((double) new_len));
9625
9626 return tov((double) new_len);
9627}
9628
9629static ant_value_t builtin_array_some(ant_t *js, ant_value_t *args, int nargs) {
9630 ant_value_t arr = js->this_val;
9631
9632 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9633 return js_mkerr(js, "some called on non-array");
9634
9635 ant_value_t callback = require_callback(js, args, nargs, "some");
9636 if (is_err(callback)) return callback;
9637 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
9638
9639 ant_offset_t len = get_array_length(js, arr);
9640 if (len == 0) return mkval(T_BOOL, 0);
9641
9642 for (ant_offset_t i = 0; i < len; i++) {
9643 if (!arr_has(js, arr, i)) continue;
9644 ant_value_t val = arr_get(js, arr, i);
9645
9646 ant_value_t call_args[3] = { val, tov((double)i), arr };
9647 ant_value_t result = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false);
9648
9649 if (is_err(result)) return result;
9650 if (js_truthy(js, result)) return mkval(T_BOOL, 1);
9651 }
9652
9653 return mkval(T_BOOL, 0);
9654}
9655
9656static ant_value_t builtin_array_sort(ant_t *js, ant_value_t *args, int nargs) {
9657 ant_value_t arr = js->this_val;
9658 ant_value_t compareFn = js_mkundef();
9659
9660 ant_value_t result = arr;
9661 ant_value_t *vals = NULL, *keys = NULL, *temp_vals = NULL, *temp_keys = NULL;
9662 ant_offset_t count = 0, undef_count = 0, len = 0;
9663
9664 gc_temp_root_scope_t temp_scope = {0};
9665 bool temp_scope_active = false;
9666
9667 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
9668 return js_mkerr(js, "sort called on non-array");
9669
9670 if (nargs >= 1) {
9671 uint8_t t = vtype(args[0]);
9672 if (t == T_FUNC || t == T_CFUNC) compareFn = args[0];
9673 else if (t != T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "compareFn must be a function or undefined");
9674 }
9675
9676 gc_temp_root_scope_begin(js, &temp_scope);
9677 temp_scope_active = true;
9678
9679 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_scope, arr))) goto oom;
9680 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_scope, compareFn))) goto oom;
9681
9682 len = get_array_length(js, arr);
9683 if (len == 0) goto done;
9684
9685 ant_offset_t doff = get_dense_buf(arr);
9686 if (doff) {
9687 vals = malloc(len * sizeof(ant_value_t));
9688 if (!vals) goto oom;
9689 for (ant_offset_t i = 0; i < len; i++) {
9690 ant_value_t v = dense_get(doff, i);
9691 if (is_empty_slot(v) || vtype(v) == T_UNDEF) undef_count++;
9692 else vals[count++] = v;
9693 }
9694 } else {
9695 vals = malloc(len * sizeof(ant_value_t));
9696 if (!vals) goto oom;
9697
9698 for (ant_offset_t i = 0; i < len; i++) {
9699 if (!arr_has(js, arr, i)) continue;
9700 ant_value_t v = arr_get(js, arr, i);
9701 if (vtype(v) == T_UNDEF) undef_count++;
9702 else vals[count++] = v;
9703 }
9704 }
9705 if (count <= 1) goto writeback;
9706 for (ant_offset_t i = 0; i < count; i++) {
9707 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_scope, vals[i]))) goto oom;
9708 }
9709
9710 bool use_keys = (vtype(compareFn) == T_UNDEF);
9711 if (use_keys) {
9712 keys = malloc(count * sizeof(ant_value_t));
9713 if (!keys) goto oom;
9714 for (ant_offset_t i = 0; i < count; i++) {
9715 const char *s = js_tostring(js, vals[i]);
9716 ant_value_t key = js_mkstr(js, s, strlen(s));
9717 if (is_err(key)) {
9718 result = key;
9719 goto done;
9720 }
9721 keys[i] = key;
9722 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_scope, key))) goto oom;
9723 }
9724 }
9725
9726 temp_vals = malloc(count * sizeof(ant_value_t));
9727 if (use_keys) temp_keys = malloc(count * sizeof(ant_value_t));
9728 if (!temp_vals || (use_keys && !temp_keys)) goto oom;
9729
9730 for (ant_offset_t width = 1; width < count; width *= 2) {
9731 for (ant_offset_t left = 0; left < count; left += width * 2) {
9732 ant_offset_t mid = left + width;
9733 ant_offset_t right = (mid + width < count) ? mid + width : count;
9734 if (mid >= count) break;
9735
9736 ant_offset_t i = left, j = mid, k = 0;
9737 while (i < mid && j < right) {
9738 int cmp;
9739 if (use_keys) {
9740 ant_offset_t len_a, len_b;
9741 const char *sa = (const char *)(uintptr_t)(vstr(js, keys[i], &len_a));
9742 const char *sb = (const char *)(uintptr_t)(vstr(js, keys[j], &len_b));
9743 cmp = strcmp(sa, sb);
9744 } else {
9745 cmp = js_compare_values(js, vals[i], vals[j], compareFn);
9746 }
9747 if (cmp <= 0) {
9748 temp_vals[k] = vals[i];
9749 if (use_keys) temp_keys[k] = keys[i];
9750 k++; i++;
9751 } else {
9752 temp_vals[k] = vals[j];
9753 if (use_keys) temp_keys[k] = keys[j];
9754 k++; j++;
9755 }
9756 }
9757 while (i < mid) {
9758 temp_vals[k] = vals[i];
9759 if (use_keys) temp_keys[k] = keys[i];
9760 k++; i++;
9761 }
9762 while (j < right) {
9763 temp_vals[k] = vals[j];
9764 if (use_keys) temp_keys[k] = keys[j];
9765 k++; j++;
9766 }
9767
9768 memcpy(&vals[left], temp_vals, k * sizeof(ant_value_t));
9769 if (use_keys) memcpy(&keys[left], temp_keys, k * sizeof(ant_value_t));
9770 }
9771 }
9772
9773writeback:
9774 if (doff) {
9775 for (ant_offset_t i = 0; i < count; i++) dense_set(js, doff, i, vals[i]);
9776 for (ant_offset_t i = count; i < count + undef_count; i++) dense_set(js, doff, i, js_mkundef());
9777 for (ant_offset_t i = count + undef_count; i < len; i++) dense_set(js, doff, i, T_EMPTY);
9778 } else {
9779 ant_offset_t out = 0;
9780 for (; out < count; out++) arr_set(js, arr, out, vals[out]);
9781 for (ant_offset_t i = 0; i < undef_count; i++, out++) arr_set(js, arr, out, js_mkundef());
9782 for (; out < len; out++) arr_del(js, arr, out);
9783 }
9784 result = arr;
9785 goto done;
9786
9787oom:
9788 result = js_mkerr(js, "out of memory");
9789
9790done:
9791 if (temp_scope_active) gc_temp_root_scope_end(&temp_scope);
9792 free(temp_keys);
9793 free(temp_vals);
9794 free(keys);
9795 free(vals);
9796
9797 return result;
9798}
9799
9800static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) {
9801 ant_value_t arr = js->this_val;
9802 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9803 return js_mkerr(js, "splice called on non-array");
9804 }
9805
9806 ant_offset_t len = proxy_aware_length(js, arr);
9807 ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr;
9808
9809 int start = 0;
9810 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
9811 start = (int) tod(args[0]);
9812 if (start < 0) start = (int)len + start;
9813 if (start < 0) start = 0;
9814 if (start > (int)len) start = (int)len;
9815 }
9816
9817 int deleteCount = (int)len - start;
9818 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
9819 deleteCount = (int) tod(args[1]);
9820 if (deleteCount < 0) deleteCount = 0;
9821 if (deleteCount > (int)len - start) deleteCount = (int)len - start;
9822 }
9823
9824 int insertCount = nargs > 2 ? nargs - 2 : 0;
9825
9826 ant_value_t removed = array_alloc_like(js, arr);
9827 if (is_err(removed)) return removed;
9828
9829 ant_offset_t doff = get_dense_buf(arr);
9830 if (doff && !is_proxy(arr)) {
9831 ant_offset_t d_len = dense_iterable_length(js, arr);
9832 if (d_len != len) goto splice_slow;
9833 for (int i = 0; i < deleteCount; i++) {
9834 ant_value_t elem = arr_get(js, arr, (ant_offset_t)(start + i));
9835 arr_set(js, removed, (ant_offset_t)i, elem);
9836 }
9837
9838 int shift = insertCount - deleteCount;
9839 ant_offset_t new_len = (ant_offset_t)((int)d_len + shift);
9840
9841 if (shift != 0) {
9842 if (new_len > dense_capacity(doff)) {
9843 doff = dense_grow(js, arr, new_len);
9844 if (doff == 0) return js_mkerr(js, "oom");
9845 }
9846 ant_offset_t move_start = (ant_offset_t)(start + deleteCount);
9847 ant_offset_t move_dest = (ant_offset_t)(start + insertCount);
9848 ant_offset_t move_count = d_len - move_start;
9849 ant_value_t *d = dense_data(doff);
9850 if (!d) return js_mkerr(js, "oom");
9851 if (move_count > 0) memmove(&d[move_dest], &d[move_start], sizeof(ant_value_t) * (size_t)move_count);
9852 }
9853
9854 for (int i = 0; i < insertCount; i++)
9855 dense_set(js, doff, (ant_offset_t)(start + i), args[2 + i]);
9856
9857 if (shift < 0) {
9858 for (ant_offset_t i = new_len; i < d_len; i++)
9859 dense_set(js, doff, i, T_EMPTY);
9860 }
9861
9862 array_len_set(js, arr, new_len);
9863 return removed;
9864 }
9865
9866 splice_slow:
9867 for (int i = 0; i < deleteCount; i++) {
9868 char src[16], dst[16];
9869 snprintf(src, sizeof(src), "%u", (unsigned)(start + i));
9870 snprintf(dst, sizeof(dst), "%u", (unsigned) i);
9871 ant_offset_t elem_off = lkp(js, read_from, src, strlen(src));
9872 if (elem_off != 0) {
9873 ant_value_t elem = propref_load(js, elem_off);
9874 ant_value_t key = js_mkstr(js, dst, strlen(dst));
9875 js_setprop(js, removed, key, elem);
9876 }
9877 }
9878
9879 js_setprop(js, removed, js->length_str, tov((double) deleteCount));
9880 int shift = insertCount - deleteCount;
9881
9882 if (shift > 0) {
9883 for (int i = (int)len - 1; i >= start + deleteCount; i--) {
9884 char src[16], dst[16];
9885 snprintf(src, sizeof(src), "%u", (unsigned) i);
9886 snprintf(dst, sizeof(dst), "%u", (unsigned)(i + shift));
9887 ant_offset_t elem_off = lkp(js, read_from, src, strlen(src));
9888 ant_value_t elem = elem_off ? propref_load(js, elem_off) : js_mkundef();
9889 ant_value_t key = js_mkstr(js, dst, strlen(dst));
9890 js_setprop(js, arr, key, elem);
9891 }
9892 } else if (shift < 0) {
9893 for (int i = start + deleteCount; i < (int)len; i++) {
9894 char src[16], dst[16];
9895 snprintf(src, sizeof(src), "%u", (unsigned) i);
9896 snprintf(dst, sizeof(dst), "%u", (unsigned)(i + shift));
9897 ant_offset_t elem_off = lkp(js, read_from, src, strlen(src));
9898 ant_value_t elem = elem_off ? propref_load(js, elem_off) : js_mkundef();
9899 ant_value_t key = js_mkstr(js, dst, strlen(dst));
9900 js_setprop(js, arr, key, elem);
9901 }
9902 }
9903
9904 for (int i = 0; i < insertCount; i++) {
9905 char idxstr[16];
9906 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(start + i));
9907 ant_value_t key = js_mkstr(js, idxstr, idxlen);
9908 js_setprop(js, arr, key, args[2 + i]);
9909 }
9910
9911 if (array_obj_ptr(arr)) array_len_set(js, arr, (ant_offset_t)((int)len + shift));
9912 else js_setprop(js, arr, js->length_str, tov((double)((int)len + shift)));
9913
9914 return removed;
9915}
9916
9917static ant_value_t builtin_array_copyWithin(ant_t *js, ant_value_t *args, int nargs) {
9918 ant_value_t arr = js->this_val;
9919 ant_value_t result = arr;
9920 gc_temp_root_scope_t temp_roots = {0};
9921
9922 bool temp_roots_active = false;
9923 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
9924 return js_mkerr(js, "copyWithin called on non-array");
9925 }
9926
9927 ant_offset_t len = proxy_aware_length(js, arr);
9928 ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr;
9929
9930 int target = 0, start = 0, end = (int)len;
9931 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
9932 target = (int) tod(args[0]);
9933 if (target < 0) target = (int)len + target;
9934 if (target < 0) target = 0;
9935 }
9936 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
9937 start = (int) tod(args[1]);
9938 if (start < 0) start = (int)len + start;
9939 if (start < 0) start = 0;
9940 }
9941 if (nargs >= 3 && vtype(args[2]) == T_NUM) {
9942 end = (int) tod(args[2]);
9943 if (end < 0) end = (int)len + end;
9944 if (end < 0) end = 0;
9945 }
9946
9947 if (end > (int)len) end = (int)len;
9948 int count = end - start;
9949 if (count > (int)len - target) count = (int)len - target;
9950 if (count <= 0) return arr;
9951
9952 ant_offset_t doff = get_dense_buf(arr);
9953 if (doff && !is_proxy(arr)) {
9954 if (start < target) {
9955 for (int i = count - 1; i >= 0; i--) {
9956 ant_value_t v = dense_get(doff, (ant_offset_t)(start + i));
9957 dense_set(js, doff, (ant_offset_t)(target + i), is_empty_slot(v) ? js_mkundef() : v);
9958 }
9959 } else {
9960 for (int i = 0; i < count; i++) {
9961 ant_value_t v = dense_get(doff, (ant_offset_t)(start + i));
9962 dense_set(js, doff, (ant_offset_t)(target + i), is_empty_slot(v) ? js_mkundef() : v);
9963 }
9964 }
9965 return arr;
9966 }
9967
9968 ant_value_t *temp = (ant_value_t *)malloc(count * sizeof(ant_value_t));
9969 if (!temp) return js_mkerr(js, "out of memory");
9970 gc_temp_root_scope_begin(js, &temp_roots);
9971 temp_roots_active = true;
9972
9973 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_roots, arr))) goto oom;
9974 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_roots, read_from))) goto oom;
9975
9976 for (int i = 0; i < count; i++) {
9977 char idxstr[16];
9978 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(start + i));
9979 ant_offset_t elem_off = lkp(js, read_from, idxstr, idxlen);
9980 temp[i] = elem_off ? propref_load(js, elem_off) : js_mkundef();
9981 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_roots, temp[i]))) goto oom;
9982 }
9983
9984 for (int i = 0; i < count; i++) {
9985 char idxstr[16];
9986 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(target + i));
9987 ant_value_t key = js_mkstr(js, idxstr, idxlen);
9988 if (is_err(key)) {
9989 result = key;
9990 goto done;
9991 }
9992 if (!gc_temp_root_handle_valid(gc_temp_root_add(&temp_roots, key))) goto oom;
9993 ant_value_t set_result = js_setprop(js, arr, key, temp[i]);
9994 if (is_err(set_result)) {
9995 result = set_result;
9996 goto done;
9997 }
9998 }
9999
10000done:
10001 if (temp_roots_active) gc_temp_root_scope_end(&temp_roots);
10002 free(temp);
10003 return result;
10004
10005oom:
10006 result = js_mkerr(js, "out of memory");
10007 goto done;
10008}
10009
10010static ant_value_t builtin_array_toSorted(ant_t *js, ant_value_t *args, int nargs) {
10011 ant_value_t arr = js->this_val;
10012 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
10013 return js_mkerr(js, "toSorted called on non-array");
10014
10015 ant_value_t result = array_shallow_copy(js, arr, get_array_length(js, arr));
10016 if (is_err(result)) return result;
10017
10018 ant_value_t saved_this = js->this_val;
10019 js->this_val = result;
10020 ant_value_t sorted = builtin_array_sort(js, args, nargs);
10021 js->this_val = saved_this;
10022
10023 if (is_err(sorted)) return sorted;
10024 return mkval(T_ARR, vdata(result));
10025}
10026
10027static ant_value_t builtin_array_toReversed(ant_t *js, ant_value_t *args, int nargs) {
10028 (void)args; (void)nargs;
10029 ant_value_t arr = js->this_val;
10030 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
10031 return js_mkerr(js, "toReversed called on non-array");
10032
10033 ant_value_t result = array_shallow_copy(js, arr, get_array_length(js, arr));
10034 if (is_err(result)) return result;
10035
10036 ant_value_t saved_this = js->this_val;
10037 js->this_val = result;
10038 ant_value_t reversed = builtin_array_reverse(js, NULL, 0);
10039 js->this_val = saved_this;
10040
10041 if (is_err(reversed)) return reversed;
10042 return mkval(T_ARR, vdata(result));
10043}
10044
10045static ant_value_t builtin_array_toSpliced(ant_t *js, ant_value_t *args, int nargs) {
10046 ant_value_t arr = js->this_val;
10047 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ)
10048 return js_mkerr(js, "toSpliced called on non-array");
10049
10050 ant_value_t result = array_shallow_copy(js, arr, get_array_length(js, arr));
10051 if (is_err(result)) return result;
10052
10053 ant_value_t saved_this = js->this_val;
10054 js->this_val = result;
10055 builtin_array_splice(js, args, nargs);
10056 js->this_val = saved_this;
10057
10058 return mkval(T_ARR, vdata(result));
10059}
10060
10061static ant_value_t builtin_array_with(ant_t *js, ant_value_t *args, int nargs) {
10062 ant_value_t arr = js->this_val;
10063 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) {
10064 return js_mkerr(js, "with called on non-array");
10065 }
10066
10067 if (nargs < 2) return js_mkerr(js, "with requires index and value arguments");
10068
10069 ant_offset_t len = get_array_length(js, arr);
10070
10071 int idx = (int) tod(args[0]);
10072 if (idx < 0) idx = (int)len + idx;
10073 if (idx < 0 || (ant_offset_t)idx >= len) return js_mkerr(js, "Invalid index");
10074
10075 ant_value_t result = mkarr(js);
10076 if (is_err(result)) return result;
10077
10078 for (ant_offset_t i = 0; i < len; i++) {
10079 ant_value_t elem = ((ant_offset_t)idx == i) ? args[1] : arr_get(js, arr, i);
10080 arr_set(js, result, i, elem);
10081 }
10082
10083 return mkval(T_ARR, vdata(result));
10084}
10085
10086static ant_value_t builtin_array_keys(ant_t *js, ant_value_t *args, int nargs) {
10087 if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ)
10088 return js_mkerr(js, "keys called on non-array");
10089 return make_array_iterator(js, js->this_val, ARR_ITER_KEYS);
10090}
10091
10092static ant_value_t builtin_array_values(ant_t *js, ant_value_t *args, int nargs) {
10093 if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ)
10094 return js_mkerr(js, "values called on non-array");
10095 return make_array_iterator(js, js->this_val, ARR_ITER_VALUES);
10096}
10097
10098static ant_value_t builtin_array_entries(ant_t *js, ant_value_t *args, int nargs) {
10099 if (vtype(js->this_val) != T_ARR && vtype(js->this_val) != T_OBJ)
10100 return js_mkerr(js, "entries called on non-array");
10101 return make_array_iterator(js, js->this_val, ARR_ITER_ENTRIES);
10102}
10103
10104static ant_value_t builtin_array_toString(ant_t *js, ant_value_t *args, int nargs) {
10105 ant_value_t arr = js->this_val;
10106 ant_value_t join_result;
10107
10108 if (js_try_call_method(js, arr, "join", NULL, 0, &join_result)) {
10109 if (is_err(join_result)) return join_result;
10110 return join_result;
10111 }
10112
10113 return builtin_object_toString(js, args, nargs);
10114}
10115
10116static ant_value_t builtin_array_toLocaleString(ant_t *js, ant_value_t *args, int nargs) {
10117 (void) args;
10118 (void) nargs;
10119 ant_value_t arr = js->this_val;
10120 if (vtype(arr) != T_ARR) return js_mkerr(js, "toLocaleString called on non-array");
10121
10122 ant_offset_t len = get_array_length(js, arr);
10123 if (len == 0) return js_mkstr(js, "", 0);
10124
10125 char *result = NULL;
10126 size_t result_len = 0, result_cap = 256;
10127 result = (char *)ant_calloc(result_cap);
10128 if (!result) return js_mkerr(js, "oom");
10129
10130 for (ant_offset_t i = 0; i < len; i++) {
10131 if (i > 0) {
10132 if (result_len + 1 >= result_cap) {
10133 result_cap *= 2;
10134 char *new_result = (char *)ant_calloc(result_cap);
10135 if (!new_result) { free(result); return js_mkerr(js, "oom"); }
10136 memcpy(new_result, result, result_len);
10137 free(result);
10138 result = new_result;
10139 }
10140 result[result_len++] = ',';
10141 }
10142
10143 if (!arr_has(js, arr, i)) continue;
10144 ant_value_t elem = arr_get(js, arr, i);
10145 if (vtype(elem) == T_NULL || vtype(elem) == T_UNDEF) continue;
10146
10147 char buf[64];
10148 size_t elem_len = tostr(js, elem, buf, sizeof(buf));
10149
10150 if (result_len + elem_len >= result_cap) {
10151 while (result_len + elem_len >= result_cap) result_cap *= 2;
10152 char *new_result = (char *)ant_calloc(result_cap);
10153 if (!new_result) { free(result); return js_mkerr(js, "oom"); }
10154 memcpy(new_result, result, result_len);
10155 free(result);
10156 result = new_result;
10157 }
10158 memcpy(result + result_len, buf, elem_len);
10159 result_len += elem_len;
10160 }
10161
10162 ant_value_t ret = js_mkstr(js, result, result_len);
10163 free(result);
10164 return ret;
10165}
10166
10167static ant_value_t builtin_Array_isArray(ant_params_t) {
10168 if (nargs == 0) return mkval(T_BOOL, 0);
10169 return mkval(T_BOOL, vtype(args[0]) == T_ARR ? 1 : 0);
10170}
10171
10172static ant_value_t builtin_Array_isTemplateObject(ant_params_t) {
10173 if (nargs == 0) return js_false;
10174 return js_bool(vtype(args[0]) == T_ARR && js_check_brand(args[0], BRAND_TEMPLATE_OBJECT));
10175}
10176
10177typedef struct {
10178 ant_value_t write_target;
10179 ant_value_t result;
10180 ant_value_t mapFn;
10181 ant_value_t mapThis;
10182 ant_offset_t index;
10183} array_from_iter_ctx_t;
10184
10185static iter_action_t array_from_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) {
10186 array_from_iter_ctx_t *fctx = (array_from_iter_ctx_t *)ctx;
10187 ant_value_t elem = value;
10188
10189 if (is_callable(fctx->mapFn)) {
10190 ant_value_t call_args[2] = { elem, tov((double)fctx->index) };
10191 elem = sv_vm_call(js->vm, js, fctx->mapFn, fctx->mapThis, call_args, 2, NULL, false);
10192 if (is_err(elem)) { *out = elem; return ITER_ERROR; }
10193 }
10194
10195 if (vtype(fctx->write_target) == T_ARR) arr_set(js, fctx->write_target, fctx->index, elem);
10196 else {
10197 char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)fctx->index);
10198 js_setprop(js, fctx->write_target, js_mkstr(js, idxstr, idxlen), elem);
10199 }
10200
10201 fctx->index++;
10202 return ITER_CONTINUE;
10203}
10204
10205static ant_value_t builtin_Array_from(ant_t *js, ant_value_t *args, int nargs) {
10206 if (nargs == 0) return mkarr(js);
10207
10208 ant_value_t src = args[0];
10209 ant_value_t mapFn = (nargs >= 2 && is_callable(args[1])) ? args[1] : js_mkundef();
10210 ant_value_t mapThis = (nargs >= 3) ? args[2] : js_mkundef();
10211
10212 ant_value_t ctor = js->this_val;
10213 bool use_ctor = (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC);
10214 ant_value_t result = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, 0) : mkarr(js);
10215 if (is_err(result)) return result;
10216
10217 bool result_is_proxy = is_proxy(result);
10218 ant_value_t write_target = result_is_proxy ? proxy_read_target(js, result) : result;
10219 ant_value_t iter_sym = get_iterator_sym();
10220
10221 if (vtype(src) == T_STR) {
10222 if (str_is_heap_rope(src) || str_is_heap_builder(src)) {
10223 src = str_materialize(js, src);
10224 if (is_err(src)) return src;
10225 }
10226 ant_offset_t slen = str_len_fast(js, src);
10227 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 };
10228 for (ant_offset_t i = 0; i < slen; ) {
10229 ant_offset_t off = vstr(js, src, NULL);
10230 utf8proc_int32_t cp;
10231 ant_offset_t cb_len = (ant_offset_t)utf8_next(
10232 (const utf8proc_uint8_t *)(uintptr_t)(off + i),
10233 (utf8proc_ssize_t)(slen - i),
10234 &cp
10235 );
10236 ant_value_t ch = js_mkstr(js, (const void *)(uintptr_t)(off + i), cb_len);
10237
10238 ant_value_t out;
10239 iter_action_t act = array_from_iter_cb(js, ch, &ctx, &out);
10240
10241 if (act == ITER_ERROR) return out;
10242 i += cb_len;
10243 }
10244 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)ctx.index));
10245 } else if (vtype(src) == T_ARR) {
10246 ant_offset_t iter_off = (vtype(iter_sym) == T_SYMBOL) ? lkp_sym_proto(js, src, (ant_offset_t)vdata(iter_sym)) : 0;
10247 bool default_iter = iter_off != 0 && vtype(propref_load(js, iter_off)) == T_CFUNC;
10248
10249 if (default_iter) {
10250 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 };
10251 ant_offset_t len = get_array_length(js, src);
10252 for (ant_offset_t i = 0; i < len; i++) {
10253 ant_value_t unused;
10254 iter_action_t act = array_from_iter_cb(js, arr_get(js, src, i), &ctx, &unused);
10255 if (act == ITER_ERROR) return unused;
10256 }
10257 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)len));
10258 } else {
10259 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 };
10260 ant_value_t iter_result = iter_foreach(js, src, array_from_iter_cb, &ctx);
10261 if (is_err(iter_result)) return iter_result;
10262 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)ctx.index));
10263 }
10264 } else {
10265 ant_offset_t iter_prop = (vtype(iter_sym) == T_SYMBOL) ? lkp_sym_proto(js, src, (ant_offset_t)vdata(iter_sym)) : 0;
10266
10267 if (iter_prop != 0) {
10268 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 };
10269 ant_value_t iter_result = iter_foreach(js, src, array_from_iter_cb, &ctx);
10270 if (is_err(iter_result)) return iter_result;
10271 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)ctx.index));
10272 } else if (vtype(src) == T_OBJ) {
10273 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 };
10274 ant_offset_t len = get_array_length(js, src);
10275 for (ant_offset_t i = 0; i < len; i++) {
10276 ant_value_t unused;
10277 iter_action_t act = array_from_iter_cb(js, arr_get(js, src, i), &ctx, &unused);
10278 if (act == ITER_ERROR) return unused;
10279 }
10280 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)len));
10281 }
10282 }
10283
10284 if (!use_ctor) return mkval(T_ARR, vdata(result));
10285 return result;
10286}
10287
10288static ant_value_t builtin_Array_of(ant_t *js, ant_value_t *args, int nargs) {
10289 ant_value_t ctor = js->this_val;
10290 bool use_ctor = (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC);
10291 ant_value_t arr = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, (ant_offset_t)nargs) : mkarr(js);
10292 if (is_err(arr)) return arr;
10293
10294 bool arr_is_proxy = is_proxy(arr);
10295 ant_value_t write_target = arr_is_proxy ? proxy_read_target(js, arr) : arr;
10296
10297 for (int i = 0; i < nargs; i++) {
10298 if (vtype(write_target) == T_ARR) arr_set(js, write_target, (ant_offset_t)i, args[i]);
10299 else {
10300 char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i);
10301 js_setprop(js, write_target, js_mkstr(js, idxstr, idxlen), args[i]);
10302 }
10303 }
10304
10305 if (vtype(arr) != T_ARR) js_setprop(js, arr, js->length_str, tov((double) nargs));
10306 if (!use_ctor) return mkval(T_ARR, vdata(arr));
10307
10308 return arr;
10309}
10310
10311static ant_value_t builtin_string_indexOf(ant_t *js, ant_value_t *args, int nargs) {
10312 ant_value_t str = to_string_val(js, js->this_val);
10313 if (vtype(str) != T_STR) return js_mkerr(js, "indexOf called on non-string");
10314 if (nargs == 0) return tov(-1);
10315
10316 ant_value_t search = args[0];
10317 if (vtype(search) != T_STR) return tov(-1);
10318
10319 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10320 ant_offset_t search_len, search_off = vstr(js, search, &search_len);
10321
10322 const char *str_ptr = (char *)(uintptr_t)(str_off);
10323 const char *search_ptr = (char *)(uintptr_t)(search_off);
10324 size_t utf16_len = utf16_strlen(str_ptr, str_len);
10325
10326 ant_offset_t start_utf16 = 0;
10327 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
10328 double pos = tod(args[1]);
10329 if (pos < 0) pos = 0;
10330 if (pos > D(utf16_len)) pos = D(utf16_len);
10331 start_utf16 = (ant_offset_t) pos;
10332 }
10333
10334 if (search_len == 0) return tov(D(start_utf16));
10335
10336 size_t byte_start = 0;
10337 if (start_utf16 > 0) {
10338 int off = utf16_index_to_byte_offset(str_ptr, str_len, start_utf16, NULL);
10339 if (off < 0) return tov(-1);
10340 byte_start = (size_t)off;
10341 }
10342
10343 if (byte_start + search_len > (size_t)str_len) return tov(-1);
10344
10345 for (size_t i = byte_start; i <= (size_t)str_len - search_len; i++) {
10346 if (memcmp(str_ptr + i, search_ptr, search_len) == 0)
10347 return tov(D(byte_offset_to_utf16(str_ptr, i)));
10348 }
10349 return tov(-1);
10350}
10351
10352static ant_value_t builtin_string_substring(ant_t *js, ant_value_t *args, int nargs) {
10353 ant_value_t str = to_string_val(js, js->this_val);
10354 if (vtype(str) != T_STR) return js_mkerr(js, "substring called on non-string");
10355 ant_offset_t byte_len, str_off = vstr(js, str, &byte_len);
10356 const char *str_ptr = (char *)(uintptr_t)(str_off);
10357 size_t utf16_len = utf16_strlen(str_ptr, byte_len);
10358 ant_offset_t start = 0, end = (ant_offset_t)utf16_len;
10359 double dstr_len2 = D(utf16_len);
10360
10361 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
10362 double d = tod(args[0]);
10363 start = (ant_offset_t) (d < 0 ? 0 : (d > dstr_len2 ? dstr_len2 : d));
10364 }
10365
10366 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
10367 double d = tod(args[1]);
10368 end = (ant_offset_t) (d < 0 ? 0 : (d > dstr_len2 ? dstr_len2 : d));
10369 }
10370
10371 if (start > end) {
10372 ant_offset_t tmp = start;
10373 start = end;
10374 end = tmp;
10375 }
10376
10377 size_t byte_start, byte_end;
10378 utf16_range_to_byte_range(str_ptr, byte_len, start, end, &byte_start, &byte_end);
10379 return js_mkstr(js, str_ptr + byte_start, byte_end - byte_start);
10380}
10381
10382static ant_value_t builtin_string_substr(ant_t *js, ant_value_t *args, int nargs) {
10383 ant_value_t str = to_string_val(js, js->this_val);
10384 if (vtype(str) != T_STR) return js_mkerr(js, "substr called on non-string");
10385 ant_offset_t byte_len, str_off = vstr(js, str, &byte_len);
10386 const char *str_ptr = (char *)(uintptr_t)(str_off);
10387 size_t utf16_len = utf16_strlen(str_ptr, byte_len);
10388
10389 if (nargs < 1) return js_mkstr(js, str_ptr, byte_len);
10390
10391 double d_start = tod(args[0]);
10392 ant_offset_t start;
10393 if (d_start < 0) {
10394 start = (ant_offset_t)((double)utf16_len + d_start);
10395 if ((int)start < 0) start = 0;
10396 } else {
10397 start = (ant_offset_t)d_start;
10398 }
10399 if (start > (ant_offset_t)utf16_len) start = (ant_offset_t)utf16_len;
10400
10401 ant_offset_t len = (ant_offset_t)utf16_len - start;
10402 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
10403 double d = tod(args[1]);
10404 if (d < 0) d = 0;
10405 len = (ant_offset_t)d;
10406 }
10407 if (start + len > (ant_offset_t)utf16_len) len = (ant_offset_t)utf16_len - start;
10408
10409 size_t byte_start, byte_end;
10410 utf16_range_to_byte_range(str_ptr, byte_len, start, start + len, &byte_start, &byte_end);
10411 return js_mkstr(js, str_ptr + byte_start, byte_end - byte_start);
10412}
10413
10414static ant_value_t string_split_impl(ant_t *js, ant_value_t str, ant_value_t *args, int nargs) {
10415 if (vtype(str) != T_STR) return js_mkerr(js, "split called on non-string");
10416 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10417 const char *str_ptr = (char *)(uintptr_t)(str_off);
10418 ant_value_t arr = mkarr(js);
10419
10420 if (is_err(arr)) return arr;
10421
10422 uint32_t limit = UINT32_MAX;
10423 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
10424 double d = tod(args[1]);
10425 if (d >= 0 && d <= UINT32_MAX) {
10426 limit = (uint32_t)d;
10427 }
10428 }
10429 if (limit == 0) {
10430 return mkval(T_ARR, vdata(arr));
10431 }
10432 if (nargs == 0) goto return_whole;
10433
10434 ant_value_t sep_arg = args[0];
10435 if (vtype(sep_arg) == T_OBJ) {
10436 ant_offset_t source_off = lkp(js, sep_arg, "source", 6);
10437 if (source_off == 0) goto return_whole;
10438 ant_value_t source_val = propref_load(js, source_off);
10439 if (vtype(source_val) != T_STR) goto return_whole;
10440
10441 ant_offset_t plen, poff = vstr(js, source_val, &plen);
10442 const char *pattern_ptr = (char *)(uintptr_t)(poff);
10443
10444 if (plen == 0 || (plen == 4 && memcmp(pattern_ptr, "(?:)", 4) == 0)) {
10445 ant_offset_t idx = 0;
10446 for (ant_offset_t i = 0; i < str_len && idx < limit; i++) {
10447 ant_value_t part = js_mkstr(js, str_ptr + i, 1);
10448 arr_set(js, arr, idx, part);
10449 idx++;
10450 }
10451 return mkval(T_ARR, vdata(arr));
10452 }
10453
10454 char pcre2_pattern[512];
10455 size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, plen, pcre2_pattern, sizeof(pcre2_pattern), false);
10456
10457 uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF;
10458 int errcode;
10459 PCRE2_SIZE erroffset;
10460 pcre2_code *re = pcre2_compile((PCRE2_SPTR)pcre2_pattern, pcre2_len, options, &errcode, &erroffset, NULL);
10461 if (re == NULL) goto return_whole;
10462
10463 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL);
10464 uint32_t capture_count;
10465 pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &capture_count);
10466
10467 if (str_len == 0) {
10468 int rc = pcre2_match(re, (PCRE2_SPTR)str_ptr, 0, 0, 0, match_data, NULL);
10469 if (rc >= 0) {
10470 pcre2_match_data_free(match_data);
10471 pcre2_code_free(re);
10472 return mkval(T_ARR, vdata(arr));
10473 }
10474 }
10475
10476 ant_offset_t idx = 0;
10477 PCRE2_SIZE search_pos = 0;
10478 PCRE2_SIZE segment_start = 0;
10479 PCRE2_SIZE last_match_end = (PCRE2_SIZE)-1;
10480 bool had_any_split = false;
10481
10482 while (idx < limit && search_pos <= str_len) {
10483 int rc = pcre2_match(re, (PCRE2_SPTR)str_ptr, str_len, search_pos, 0, match_data, NULL);
10484 if (rc < 0) break;
10485
10486 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
10487 PCRE2_SIZE match_start = ovector[0];
10488 PCRE2_SIZE match_end = ovector[1];
10489
10490 if (match_start == match_end && match_start == last_match_end) {
10491 search_pos = match_end + 1;
10492 continue;
10493 }
10494
10495 if (match_start == match_end && capture_count > 0) {
10496 bool is_pure_empty_capture = true;
10497 for (uint32_t i = 1; i <= capture_count; i++) {
10498 PCRE2_SIZE cap_start = ovector[2*i];
10499 PCRE2_SIZE cap_end = ovector[2*i+1];
10500 if (cap_start == PCRE2_UNSET || cap_end != cap_start) {
10501 is_pure_empty_capture = false;
10502 break;
10503 }
10504 }
10505 if (is_pure_empty_capture) {
10506 search_pos = match_end + 1;
10507 continue;
10508 }
10509 }
10510
10511 had_any_split = true;
10512
10513 ant_value_t part = js_mkstr(js, str_ptr + segment_start, match_start - segment_start);
10514 arr_set(js, arr, idx, part);
10515 idx++;
10516
10517 for (uint32_t i = 1; i <= capture_count && idx < limit; i++) {
10518 PCRE2_SIZE cap_start = ovector[2*i];
10519 PCRE2_SIZE cap_end = ovector[2*i+1];
10520 if (cap_start == PCRE2_UNSET) {
10521 arr_set(js, arr, idx, js_mkundef());
10522 } else {
10523 part = js_mkstr(js, str_ptr + cap_start, cap_end - cap_start);
10524 arr_set(js, arr, idx, part);
10525 }
10526 idx++;
10527 }
10528
10529 last_match_end = match_end;
10530 segment_start = match_end;
10531 if (match_start == match_end) {
10532 search_pos = match_end + 1;
10533 } else {
10534 search_pos = match_end;
10535 }
10536 }
10537
10538 if (!had_any_split) {
10539 pcre2_match_data_free(match_data);
10540 pcre2_code_free(re);
10541 arr_set(js, arr, 0, js_mkstr(js, str_ptr, str_len));
10542 return mkval(T_ARR, vdata(arr));
10543 }
10544
10545 if (idx < limit) {
10546 ant_value_t part = js_mkstr(js, str_ptr + segment_start, str_len - segment_start);
10547 arr_set(js, arr, idx, part);
10548 idx++;
10549 }
10550
10551 pcre2_match_data_free(match_data);
10552 pcre2_code_free(re);
10553 return mkval(T_ARR, vdata(arr));
10554 }
10555
10556 if (vtype(sep_arg) != T_STR) goto return_whole;
10557
10558 ant_offset_t sep_len, sep_off = vstr(js, sep_arg, &sep_len);
10559 const char *sep_ptr = (char *)(uintptr_t)(sep_off);
10560 ant_offset_t idx = 0, start = 0;
10561
10562 if (sep_len == 0) {
10563 for (ant_offset_t i = 0; i < str_len && idx < limit; i++) {
10564 ant_value_t part = js_mkstr(js, str_ptr + i, 1);
10565 arr_set(js, arr, idx, part);
10566 idx++;
10567 }
10568 return mkval(T_ARR, vdata(arr));
10569 }
10570
10571 for (ant_offset_t i = 0; i + sep_len <= str_len && idx < limit; i++) {
10572 if (memcmp(str_ptr + i, sep_ptr, sep_len) != 0) continue;
10573 ant_value_t part = js_mkstr(js, str_ptr + start, i - start);
10574 arr_set(js, arr, idx, part);
10575 idx++;
10576 start = i + sep_len;
10577 i += sep_len - 1;
10578 }
10579 if (idx < limit && start <= str_len) {
10580 ant_value_t part = js_mkstr(js, str_ptr + start, str_len - start);
10581 arr_set(js, arr, idx, part);
10582 idx++;
10583 }
10584
10585 return mkval(T_ARR, vdata(arr));
10586
10587return_whole:
10588 if (limit > 0) {
10589 arr_set(js, arr, 0, str);
10590 }
10591 return mkval(T_ARR, vdata(arr));
10592}
10593
10594static ant_value_t builtin_string_split(ant_t *js, ant_value_t *args, int nargs) {
10595 ant_value_t str = to_string_val(js, js->this_val);
10596 if (vtype(str) != T_STR) return js_mkerr(js, "split called on non-string");
10597
10598 if (nargs > 0 && is_object_type(args[0])) {
10599 bool called = false;
10600 ant_value_t call_args[2];
10601 int call_nargs = 1;
10602 call_args[0] = str;
10603 if (nargs >= 2) {
10604 call_args[1] = args[1];
10605 call_nargs = 2;
10606 }
10607 ant_value_t dispatched = maybe_call_symbol_method(
10608 js, args[0], get_split_sym(), args[0], call_args, call_nargs, &called
10609 );
10610 if (is_err(dispatched)) return dispatched;
10611 if (called) return dispatched;
10612 }
10613
10614 return string_split_impl(js, str, args, nargs);
10615}
10616
10617static ant_value_t builtin_string_slice(ant_t *js, ant_value_t *args, int nargs) {
10618 ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val);
10619 ant_value_t str = js_tostring_val(js, this_unwrapped);
10620 if (is_err(str)) return str;
10621
10622 ant_offset_t byte_len, str_off = vstr(js, str, &byte_len);
10623 const char *str_ptr = (char *)(uintptr_t)(str_off);
10624 size_t utf16_len = utf16_strlen(str_ptr, byte_len);
10625 ant_offset_t start = 0, end = (ant_offset_t)utf16_len;
10626 double dstr_len = D(utf16_len);
10627
10628 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
10629 double d = tod(args[0]);
10630 if (d < 0) {
10631 start = (ant_offset_t) (d + dstr_len < 0 ? 0 : d + dstr_len);
10632 } else start = (ant_offset_t) (d > dstr_len ? dstr_len : d);
10633 }
10634
10635 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
10636 double d = tod(args[1]);
10637 if (d < 0) {
10638 end = (ant_offset_t) (d + dstr_len < 0 ? 0 : d + dstr_len);
10639 } else end = (ant_offset_t) (d > dstr_len ? dstr_len : d);
10640 }
10641
10642 if (start > end) start = end;
10643 size_t byte_start, byte_end;
10644 utf16_range_to_byte_range(str_ptr, byte_len, start, end, &byte_start, &byte_end);
10645 return js_mkstr(js, str_ptr + byte_start, byte_end - byte_start);
10646}
10647
10648static ant_value_t builtin_string_includes(ant_t *js, ant_value_t *args, int nargs) {
10649 ant_value_t str = to_string_val(js, js->this_val);
10650 if (vtype(str) != T_STR) return js_mkerr(js, "includes called on non-string");
10651 if (nargs == 0) return mkval(T_BOOL, 0);
10652 ant_value_t search = args[0];
10653
10654 if (is_object_type(search)) {
10655 ant_value_t maybe_err = reject_regexp_arg(js, search, "includes");
10656 if (is_err(maybe_err)) return maybe_err;
10657 }
10658 search = js_tostring_val(js, search);
10659 if (is_err(search)) return search;
10660
10661 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10662 ant_offset_t search_len, search_off = vstr(js, search, &search_len);
10663 const char *str_ptr = (char *)(uintptr_t)(str_off);
10664 const char *search_ptr = (char *)(uintptr_t)(search_off);
10665
10666 ant_offset_t start = 0;
10667 if (nargs >= 2) {
10668 double pos = tod(args[1]);
10669 if (isnan(pos) || pos < 0) pos = 0;
10670 if (pos > D(str_len)) return mkval(T_BOOL, 0);
10671 start = (ant_offset_t) pos;
10672 }
10673
10674 if (search_len == 0) return mkval(T_BOOL, 1);
10675 if (start + search_len > str_len) return mkval(T_BOOL, 0);
10676 for (ant_offset_t i = start; i <= str_len - search_len; i++) {
10677 if (memcmp(str_ptr + i, search_ptr, search_len) == 0) return mkval(T_BOOL, 1);
10678 }
10679
10680 return mkval(T_BOOL, 0);
10681}
10682
10683static ant_value_t builtin_string_startsWith(ant_t *js, ant_value_t *args, int nargs) {
10684 ant_value_t str = to_string_val(js, js->this_val);
10685 if (vtype(str) != T_STR) return js_mkerr(js, "startsWith called on non-string");
10686 if (nargs == 0) return mkval(T_BOOL, 0);
10687 ant_value_t search = args[0];
10688
10689 if (is_object_type(search)) {
10690 ant_value_t maybe_err = reject_regexp_arg(js, search, "startsWith");
10691 if (is_err(maybe_err)) return maybe_err;
10692 }
10693 search = js_tostring_val(js, search);
10694 if (is_err(search)) return search;
10695
10696 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10697 ant_offset_t search_len, search_off = vstr(js, search, &search_len);
10698 const char *str_ptr = (char *)(uintptr_t)(str_off);
10699 const char *search_ptr = (char *)(uintptr_t)(search_off);
10700
10701 if (search_len > str_len) return mkval(T_BOOL, 0);
10702 if (search_len == 0) return mkval(T_BOOL, 1);
10703
10704 return mkval(T_BOOL, memcmp(str_ptr, search_ptr, search_len) == 0 ? 1 : 0);
10705}
10706
10707static ant_value_t builtin_string_endsWith(ant_t *js, ant_value_t *args, int nargs) {
10708 ant_value_t str = to_string_val(js, js->this_val);
10709 if (vtype(str) != T_STR) return js_mkerr(js, "endsWith called on non-string");
10710 if (nargs == 0) return mkval(T_BOOL, 0);
10711 ant_value_t search = args[0];
10712
10713 if (is_object_type(search)) {
10714 ant_value_t maybe_err = reject_regexp_arg(js, search, "endsWith");
10715 if (is_err(maybe_err)) return maybe_err;
10716 }
10717 search = js_tostring_val(js, search);
10718 if (is_err(search)) return search;
10719
10720 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10721 ant_offset_t search_len, search_off = vstr(js, search, &search_len);
10722
10723 const char *str_ptr = (char *)(uintptr_t)(str_off);
10724 const char *search_ptr = (char *)(uintptr_t)(search_off);
10725
10726 if (search_len > str_len) return mkval(T_BOOL, 0);
10727 if (search_len == 0) return mkval(T_BOOL, 1);
10728
10729 return mkval(T_BOOL, memcmp(str_ptr + str_len - search_len, search_ptr, search_len) == 0 ? 1 : 0);
10730}
10731
10732static ant_value_t builtin_string_template(ant_t *js, ant_value_t *args, int nargs) {
10733 ant_value_t str = to_string_val(js, js->this_val);
10734 if (vtype(str) != T_STR) return js_mkerr(js, "template called on non-string");
10735 if (nargs < 1 || vtype(args[0]) != T_OBJ) return str;
10736
10737 ant_value_t data = args[0];
10738 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10739 const char *str_ptr = (char *)(uintptr_t)(str_off);
10740
10741 size_t result_cap = str_len + 256;
10742 size_t result_len = 0;
10743 char *result = (char *)ant_calloc(result_cap);
10744 if (!result) return js_mkerr(js, "oom");
10745 ant_offset_t i = 0;
10746
10747#define ENSURE_CAP(need) do { \
10748 if (result_len + (need) >= result_cap) { \
10749 result_cap = (result_len + (need) + 1) * 2; \
10750 char *nr = (char *)ant_realloc(result, result_cap); \
10751 if (!nr) return js_mkerr(js, "oom"); \
10752 result = nr; \
10753 } \
10754} while(0)
10755
10756 while (i < str_len) {
10757 if (i < str_len - 3 && str_ptr[i] == '{' && str_ptr[i + 1] == '{') {
10758 ant_offset_t start = i + 2;
10759 ant_offset_t end = start;
10760 while (end < str_len - 1 && !(str_ptr[end] == '}' && str_ptr[end + 1] == '}')) {
10761 end++;
10762 }
10763 if (end < str_len - 1 && str_ptr[end] == '}' && str_ptr[end + 1] == '}') {
10764 ant_offset_t key_len = end - start;
10765 ant_offset_t prop_off = lkp(js, data, str_ptr + start, key_len);
10766
10767 if (prop_off != 0) {
10768 ant_value_t value = propref_load(js, prop_off);
10769 if (vtype(value) == T_STR) {
10770 ant_offset_t val_len, val_off = vstr(js, value, &val_len);
10771 ENSURE_CAP(val_len);
10772 memcpy(result + result_len, (const void *)(uintptr_t)val_off, val_len);
10773 result_len += val_len;
10774 } else if (vtype(value) == T_NUM) {
10775 char numstr[32];
10776 snprintf(numstr, sizeof(numstr), "%g", tod(value));
10777 size_t num_len = strlen(numstr);
10778 ENSURE_CAP(num_len);
10779 memcpy(result + result_len, numstr, num_len);
10780 result_len += num_len;
10781 } else if (vtype(value) == T_BOOL) {
10782 const char *boolstr = vdata(value) ? "true" : "false";
10783 size_t bool_len = strlen(boolstr);
10784 ENSURE_CAP(bool_len);
10785 memcpy(result + result_len, boolstr, bool_len);
10786 result_len += bool_len;
10787 }
10788 }
10789 i = end + 2;
10790 continue;
10791 }
10792 }
10793 ENSURE_CAP(1);
10794 result[result_len++] = str_ptr[i++];
10795 }
10796 ant_value_t ret = js_mkstr(js, result, result_len);
10797 free(result);
10798 return ret;
10799#undef ENSURE_CAP
10800}
10801
10802static ant_value_t builtin_string_charCodeAt(ant_t *js, ant_value_t *args, int nargs) {
10803 ant_value_t str = to_string_val(js, js->this_val);
10804 if (vtype(str) != T_STR) return js_mkerr(js, "charCodeAt called on non-string");
10805
10806 double idx_d = nargs < 1 ? 0.0 : js_to_number(js, args[0]);
10807 if (isnan(idx_d)) idx_d = 0.0;
10808 if (isinf(idx_d) || idx_d > (double)LONG_MAX) return tov(JS_NAN);
10809
10810 long idx_l = (long) idx_d;
10811 if (idx_l < 0) return tov(JS_NAN);
10812
10813 ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len);
10814 const char *str_data = (const char *)(uintptr_t)(str_off);
10815
10816 uint32_t code_unit = utf16_code_unit_at(str_data, byte_len, idx_l);
10817 if (code_unit == 0xFFFFFFFF) return tov(JS_NAN);
10818
10819 return tov((double) code_unit);
10820}
10821
10822static ant_value_t builtin_string_codePointAt(ant_t *js, ant_value_t *args, int nargs) {
10823 ant_value_t str = to_string_val(js, js->this_val);
10824 if (vtype(str) != T_STR) return js_mkerr(js, "codePointAt called on non-string");
10825
10826 double idx_d = nargs < 1 ? 0.0 : js_to_number(js, args[0]);
10827 if (isnan(idx_d)) idx_d = 0.0;
10828 if (isinf(idx_d) || idx_d > (double)LONG_MAX) return js_mkundef();
10829
10830 long idx_l = (long) idx_d;
10831 if (idx_l < 0) return js_mkundef();
10832
10833 ant_offset_t byte_len;
10834 ant_offset_t str_off = vstr(js, str, &byte_len);
10835 const char *str_data = (const char *)(uintptr_t)(str_off);
10836
10837 uint32_t cp = utf16_codepoint_at(str_data, byte_len, idx_l);
10838 if (cp == 0xFFFFFFFF) return js_mkundef();
10839
10840 return tov((double) cp);
10841}
10842
10843static ant_value_t builtin_string_toLowerCase(ant_t *js, ant_value_t *args, int nargs) {
10844 ant_value_t str = to_string_val(js, js->this_val);
10845 if (vtype(str) != T_STR) return js_mkerr(js, "toLowerCase called on non-string");
10846
10847 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10848 const char *str_ptr = (char *)(uintptr_t)(str_off);
10849 if (str_len == 0) return js_mkstr(js, "", 0);
10850
10851 const utf8proc_uint8_t *src = (const utf8proc_uint8_t *)str_ptr;
10852 utf8proc_ssize_t src_len = (utf8proc_ssize_t)str_len;
10853
10854 ant_offset_t out_len = 0;
10855 utf8proc_ssize_t pos = 0;
10856
10857 while (pos < src_len) {
10858 utf8proc_int32_t cp;
10859 utf8proc_ssize_t n = utf8_next(src + pos, src_len - pos, &cp);
10860 if (cp < 0) { out_len++; pos++; continue; }
10861 utf8proc_uint8_t tmp[4];
10862 out_len += (ant_offset_t)utf8proc_encode_char(utf8proc_tolower(cp), tmp);
10863 pos += n;
10864 }
10865
10866 ant_value_t result = js_mkstr(js, NULL, out_len);
10867 if (is_err(result)) return result;
10868
10869 ant_offset_t result_len, result_off = vstr(js, result, &result_len);
10870 char *result_ptr = (char *)(uintptr_t)(result_off);
10871 uint8_t ascii_state = STR_ASCII_YES;
10872
10873 pos = 0;
10874 ant_offset_t wpos = 0;
10875
10876 while (pos < src_len) {
10877 utf8proc_int32_t cp;
10878 utf8proc_ssize_t n = utf8_next(src + pos, src_len - pos, &cp);
10879
10880 if (cp < 0) {
10881 unsigned char byte = src[pos];
10882 if (byte >= 0x80) ascii_state = STR_ASCII_NO;
10883 result_ptr[wpos++] = (char)byte;
10884 pos++; continue;
10885 }
10886
10887 utf8proc_int32_t mapped = utf8proc_tolower(cp);
10888 if (mapped >= 0x80) ascii_state = STR_ASCII_NO;
10889
10890 wpos += (ant_offset_t)utf8proc_encode_char(mapped, (utf8proc_uint8_t *)(result_ptr + wpos));
10891 pos += n;
10892 }
10893
10894 str_set_ascii_state(result_ptr, ascii_state);
10895 return result;
10896}
10897
10898static ant_value_t builtin_string_toUpperCase(ant_t *js, ant_value_t *args, int nargs) {
10899 ant_value_t str = to_string_val(js, js->this_val);
10900 if (vtype(str) != T_STR) return js_mkerr(js, "toUpperCase called on non-string");
10901
10902 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10903 const char *str_ptr = (char *)(uintptr_t)(str_off);
10904 if (str_len == 0) return js_mkstr(js, "", 0);
10905
10906 const utf8proc_uint8_t *src = (const utf8proc_uint8_t *)str_ptr;
10907 utf8proc_ssize_t src_len = (utf8proc_ssize_t)str_len;
10908
10909 ant_offset_t out_len = 0;
10910 utf8proc_ssize_t pos = 0;
10911
10912 while (pos < src_len) {
10913 utf8proc_int32_t cp;
10914 utf8proc_ssize_t n = utf8_next(src + pos, src_len - pos, &cp);
10915 if (cp < 0) { out_len++; pos++; continue; }
10916 utf8proc_uint8_t tmp[4];
10917 out_len += (ant_offset_t)utf8proc_encode_char(utf8proc_toupper(cp), tmp);
10918 pos += n;
10919 }
10920
10921 ant_value_t result = js_mkstr(js, NULL, out_len);
10922 if (is_err(result)) return result;
10923
10924 ant_offset_t result_len, result_off = vstr(js, result, &result_len);
10925 char *result_ptr = (char *)(uintptr_t)(result_off);
10926 uint8_t ascii_state = STR_ASCII_YES;
10927
10928 pos = 0;
10929 ant_offset_t wpos = 0;
10930
10931 while (pos < src_len) {
10932 utf8proc_int32_t cp;
10933 utf8proc_ssize_t n = utf8_next(src + pos, src_len - pos, &cp);
10934
10935 if (cp < 0) {
10936 unsigned char byte = src[pos];
10937 if (byte >= 0x80) ascii_state = STR_ASCII_NO;
10938 result_ptr[wpos++] = (char)byte;
10939 pos++; continue;
10940 }
10941
10942 utf8proc_int32_t mapped = utf8proc_toupper(cp);
10943 if (mapped >= 0x80) ascii_state = STR_ASCII_NO;
10944
10945 wpos += (ant_offset_t)utf8proc_encode_char(mapped, (utf8proc_uint8_t *)(result_ptr + wpos));
10946 pos += n;
10947 }
10948
10949 str_set_ascii_state(result_ptr, ascii_state);
10950 return result;
10951}
10952
10953static ant_value_t builtin_string_trim(ant_t *js, ant_value_t *args, int nargs) {
10954 ant_value_t str = to_string_val(js, js->this_val);
10955 if (vtype(str) != T_STR) return js_mkerr(js, "trim called on non-string");
10956
10957 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10958 const char *str_ptr = (char *)(uintptr_t)(str_off);
10959
10960 ant_offset_t start = 0, end = str_len;
10961 while (start < end && is_space(str_ptr[start])) start++;
10962 while (end > start && is_space(str_ptr[end - 1])) end--;
10963
10964 return js_mkstr(js, str_ptr + start, end - start);
10965}
10966
10967static ant_value_t builtin_string_trimStart(ant_t *js, ant_value_t *args, int nargs) {
10968 ant_value_t str = to_string_val(js, js->this_val);
10969 if (vtype(str) != T_STR) return js_mkerr(js, "trimStart called on non-string");
10970
10971 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10972 const char *str_ptr = (char *)(uintptr_t)(str_off);
10973
10974 ant_offset_t start = 0;
10975 while (start < str_len && is_space(str_ptr[start])) start++;
10976
10977 return js_mkstr(js, str_ptr + start, str_len - start);
10978}
10979
10980static ant_value_t builtin_string_trimEnd(ant_t *js, ant_value_t *args, int nargs) {
10981 ant_value_t str = to_string_val(js, js->this_val);
10982 if (vtype(str) != T_STR) return js_mkerr(js, "trimEnd called on non-string");
10983
10984 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
10985 const char *str_ptr = (char *)(uintptr_t)(str_off);
10986
10987 ant_offset_t end = str_len;
10988 while (end > 0 && is_space(str_ptr[end - 1])) end--;
10989
10990 return js_mkstr(js, str_ptr, end);
10991}
10992
10993static ant_value_t builtin_string_repeat(ant_t *js, ant_value_t *args, int nargs) {
10994 ant_value_t str = to_string_val(js, js->this_val);
10995 if (vtype(str) != T_STR) return js_mkerr(js, "repeat called on non-string");
10996 if (nargs < 1 || vtype(args[0]) != T_NUM) return js_mkerr(js, "repeat count required");
10997
10998 double count_d = tod(args[0]);
10999 if (count_d < 0 || count_d != (double)(long)count_d) return js_mkerr(js, "invalid repeat count");
11000 ant_offset_t count = (ant_offset_t) count_d;
11001
11002 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11003 const char *str_ptr = (char *)(uintptr_t)(str_off);
11004
11005 if (count == 0 || str_len == 0) return js_mkstr(js, "", 0);
11006
11007 ant_value_t result = js_mkstr(js, NULL, str_len * count);
11008 if (is_err(result)) return result;
11009
11010 ant_offset_t result_len, result_off = vstr(js, result, &result_len);
11011 char *result_ptr = (char *)(uintptr_t)(result_off);
11012
11013 for (ant_offset_t i = 0; i < count; i++) {
11014 memcpy(result_ptr + i * str_len, str_ptr, str_len);
11015 }
11016 str_set_ascii_state(
11017 result_ptr,
11018 str_is_ascii(str_ptr) ? STR_ASCII_YES : STR_ASCII_NO
11019 );
11020
11021 return result;
11022}
11023
11024static ant_value_t builtin_string_padStart(ant_t *js, ant_value_t *args, int nargs) {
11025 ant_value_t str = to_string_val(js, js->this_val);
11026 if (vtype(str) != T_STR) return js_mkerr(js, "padStart called on non-string");
11027 if (nargs < 1 || vtype(args[0]) != T_NUM) return str;
11028
11029 ant_offset_t target_len = (ant_offset_t)tod(args[0]);
11030 if (target_len <= 0) return str;
11031 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11032 const char *str_ptr = (char *)(uintptr_t)(str_off);
11033 size_t str_utf16_len = utf16_strlen(str_ptr, (size_t)str_len);
11034
11035 if ((size_t)target_len <= str_utf16_len) return str;
11036
11037 ant_value_t pad_val = js_mkstr(js, " ", 1);
11038 if (nargs >= 2 && vtype(args[1]) != T_UNDEF) {
11039 pad_val = coerce_to_str(js, args[1]);
11040 if (is_err(pad_val)) return pad_val;
11041 }
11042 ant_offset_t pad_len, pad_off = vstr(js, pad_val, &pad_len);
11043 const char *pad_str = (char *)(uintptr_t)(pad_off);
11044 size_t pad_utf16_len = utf16_strlen(pad_str, (size_t)pad_len);
11045
11046 if (pad_utf16_len == 0) return str;
11047
11048 size_t fill_utf16_len = (size_t)target_len - str_utf16_len;
11049 size_t full_repeats = fill_utf16_len / pad_utf16_len;
11050 size_t rem_utf16 = fill_utf16_len % pad_utf16_len;
11051 size_t rem_bytes = 0;
11052 if (rem_utf16 > 0) {
11053 int off = utf16_index_to_byte_offset(pad_str, (size_t)pad_len, rem_utf16, NULL);
11054 if (off < 0) return str;
11055 rem_bytes = (size_t)off;
11056 }
11057 size_t fill_bytes = full_repeats * (size_t)pad_len + rem_bytes;
11058 size_t total_bytes = fill_bytes + (size_t)str_len;
11059
11060 ant_value_t result = js_mkstr(js, NULL, (ant_offset_t)total_bytes);
11061 if (is_err(result)) return result;
11062
11063 ant_offset_t result_len, result_off = vstr(js, result, &result_len);
11064 char *result_ptr = (char *)(uintptr_t)(result_off);
11065
11066 size_t pos = 0;
11067 for (size_t i = 0; i < full_repeats; i++) {
11068 memcpy(result_ptr + pos, pad_str, (size_t)pad_len);
11069 pos += (size_t)pad_len;
11070 }
11071 if (rem_bytes > 0) {
11072 memcpy(result_ptr + pos, pad_str, rem_bytes);
11073 pos += rem_bytes;
11074 }
11075 memcpy(result_ptr + pos, str_ptr, (size_t)str_len);
11076 str_set_ascii_state(
11077 result_ptr,
11078 (str_is_ascii(pad_str) && str_is_ascii(str_ptr)) ? STR_ASCII_YES : STR_ASCII_NO
11079 );
11080
11081 return result;
11082}
11083
11084static ant_value_t builtin_string_padEnd(ant_t *js, ant_value_t *args, int nargs) {
11085 ant_value_t str = to_string_val(js, js->this_val);
11086 if (vtype(str) != T_STR) return js_mkerr(js, "padEnd called on non-string");
11087 if (nargs < 1 || vtype(args[0]) != T_NUM) return str;
11088
11089 ant_offset_t target_len = (ant_offset_t)tod(args[0]);
11090 if (target_len <= 0) return str;
11091 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11092 const char *str_ptr = (char *)(uintptr_t)(str_off);
11093 size_t str_utf16_len = utf16_strlen(str_ptr, (size_t)str_len);
11094
11095 if ((size_t)target_len <= str_utf16_len) return str;
11096
11097 ant_value_t pad_val = js_mkstr(js, " ", 1);
11098 if (nargs >= 2 && vtype(args[1]) != T_UNDEF) {
11099 pad_val = coerce_to_str(js, args[1]);
11100 if (is_err(pad_val)) return pad_val;
11101 }
11102 ant_offset_t pad_len, pad_off = vstr(js, pad_val, &pad_len);
11103 const char *pad_str = (char *)(uintptr_t)(pad_off);
11104 size_t pad_utf16_len = utf16_strlen(pad_str, (size_t)pad_len);
11105
11106 if (pad_utf16_len == 0) return str;
11107
11108 size_t fill_utf16_len = (size_t)target_len - str_utf16_len;
11109 size_t full_repeats = fill_utf16_len / pad_utf16_len;
11110 size_t rem_utf16 = fill_utf16_len % pad_utf16_len;
11111 size_t rem_bytes = 0;
11112 if (rem_utf16 > 0) {
11113 int off = utf16_index_to_byte_offset(pad_str, (size_t)pad_len, rem_utf16, NULL);
11114 if (off < 0) return str;
11115 rem_bytes = (size_t)off;
11116 }
11117 size_t fill_bytes = full_repeats * (size_t)pad_len + rem_bytes;
11118 size_t total_bytes = (size_t)str_len + fill_bytes;
11119
11120 ant_value_t result = js_mkstr(js, NULL, (ant_offset_t)total_bytes);
11121 if (is_err(result)) return result;
11122
11123 ant_offset_t result_len, result_off = vstr(js, result, &result_len);
11124 char *result_ptr = (char *)(uintptr_t)(result_off);
11125
11126 memcpy(result_ptr, str_ptr, (size_t)str_len);
11127 size_t pos = (size_t)str_len;
11128 for (size_t i = 0; i < full_repeats; i++) {
11129 memcpy(result_ptr + pos, pad_str, (size_t)pad_len);
11130 pos += (size_t)pad_len;
11131 }
11132 if (rem_bytes > 0) {
11133 memcpy(result_ptr + pos, pad_str, rem_bytes);
11134 pos += rem_bytes;
11135 }
11136 str_set_ascii_state(
11137 result_ptr,
11138 (str_is_ascii(str_ptr) && str_is_ascii(pad_str)) ? STR_ASCII_YES : STR_ASCII_NO
11139 );
11140
11141 return result;
11142}
11143
11144static ant_value_t builtin_string_charAt(ant_t *js, ant_value_t *args, int nargs) {
11145 ant_value_t str = to_string_val(js, js->this_val);
11146 if (vtype(str) != T_STR) return js_mkerr(js, "charAt called on non-string");
11147
11148 double idx_d = nargs < 1 ? 0.0 : js_to_number(js, args[0]);
11149 if (isnan(idx_d)) idx_d = 0;
11150 else if (idx_d < 0) idx_d = -floor(-idx_d);
11151 else idx_d = floor(idx_d);
11152 if (idx_d < 0 || isinf(idx_d)) return js_mkstr(js, "", 0);
11153
11154 ant_offset_t idx = (ant_offset_t) idx_d;
11155 ant_offset_t byte_len;
11156 ant_offset_t str_off = vstr(js, str, &byte_len);
11157
11158 const char *str_data = (const char *)(uintptr_t)(str_off);
11159 uint32_t code_unit = utf16_code_unit_at(str_data, byte_len, idx);
11160 if (code_unit == 0xFFFFFFFF) return js_mkstr(js, "", 0);
11161
11162 return js_string_from_utf16_code_unit(js, code_unit);
11163}
11164
11165static ant_value_t builtin_string_at(ant_t *js, ant_value_t *args, int nargs) {
11166 ant_value_t str = to_string_val(js, js->this_val);
11167 if (vtype(str) != T_STR) return js_mkerr(js, "at called on non-string");
11168
11169 double idx_d = nargs < 1 ? 0.0 : js_to_number(js, args[0]);
11170 if (isnan(idx_d) || isinf(idx_d)) return js_mkundef();
11171
11172 ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len);
11173 const char *str_data = (const char *)(uintptr_t)(str_off);
11174 size_t utf16_len = utf16_strlen(str_data, byte_len);
11175
11176 long idx = (long) idx_d;
11177 if (idx < 0) idx += (long) utf16_len;
11178 if (idx < 0 || idx >= (long) utf16_len) return js_mkundef();
11179
11180 uint32_t code_unit = utf16_code_unit_at(str_data, byte_len, idx);
11181 if (code_unit == 0xFFFFFFFF) return js_mkundef();
11182
11183 return js_string_from_utf16_code_unit(js, code_unit);
11184}
11185
11186static ant_value_t builtin_string_localeCompare(ant_t *js, ant_value_t *args, int nargs) {
11187 ant_value_t str = to_string_val(js, js->this_val);
11188 if (vtype(str) != T_STR) return js_mkerr(js, "localeCompare called on non-string");
11189 if (nargs < 1) return tov(0);
11190
11191 ant_value_t that = args[0];
11192 if (vtype(that) != T_STR) {
11193 char buf[64];
11194 size_t n = tostr(js, that, buf, sizeof(buf));
11195 that = js_mkstr(js, buf, n);
11196 }
11197
11198 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11199 ant_offset_t that_len, that_off = vstr(js, that, &that_len);
11200 const char *str_ptr = (char *)(uintptr_t)(str_off);
11201 const char *that_ptr = (char *)(uintptr_t)(that_off);
11202
11203 int result = strcoll(str_ptr, that_ptr);
11204 if (result < 0) return tov(-1.0);
11205 if (result > 0) return tov(1.0);
11206
11207 return tov(0);
11208}
11209
11210static ant_value_t builtin_string_lastIndexOf(ant_t *js, ant_value_t *args, int nargs) {
11211 ant_value_t str = to_string_val(js, js->this_val);
11212 if (vtype(str) != T_STR) return js_mkerr(js, "lastIndexOf called on non-string");
11213 if (nargs == 0) return tov(-1);
11214
11215 ant_value_t search = args[0];
11216 if (vtype(search) != T_STR) return tov(-1);
11217
11218 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11219 ant_offset_t search_len, search_off = vstr(js, search, &search_len);
11220
11221 ant_offset_t max_start = str_len;
11222 double dstr_len = D(str_len);
11223 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
11224 double pos = tod(args[1]);
11225 if (isnan(pos)) pos = dstr_len;
11226 if (pos < 0) pos = 0;
11227 if (pos > dstr_len) pos = dstr_len;
11228 max_start = (ant_offset_t) pos;
11229 }
11230
11231 if (search_len == 0) return tov((double) (max_start > str_len ? str_len : max_start));
11232 if (search_len > str_len) return tov(-1);
11233
11234 const char *str_ptr = (char *)(uintptr_t)(str_off);
11235 const char *search_ptr = (char *)(uintptr_t)(search_off);
11236
11237 ant_offset_t start = (max_start + search_len > str_len) ? str_len - search_len : max_start;
11238 for (ant_offset_t i = start + 1; i > 0; i--) {
11239 if (memcmp(str_ptr + i - 1, search_ptr, search_len) == 0) return tov((double)(i - 1));
11240 }
11241 return tov(-1);
11242}
11243
11244static ant_value_t builtin_string_concat(ant_t *js, ant_value_t *args, int nargs) {
11245 ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val);
11246 ant_value_t str = js_tostring_val(js, this_unwrapped);
11247 if (is_err(str)) return str;
11248
11249 ant_offset_t total_len;
11250 ant_offset_t base_off = vstr(js, str, &total_len);
11251
11252 ant_value_t *str_args = NULL;
11253 if (nargs > 0) {
11254 str_args = (ant_value_t *)ant_calloc(nargs * sizeof(ant_value_t));
11255 if (!str_args) return js_mkerr(js, "oom");
11256 for (int i = 0; i < nargs; i++) {
11257 str_args[i] = js_tostring_val(js, args[i]);
11258 if (is_err(str_args[i])) {
11259 free(str_args);
11260 return str_args[i];
11261 }
11262 ant_offset_t arg_len;
11263 vstr(js, str_args[i], &arg_len);
11264 total_len += arg_len;
11265 }
11266 }
11267
11268 char *result = (char *)ant_calloc(total_len + 1);
11269 if (!result) {
11270 if (str_args) free(str_args);
11271 return js_mkerr(js, "oom");
11272 }
11273
11274 ant_offset_t base_len;
11275 base_off = vstr(js, str, &base_len);
11276 memcpy(result, (const void *)(uintptr_t)base_off, base_len);
11277 ant_offset_t pos = base_len;
11278
11279 for (int i = 0; i < nargs; i++) {
11280 ant_offset_t arg_len, arg_off = vstr(js, str_args[i], &arg_len);
11281 memcpy(result + pos, (const void *)(uintptr_t)arg_off, arg_len);
11282 pos += arg_len;
11283 }
11284 result[pos] = '\0';
11285
11286 ant_value_t ret = js_mkstr(js, result, pos);
11287 free(result); if (str_args) free(str_args);
11288
11289 return ret;
11290}
11291
11292static ant_value_t builtin_string_normalize(ant_t *js, ant_value_t *args, int nargs) {
11293 ant_value_t str = to_string_val(js, js->this_val);
11294 if (vtype(str) != T_STR) return js_mkerr(js, "normalize called on non-string");
11295
11296 ant_offset_t str_len, str_off = vstr(js, str, &str_len);
11297 const char *str_ptr = (const char *)(uintptr_t)(str_off);
11298
11299 if (str_len == 0) return js_mkstr(js, "", 0);
11300 utf8proc_option_t opts = UTF8PROC_COMPOSE | UTF8PROC_STABLE;
11301
11302 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) {
11303 ant_value_t form_val = js_tostring_val(js, args[0]);
11304 if (is_err(form_val)) return form_val;
11305 ant_offset_t flen, foff = vstr(js, form_val, &flen);
11306 const char *form = (const char *)(uintptr_t)(foff);
11307
11308 if (flen == 3 && memcmp(form, "NFC", 3) == 0) {
11309 opts = UTF8PROC_COMPOSE | UTF8PROC_STABLE;
11310 } else if (flen == 3 && memcmp(form, "NFD", 3) == 0) {
11311 opts = UTF8PROC_DECOMPOSE | UTF8PROC_STABLE;
11312 } else if (flen == 4 && memcmp(form, "NFKC", 4) == 0) {
11313 opts = UTF8PROC_COMPOSE | UTF8PROC_STABLE | UTF8PROC_COMPAT;
11314 } else if (flen == 4 && memcmp(form, "NFKD", 4) == 0) {
11315 opts = UTF8PROC_DECOMPOSE | UTF8PROC_STABLE | UTF8PROC_COMPAT;
11316 } else return js_mkerr_typed(js, JS_ERR_RANGE, "The normalization form should be one of NFC, NFD, NFKC, NFKD");
11317 }
11318
11319 utf8proc_uint8_t *result = NULL;
11320 utf8proc_ssize_t rlen = utf8proc_map(
11321 (const utf8proc_uint8_t *)str_ptr, (utf8proc_ssize_t)str_len, &result, opts
11322 );
11323
11324 if (rlen < 0 || !result) {
11325 if (result) free(result);
11326 return js_mkstr(js, str_ptr, str_len);
11327 }
11328
11329 ant_value_t ret = js_mkstr(js, (const char *)result, (ant_offset_t)rlen);
11330 free(result);
11331
11332 return ret;
11333}
11334
11335static ant_value_t builtin_string_fromCharCode(ant_t *js, ant_value_t *args, int nargs) {
11336 if (nargs == 0) return js_mkstr(js, "", 0);
11337
11338 char *buf = (char *)ant_calloc(nargs + 1);
11339 if (!buf) return js_mkerr(js, "oom");
11340
11341 for (int i = 0; i < nargs; i++) {
11342 if (vtype(args[i]) != T_NUM) { buf[i] = 0; continue; }
11343 int code = (int) tod(args[i]);
11344 buf[i] = (char)(code & 0xFF);
11345 }
11346 buf[nargs] = '\0';
11347
11348 ant_value_t ret = js_mkstr(js, buf, nargs);
11349 free(buf);
11350 return ret;
11351}
11352
11353static ant_value_t builtin_string_fromCodePoint(ant_t *js, ant_value_t *args, int nargs) {
11354 if (nargs == 0) return js_mkstr(js, "", 0);
11355
11356 char *buf = (char *)ant_calloc(nargs * 4 + 1);
11357 if (!buf) return js_mkerr(js, "oom");
11358
11359 size_t len = 0;
11360 for (int i = 0; i < nargs; i++) {
11361 if (vtype(args[i]) != T_NUM) continue;
11362 double d = tod(args[i]);
11363 if (d < 0 || d > 0x10FFFF || d != floor(d)) {
11364 free(buf);
11365 return js_mkerr_typed(js, JS_ERR_RANGE, "Invalid code point");
11366 }
11367 uint32_t cp = (uint32_t)d;
11368 if (cp < 0x80) {
11369 buf[len++] = (char)cp;
11370 } else if (cp < 0x800) {
11371 buf[len++] = (char)(0xC0 | (cp >> 6));
11372 buf[len++] = (char)(0x80 | (cp & 0x3F));
11373 } else if (cp < 0x10000) {
11374 buf[len++] = (char)(0xE0 | (cp >> 12));
11375 buf[len++] = (char)(0x80 | ((cp >> 6) & 0x3F));
11376 buf[len++] = (char)(0x80 | (cp & 0x3F));
11377 } else {
11378 buf[len++] = (char)(0xF0 | (cp >> 18));
11379 buf[len++] = (char)(0x80 | ((cp >> 12) & 0x3F));
11380 buf[len++] = (char)(0x80 | ((cp >> 6) & 0x3F));
11381 buf[len++] = (char)(0x80 | (cp & 0x3F));
11382 }
11383 }
11384 buf[len] = '\0';
11385
11386 ant_value_t ret = js_mkstr(js, buf, len);
11387 free(buf);
11388 return ret;
11389}
11390
11391static bool string_builder_append_value(
11392 ant_t *js, char **buf,
11393 size_t *len, size_t *cap,
11394 ant_value_t value, ant_value_t *err
11395) {
11396 ant_value_t s = js_tostring_val(js, value);
11397 if (is_err(s)) {
11398 if (err) *err = s;
11399 return false;
11400 }
11401
11402 ant_offset_t slen = 0;
11403 ant_offset_t soff = vstr(js, s, &slen);
11404
11405 size_t need = *len + (size_t)slen + 1;
11406 if (need > *cap) {
11407 size_t next = (*cap == 0) ? 64 : *cap;
11408 while (next < need) next *= 2;
11409 char *grown = (char *)realloc(*buf, next);
11410 if (!grown) {
11411 if (err) *err = js_mkerr(js, "oom");
11412 return false;
11413 }
11414 *buf = grown;
11415 *cap = next;
11416 }
11417
11418 if (slen > 0) memcpy(*buf + *len, (const void *)(uintptr_t)soff, (size_t)slen);
11419 *len += (size_t)slen;
11420 (*buf)[*len] = '\0';
11421 return true;
11422}
11423
11424static ant_value_t builtin_string_raw(ant_t *js, ant_value_t *args, int nargs) {
11425 if (nargs < 1 || is_null(args[0]) || is_undefined(args[0])) {
11426 return js_mkerr_typed(js, JS_ERR_TYPE, "String.raw requires a template object");
11427 }
11428
11429 ant_value_t tmpl = args[0];
11430 if (!is_object_type(tmpl)) {
11431 return js_mkerr_typed(js, JS_ERR_TYPE, "String.raw requires a template object");
11432 }
11433
11434 ant_value_t raw = js_get(js, tmpl, "raw");
11435 if (is_null(raw) || is_undefined(raw) || !is_object_type(raw)) {
11436 return js_mkerr_typed(js, JS_ERR_TYPE, "String.raw requires template.raw");
11437 }
11438
11439 ant_value_t raw_len_val = js_get(js, raw, "length");
11440 double raw_len_num = js_to_number(js, raw_len_val);
11441 if (!isfinite(raw_len_num) || raw_len_num <= 0) return js_mkstr(js, "", 0);
11442
11443 size_t literal_count = (size_t)raw_len_num;
11444 if (literal_count == 0) return js_mkstr(js, "", 0);
11445
11446 char *buf = NULL;
11447 size_t len = 0; size_t cap = 0;
11448 ant_value_t err = js_mkundef();
11449
11450 for (size_t i = 0; i < literal_count; i++) {
11451 ant_value_t chunk = js_mkundef();
11452 if (vtype(raw) == T_ARR) chunk = js_arr_get(js, raw, (ant_offset_t)i);
11453 else {
11454 char key[32];
11455 snprintf(key, sizeof(key), "%zu", i);
11456 chunk = js_get(js, raw, key);
11457 }
11458
11459 if (!string_builder_append_value(js, &buf, &len, &cap, chunk, &err)) {
11460 free(buf);
11461 return is_err(err) ? err : js_mkerr(js, "oom");
11462 }
11463
11464 if (i + 1 < literal_count && (int)(i + 1) < nargs) {
11465 if (!string_builder_append_value(js, &buf, &len, &cap, args[i + 1], &err)) {
11466 free(buf); return is_err(err) ? err : js_mkerr(js, "oom");
11467 }
11468 }
11469 }
11470
11471 ant_value_t out = js_mkstr(js, buf ? buf : "", len);
11472 free(buf);
11473
11474 return out;
11475}
11476
11477static ant_value_t builtin_number_toString(ant_t *js, ant_value_t *args, int nargs) {
11478 ant_value_t num = unwrap_primitive(js, js->this_val);
11479 if (vtype(num) != T_NUM) return js_mkerr(js, "toString called on non-number");
11480
11481 int radix = 10;
11482 if (nargs >= 1 && vtype(args[0]) == T_NUM) {
11483 radix = (int)tod(args[0]);
11484 if (radix < 2 || radix > 36) {
11485 return js_mkerr(js, "radix must be between 2 and 36");
11486 }
11487 }
11488
11489 if (radix == 10) {
11490 char buf[64];
11491 size_t len = strnum(num, buf, sizeof(buf));
11492 return js_mkstr(js, buf, len);
11493 }
11494
11495 double val = tod(num);
11496
11497 if (isnan(val)) return js_mkstr(js, "NaN", 3);
11498 if (isinf(val)) return val > 0 ? js_mkstr(js, "Infinity", 8) : js_mkstr(js, "-Infinity", 9);
11499
11500 char buf[128];
11501 char *p = buf + sizeof(buf) - 1;
11502 *p = '\0';
11503
11504 bool negative = val < 0;
11505 if (negative) val = -val;
11506
11507 long long int_part = (long long)val;
11508 double frac_part = val - (double)int_part;
11509
11510 if (int_part == 0) {
11511 *--p = '0';
11512 } else {
11513 while (int_part > 0 && p > buf) {
11514 int digit = int_part % radix;
11515 *--p = (char)(digit < 10 ? '0' + digit : 'a' + (digit - 10));
11516 int_part /= radix;
11517 }
11518 }
11519
11520 if (negative && p > buf) {
11521 *--p = '-';
11522 }
11523
11524 size_t int_len = strlen(p);
11525
11526 if (frac_part > 0.0000001) {
11527 char frac_buf[64];
11528 int frac_pos = 0;
11529 frac_buf[frac_pos++] = '.';
11530
11531 for (int i = 0; i < 16 && frac_part > 0.0000001 && frac_pos < 63; i++) {
11532 frac_part *= radix;
11533 int digit = (int)frac_part;
11534 frac_buf[frac_pos++] = (char)(digit < 10 ? '0' + digit : 'a' + (digit - 10));
11535 frac_part -= digit;
11536 }
11537 frac_buf[frac_pos] = '\0';
11538
11539 char result[192];
11540 snprintf(result, sizeof(result), "%s%s", p, frac_buf);
11541 return js_mkstr(js, result, strlen(result));
11542 }
11543
11544 return js_mkstr(js, p, int_len);
11545}
11546
11547static ant_value_t builtin_number_toFixed(ant_t *js, ant_value_t *args, int nargs) {
11548 ant_value_t num = unwrap_primitive(js, js->this_val);
11549 if (vtype(num) != T_NUM) return js_mkerr(js, "toFixed called on non-number");
11550
11551 double d = tod(num);
11552 if (isnan(d)) return js_mkstr(js, "NaN", 3);
11553 if (isinf(d)) return d > 0 ? js_mkstr(js, "Infinity", 8) : js_mkstr(js, "-Infinity", 9);
11554
11555 int digits = 0;
11556 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) {
11557 digits = (int) tod(args[0]);
11558 if (digits < 0 || digits > 100) {
11559 return js_mkerr_typed(js, JS_ERR_RANGE, "toFixed() digits argument must be between 0 and 100");
11560 }
11561 }
11562
11563 if (fabs(d) >= 1e21) {
11564 char buf[64];
11565 size_t len = strnum(num, buf, sizeof(buf));
11566 return js_mkstr(js, buf, len);
11567 }
11568
11569 char buf[160];
11570 size_t len = ant_number_to_fixed(d, digits, buf, sizeof(buf));
11571 if (len == 0) return js_mkerr(js, "number formatting failed");
11572
11573 return js_mkstr(js, buf, len);
11574}
11575
11576static ant_value_t builtin_number_toPrecision(ant_t *js, ant_value_t *args, int nargs) {
11577 ant_value_t num = unwrap_primitive(js, js->this_val);
11578 if (vtype(num) != T_NUM) return js_mkerr(js, "toPrecision called on non-number");
11579
11580 double d = tod(num);
11581 if (isnan(d)) return js_mkstr(js, "NaN", 3);
11582 if (isinf(d)) return d > 0 ? js_mkstr(js, "Infinity", 8) : js_mkstr(js, "-Infinity", 9);
11583
11584 if (nargs < 1 || vtype(args[0]) == T_UNDEF) {
11585 char buf[64];
11586 size_t len = strnum(num, buf, sizeof(buf));
11587 return js_mkstr(js, buf, len);
11588 }
11589
11590 int precision = (int) tod(args[0]);
11591 if (precision < 1 || precision > 100) {
11592 return js_mkerr_typed(js, JS_ERR_RANGE, "toPrecision() argument must be between 1 and 100");
11593 }
11594
11595 char buf[160];
11596 size_t len = ant_number_to_precision(d, precision, buf, sizeof(buf));
11597 if (len == 0) return js_mkerr(js, "number formatting failed");
11598
11599 return js_mkstr(js, buf, len);
11600}
11601
11602static ant_value_t builtin_number_toExponential(ant_t *js, ant_value_t *args, int nargs) {
11603 ant_value_t num = unwrap_primitive(js, js->this_val);
11604 if (vtype(num) != T_NUM) return js_mkerr(js, "toExponential called on non-number");
11605
11606 double d = tod(num);
11607 if (isnan(d)) return js_mkstr(js, "NaN", 3);
11608 if (isinf(d)) return d > 0 ? js_mkstr(js, "Infinity", 8) : js_mkstr(js, "-Infinity", 9);
11609
11610 int digits = -1;
11611 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) {
11612 digits = (int) tod(args[0]);
11613 if (digits < 0 || digits > 100) {
11614 return js_mkerr_typed(js, JS_ERR_RANGE, "toExponential() argument must be between 0 and 100");
11615 }
11616 }
11617
11618 char buf[160];
11619 size_t len = ant_number_to_exponential(d, digits, buf, sizeof(buf));
11620 if (len == 0) return js_mkerr(js, "number formatting failed");
11621
11622 return js_mkstr(js, buf, len);
11623}
11624
11625static ant_value_t builtin_number_valueOf(ant_t *js, ant_value_t *args, int nargs) {
11626 (void) args; (void) nargs;
11627 ant_value_t num = unwrap_primitive(js, js->this_val);
11628 if (vtype(num) != T_NUM) return js_mkerr(js, "valueOf called on non-number");
11629 return num;
11630}
11631
11632static ant_value_t builtin_number_toLocaleString(ant_t *js, ant_value_t *args, int nargs) {
11633 (void) args; (void) nargs;
11634 ant_value_t num = unwrap_primitive(js, js->this_val);
11635 if (vtype(num) != T_NUM) return js_mkerr(js, "toLocaleString called on non-number");
11636 double d = tod(num);
11637 char raw[64];
11638 strnum(num, raw, sizeof(raw));
11639 if (!isfinite(d) || strchr(raw, 'e') || strchr(raw, 'E'))
11640 return js_mkstr(js, raw, strlen(raw));
11641 char *dot = strchr(raw, '.');
11642 size_t int_len = dot ? (size_t)(dot - raw) : strlen(raw);
11643 size_t start = (raw[0] == '-') ? 1 : 0;
11644 size_t frac_len = dot ? strlen(dot) : 0;
11645 char buf[128];
11646 size_t pos = 0;
11647 if (start) buf[pos++] = '-';
11648 for (size_t i = start; i < int_len; i++) {
11649 buf[pos++] = raw[i];
11650 size_t remaining = int_len - 1 - i;
11651 if (remaining > 0 && remaining % 3 == 0) buf[pos++] = ',';
11652 }
11653 if (frac_len) memcpy(buf + pos, dot, frac_len);
11654 pos += frac_len;
11655 buf[pos] = '\0';
11656 return js_mkstr(js, buf, pos);
11657}
11658
11659static ant_value_t builtin_string_valueOf(ant_t *js, ant_value_t *args, int nargs) {
11660 (void) args; (void) nargs;
11661 ant_value_t str = to_string_val(js, js->this_val);
11662 if (vtype(str) != T_STR) return js_mkerr(js, "valueOf called on non-string");
11663 return str;
11664}
11665
11666static ant_value_t builtin_string_toString(ant_t *js, ant_value_t *args, int nargs) {
11667 return builtin_string_valueOf(js, args, nargs);
11668}
11669
11670static ant_value_t builtin_boolean_valueOf(ant_t *js, ant_value_t *args, int nargs) {
11671 (void) args; (void) nargs;
11672 ant_value_t b = unwrap_primitive(js, js->this_val);
11673 if (vtype(b) != T_BOOL) return js_mkerr(js, "valueOf called on non-boolean");
11674 return b;
11675}
11676
11677static ant_value_t builtin_boolean_toString(ant_t *js, ant_value_t *args, int nargs) {
11678 (void) args; (void) nargs;
11679 ant_value_t b = unwrap_primitive(js, js->this_val);
11680 if (vtype(b) != T_BOOL) return js_mkerr(js, "toString called on non-boolean");
11681 return vdata(b) ? js_mkstr(js, "true", 4) : js_mkstr(js, "false", 5);
11682}
11683
11684static ant_value_t builtin_parseInt(ant_t *js, ant_value_t *args, int nargs) {
11685 if (nargs < 1) return tov(JS_NAN);
11686
11687 ant_value_t str_val = args[0];
11688 if (vtype(str_val) != T_STR) {
11689 const char *str = js_str(js, str_val);
11690 str_val = js_mkstr(js, str, strlen(str));
11691 }
11692
11693 ant_offset_t str_len, str_off = vstr(js, str_val, &str_len);
11694 const char *str = (char *)(uintptr_t)(str_off);
11695
11696 int radix = 0;
11697 if (nargs >= 2 && vtype(args[1]) == T_NUM) {
11698 radix = (int) tod(args[1]);
11699 if (radix != 0 && (radix < 2 || radix > 36)) return tov(JS_NAN);
11700 }
11701
11702 ant_offset_t i = 0;
11703 while (i < str_len && is_space(str[i])) i++;
11704
11705 if (i >= str_len) return tov(JS_NAN);
11706
11707 int sign = 1;
11708 if (str[i] == '-') {
11709 sign = -1;
11710 i++;
11711 } else if (str[i] == '+') {
11712 i++;
11713 }
11714
11715 if ((radix == 0 || radix == 16) && i + 1 < str_len && str[i] == '0' && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
11716 radix = 16;
11717 i += 2;
11718 }
11719
11720 if (radix == 0) radix = 10;
11721
11722 double result = 0;
11723 bool found_digit = false;
11724
11725 while (i < str_len) {
11726 char ch = str[i];
11727 int digit = -1;
11728
11729 if (ch >= '0' && ch <= '9') {
11730 digit = ch - '0';
11731 } else if (ch >= 'a' && ch <= 'z') {
11732 digit = ch - 'a' + 10;
11733 } else if (ch >= 'A' && ch <= 'Z') {
11734 digit = ch - 'A' + 10;
11735 }
11736
11737 if (digit < 0 || digit >= radix) break;
11738
11739 result = result * radix + digit;
11740 found_digit = true;
11741 i++;
11742 }
11743
11744 if (!found_digit) return tov(JS_NAN);
11745
11746 return tov(sign * result);
11747}
11748
11749static ant_value_t builtin_parseFloat(ant_t *js, ant_value_t *args, int nargs) {
11750 if (nargs < 1) return tov(JS_NAN);
11751
11752 ant_value_t str_val = args[0];
11753 if (vtype(str_val) != T_STR) {
11754 const char *str = js_str(js, str_val);
11755 str_val = js_mkstr(js, str, strlen(str));
11756 }
11757
11758 ant_offset_t str_len, str_off = vstr(js, str_val, &str_len);
11759 const char *str = (char *)(uintptr_t)(str_off);
11760
11761 ant_offset_t i = 0;
11762 while (i < str_len && is_space(str[i])) i++;
11763
11764 if (i >= str_len) return tov(JS_NAN);
11765
11766 double result = JS_NAN;
11767 size_t processed = 0;
11768
11769 if (!ant_number_parse(
11770 str + i,
11771 (size_t)(str_len - i),
11772 ANT_NUMBER_PARSE_FLOAT_PREFIX,
11773 &result, &processed
11774 ) || processed == 0) return tov(JS_NAN);
11775
11776 return tov(result);
11777}
11778
11779static ant_value_t builtin_btoa(ant_t *js, ant_value_t *args, int nargs) {
11780 if (nargs < 1) return js_mkerr(js, "btoa requires 1 argument");
11781
11782 ant_value_t str_val = args[0];
11783 if (vtype(str_val) != T_STR) {
11784 const char *str = js_str(js, str_val);
11785 str_val = js_mkstr(js, str, strlen(str));
11786 }
11787
11788 ant_offset_t str_len, str_off = vstr(js, str_val, &str_len);
11789 const char *str = (char *)(uintptr_t)(str_off);
11790
11791 size_t out_len;
11792 char *out = ant_base64_encode((const uint8_t *)str, str_len, &out_len);
11793 if (!out) return js_mkerr(js, "out of memory");
11794
11795 ant_value_t result = js_mkstr(js, out, out_len);
11796 free(out);
11797
11798 return result;
11799}
11800
11801static ant_value_t builtin_atob(ant_t *js, ant_value_t *args, int nargs) {
11802 if (nargs < 1) return js_mkerr(js, "atob requires 1 argument");
11803
11804 ant_value_t str_val = args[0];
11805 if (vtype(str_val) != T_STR) {
11806 const char *str = js_str(js, str_val);
11807 str_val = js_mkstr(js, str, strlen(str));
11808 }
11809
11810 ant_offset_t str_len, str_off = vstr(js, str_val, &str_len);
11811 const char *str = (char *)(uintptr_t)(str_off);
11812 if (str_len == 0) return js_mkstr(js, "", 0);
11813
11814 size_t out_len;
11815 uint8_t *out = ant_base64_decode(str, str_len, &out_len);
11816 if (!out) return js_mkerr(js, "atob: invalid base64 string");
11817
11818 ant_value_t result = js_mkstr(js, (char *)out, out_len);
11819 free(out);
11820
11821 return result;
11822}
11823
11824static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int nargs);
11825static ant_value_t builtin_reject_internal(ant_t *js, ant_value_t *args, int nargs);
11826
11827static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len) {
11828 uint32_t pid = get_promise_id(js, value);
11829 ant_promise_state_t *pd = get_promise_data(js, value, false);
11830
11831 const char *content;
11832 char *allocated = NULL;
11833
11834 if (!pd || pd->state == 0) {
11835 content = "<pending>";
11836 } else if (pd->state == 2) {
11837 char *val = tostr_alloc(js, pd->value);
11838 allocated = ant_calloc(strlen(val) + 12);
11839 sprintf(allocated, "<rejected> %s", val);
11840 free(val);
11841 content = allocated;
11842 } else { content = allocated = tostr_alloc(js, pd->value); }
11843
11844 uint32_t trigger_pid = 0;
11845 if (pd && vtype(pd->trigger_parent) == T_PROMISE) {
11846 trigger_pid = get_promise_id(js, pd->trigger_parent);
11847 }
11848 size_t result = trigger_pid
11849 ? (size_t)snprintf(buf, len, "Promise {\n %s,\n Symbol(async_id): %u,\n Symbol(trigger_async_id): %u\n}", content, pid, trigger_pid)
11850 : (size_t)snprintf(buf, len, "Promise {\n %s,\n Symbol(async_id): %u\n}", content, pid);
11851
11852 if (allocated) free(allocated);
11853 return result;
11854}
11855
11856static ant_promise_state_t *get_promise_data(ant_t *js, ant_value_t promise, bool create) {
11857 if (vtype(promise) != T_PROMISE) return NULL;
11858 ant_object_t *obj = js_obj_ptr(js_as_obj(promise));
11859 if (!obj) return NULL;
11860 if (obj->promise_state) return obj->promise_state;
11861 if (!create) return NULL;
11862
11863 ant_promise_state_t *entry = (ant_promise_state_t *)calloc(1, sizeof(*entry));
11864 if (!entry) return NULL;
11865 entry->promise_id = next_promise_id++;
11866 entry->trigger_parent = js_mkundef();
11867 entry->inline_handler = (promise_handler_t){ 0 };
11868 entry->handlers = NULL;
11869 entry->handler_count = 0;
11870 entry->state = 0;
11871 entry->value = js_mkundef();
11872 entry->trigger_queued = false;
11873 entry->has_rejection_handler = false;
11874 entry->processing = false;
11875 entry->unhandled_reported = false;
11876 obj->promise_state = entry;
11877 obj->type_tag = T_PROMISE;
11878 return entry;
11879}
11880
11881static uint32_t get_promise_id(ant_t *js, ant_value_t p) {
11882 ant_promise_state_t *pd = get_promise_data(js, p, false);
11883 return pd ? pd->promise_id : 0;
11884}
11885
11886bool js_mark_promise_trigger_queued(ant_t *js, ant_value_t promise) {
11887 ant_promise_state_t *pd = get_promise_data(js, promise, false);
11888 if (!pd) return true;
11889 if (pd->trigger_queued) return false;
11890 pd->trigger_queued = true;
11891 return true;
11892}
11893
11894void js_mark_promise_trigger_dequeued(ant_t *js, ant_value_t promise) {
11895 ant_promise_state_t *pd = get_promise_data(js, promise, false);
11896 if (!pd) return;
11897 pd->trigger_queued = false;
11898}
11899
11900static ant_value_t make_data_cfunc(
11901 ant_t *js, ant_value_t data,
11902 ant_value_t (*fn)(ant_t *, ant_value_t *, int)
11903) {
11904 GC_ROOT_SAVE(root_mark, js);
11905 GC_ROOT_PIN(js, data);
11906
11907 ant_value_t obj = mkobj(js, 0);
11908 if (is_err(obj)) {
11909 GC_ROOT_RESTORE(js, root_mark);
11910 return obj;
11911 }
11912
11913 GC_ROOT_PIN(js, obj);
11914 set_slot(obj, SLOT_DATA, data);
11915 set_slot(obj, SLOT_CFUNC, js_mkfun_dyn(fn));
11916
11917 ant_value_t func = js_obj_to_func(obj);
11918 GC_ROOT_RESTORE(js, root_mark);
11919 return func;
11920}
11921
11922ant_value_t js_mkpromise(ant_t *js) {
11923 ant_value_t obj = mkobj(js, 0);
11924 if (is_err(obj)) return obj;
11925 if (!get_promise_data(js, mkval(T_PROMISE, vdata(obj)), true))
11926 return js_mkerr(js, "out of memory");
11927
11928 ant_value_t promise_ctor = js_get(js, js_glob(js), "Promise");
11929 if (vtype(promise_ctor) == T_FUNC || vtype(promise_ctor) == T_CFUNC) {
11930 set_slot(obj, SLOT_CTOR, promise_ctor);
11931 }
11932
11933 ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7);
11934 if (is_object_type(promise_proto)) {
11935 js_set_proto_init(obj, promise_proto);
11936 }
11937
11938 return mkval(T_PROMISE, vdata(obj));
11939}
11940
11941ant_value_t js_mkgenerator(ant_t *js) {
11942 ant_value_t obj = mkobj(js, 0);
11943 if (is_err(obj)) return obj;
11944
11945 ant_object_t *ptr = js_obj_ptr(obj);
11946 if (!ptr) return js_mkerr(js, "invalid generator object");
11947 ptr->type_tag = T_GENERATOR;
11948
11949 ant_value_t gen = mkval(T_GENERATOR, vdata(obj));
11950 if (is_object_type(js->sym.generator_proto)) {
11951 js_set_proto_init(gen, js->sym.generator_proto);
11952 }
11953
11954 return gen;
11955}
11956
11957ant_value_t js_promise_then(ant_t *js, ant_value_t promise, ant_value_t on_fulfilled, ant_value_t on_rejected) {
11958 ant_value_t args_then[2] = { on_fulfilled, on_rejected };
11959 ant_value_t saved_this = js->this_val;
11960
11961 js->this_val = promise;
11962 ant_value_t result = builtin_promise_then(js, args_then, 2);
11963 js->this_val = saved_this;
11964
11965 return result;
11966}
11967
11968static void js_mark_promise_rejection_handled_chain(ant_t *js, ant_value_t promise) {
11969 ant_value_t current = promise;
11970
11971 while (vtype(current) == T_PROMISE) {
11972 ant_promise_state_t *pd = get_promise_data(js, current, false);
11973 if (!pd) break;
11974
11975 if (pd->unhandled_reported) js_fire_rejection_handled(js, current, pd->value);
11976 pd->has_rejection_handler = true;
11977 pd->unhandled_reported = false;
11978 current = pd->trigger_parent;
11979 }
11980}
11981
11982static inline ant_value_t js_get_thenable_then(ant_t *js, ant_value_t value) {
11983 if (!is_object_type(value)) return js_mkundef();
11984 return js_getprop_fallback(js, value, "then");
11985}
11986
11987ant_value_t js_promise_assimilate_awaitable(ant_t *js, ant_value_t value) {
11988 if (vtype(value) == T_PROMISE || !is_object_type(value)) return value;
11989
11990 GC_ROOT_SAVE(root_mark, js);
11991 GC_ROOT_PIN(js, value);
11992
11993 ant_value_t then_prop = js_get_thenable_then(js, value);
11994 GC_ROOT_PIN(js, then_prop);
11995
11996 if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) {
11997 ant_value_t promise = js_mkpromise(js);
11998 GC_ROOT_PIN(js, promise);
11999 js_resolve_promise(js, promise, value);
12000 GC_ROOT_RESTORE(js, root_mark);
12001 return promise;
12002 }
12003
12004 GC_ROOT_RESTORE(js, root_mark);
12005 return value;
12006}
12007
12008void js_promise_clear_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) {
12009 if (vtype(promise) != T_PROMISE || !coro) return;
12010
12011 ant_promise_state_t *pd = get_promise_data(js, promise, false);
12012 if (!pd || pd->handler_count == 0) return;
12013
12014 if (pd->handler_count == 1) {
12015 if (pd->inline_handler.await_coro == coro) pd->inline_handler.await_coro = NULL;
12016 return;
12017 }
12018
12019 if (!pd->handlers) return;
12020 promise_handler_t *h = NULL;
12021 while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) {
12022 if (h->await_coro == coro) h->await_coro = NULL;
12023 }
12024}
12025
12026js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) {
12027 js_await_result_t result = {
12028 .state = JS_AWAIT_PENDING,
12029 .value = js_mkundef(),
12030 };
12031
12032 if (vtype(promise) != T_PROMISE || !coro) return result;
12033 ant_promise_state_t *pd = get_promise_data(js, promise, false);
12034
12035 if (!pd) {
12036 result.state = JS_AWAIT_ERROR;
12037 result.value = js_mkerr(js, "invalid promise state");
12038 return result;
12039 }
12040
12041 promise_handler_t h = { js_mkundef(), js_mkundef(), js_mkundef(), coro };
12042 if (!promise_handler_append(pd, &h)) {
12043 result.state = JS_AWAIT_ERROR;
12044 result.value = js_mkerr(js, "out of memory");
12045 return result;
12046 }
12047
12048 coro->awaited_promise = promise;
12049 coro->await_registered = true;
12050 coroutine_hold(coro, CORO_HOLD_AWAIT);
12051
12052 js_mark_promise_rejection_handled_chain(js, promise);
12053 if (pd->state == 0) gc_root_pending_promise(js_obj_ptr(js_as_obj(promise)));
12054 else queue_promise_trigger(js, promise);
12055
12056 return result;
12057}
12058
12059void js_process_promise_handlers(ant_t *js, ant_value_t promise) {
12060 ant_object_t *pobj = js_obj_ptr(promise);
12061 ant_promise_state_t *pd = get_promise_data(js, promise, false);
12062 if (!pd) return;
12063
12064 int state = pd->state;
12065 ant_value_t val = pd->value;
12066
12067 uint32_t len = promise_handler_count(pd);
12068 if (len == 0) return;
12069
12070 gc_root_pending_promise(pobj);
12071 pd->processing = true;
12072
12073 for (uint32_t i = 0; i < len; i++) {
12074 promise_handler_t *h = promise_handler_at(pd, i);
12075 if (!h) continue;
12076
12077 if (h->await_coro) {
12078 coroutine_t *await_coro = h->await_coro;
12079 h->await_coro = NULL;
12080 settle_and_resume_coroutine(js, await_coro, val, state != 1);
12081 continue;
12082 }
12083
12084 ant_value_t handler = (state == 1) ? h->onFulfilled : h->onRejected;
12085 if (vtype(handler) != T_FUNC && vtype(handler) != T_CFUNC) {
12086 if (state == 1) js_resolve_promise(js, h->nextPromise, val);
12087 else js_reject_promise(js, h->nextPromise, val);
12088 continue;
12089 }
12090
12091 ant_value_t res = js_mkundef();
12092 if (vtype(handler) == T_CFUNC) {
12093 ant_value_t (*fn)(ant_t *, ant_value_t *, int) = js_as_cfunc(handler);
12094 res = fn(js, &val, 1);
12095 } else {
12096 ant_value_t call_args[] = { val };
12097 res = sv_vm_call(js->vm, js, handler, js_mkundef(), call_args, 1, NULL, false);
12098 }
12099
12100 if (!is_err(res)) {
12101 js_resolve_promise(js, h->nextPromise, res);
12102 continue;
12103 }
12104
12105 ant_value_t reject_val = js->thrown_value;
12106 if (vtype(reject_val) == T_UNDEF) reject_val = res;
12107
12108 js->thrown_exists = false;
12109 js->thrown_value = js_mkundef();
12110 js->thrown_stack = js_mkundef();
12111 js_reject_promise(js, h->nextPromise, reject_val);
12112 }
12113
12114 pd->processing = false;
12115 promise_handlers_clear(pd);
12116 gc_unroot_pending_promise(js_obj_ptr(promise));
12117}
12118
12119void js_resolve_promise(ant_t *js, ant_value_t p, ant_value_t val) {
12120 GC_ROOT_SAVE(root_mark, js);
12121 GC_ROOT_PIN(js, p);
12122 GC_ROOT_PIN(js, val);
12123
12124 ant_promise_state_t *pd = get_promise_data(js, p, false);
12125 if (!pd || pd->state != 0) {
12126 GC_ROOT_RESTORE(js, root_mark);
12127 return;
12128 }
12129
12130 if (vtype(val) == T_PROMISE) {
12131 if (vdata(js_as_obj(val)) == vdata(js_as_obj(p))) {
12132 ant_value_t err = js_mkerr(js, "TypeError: Chaining cycle");
12133 GC_ROOT_PIN(js, err);
12134 js_reject_promise(js, p, err);
12135 GC_ROOT_RESTORE(js, root_mark);
12136 return;
12137 }
12138
12139 ant_promise_state_t *src_pd = get_promise_data(js, val, false);
12140 if (!src_pd) {
12141 pd->state = 1;
12142 pd->value = val;
12143 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val);
12144 GC_ROOT_RESTORE(js, root_mark);
12145 return;
12146 }
12147
12148 pd->trigger_parent = val;
12149
12150 promise_handler_t h = { js_mkundef(), js_mkundef(), p, NULL };
12151 if (!promise_handler_append(src_pd, &h)) {
12152 pd->state = 2;
12153 pd->value = js_mkerr(js, "out of memory");
12154 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), pd->value);
12155 GC_ROOT_RESTORE(js, root_mark);
12156 return;
12157 }
12158
12159 gc_write_barrier(js, js_obj_ptr(js_as_obj(val)), p);
12160 js_mark_promise_rejection_handled_chain(js, val);
12161
12162 if (src_pd->state == 0) gc_root_pending_promise(js_obj_ptr(js_as_obj(val)));
12163 else queue_promise_trigger(js, val);
12164 GC_ROOT_RESTORE(js, root_mark);
12165
12166 return;
12167 }
12168
12169 if (is_object_type(val)) {
12170 ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal);
12171 GC_ROOT_PIN(js, res_fn);
12172
12173 if (is_err(res_fn)) {
12174 js_reject_promise(js, p, res_fn);
12175 GC_ROOT_RESTORE(js, root_mark);
12176 return;
12177 }
12178
12179 ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal);
12180 GC_ROOT_PIN(js, rej_fn);
12181
12182 if (is_err(rej_fn)) {
12183 js_reject_promise(js, p, rej_fn);
12184 GC_ROOT_RESTORE(js, root_mark);
12185 return;
12186 }
12187
12188 ant_value_t then_prop = js_get_thenable_then(js, val);
12189 GC_ROOT_PIN(js, then_prop);
12190
12191 if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) {
12192 ant_value_t call_args[] = { res_fn, rej_fn };
12193 sv_vm_call(js->vm, js, then_prop, val, call_args, 2, NULL, false);
12194 GC_ROOT_RESTORE(js, root_mark);
12195 return;
12196 }
12197 }
12198
12199 pd->state = 1;
12200 pd->value = val;
12201
12202 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val);
12203 if (promise_has_handlers(pd)) queue_promise_trigger(js, p);
12204 GC_ROOT_RESTORE(js, root_mark);
12205}
12206
12207void js_reject_promise(ant_t *js, ant_value_t p, ant_value_t val) {
12208 if (vtype(val) == T_ERR) {
12209 if (vdata(val) != 0) val = mkval(T_OBJ, vdata(val));
12210 else if (js->thrown_exists) val = js->thrown_value;
12211 else val = js_make_error_silent(js, JS_ERR_INTERNAL, "unknown error");
12212 }
12213
12214 ant_promise_state_t *pd = get_promise_data(js, p, false);
12215 if (!pd || pd->state != 0) return;
12216
12217 pd->state = 2;
12218 pd->value = val;
12219 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val);
12220 pd->unhandled_reported = false;
12221
12222 if (js->pending_rejections.len >= js->pending_rejections.cap) {
12223 size_t new_cap = js->pending_rejections.cap ? js->pending_rejections.cap * 2 : 16;
12224 ant_value_t *ns = realloc(js->pending_rejections.items, new_cap * sizeof(*ns));
12225 if (ns) { js->pending_rejections.items = ns; js->pending_rejections.cap = new_cap; }
12226 }
12227 if (js->pending_rejections.len < js->pending_rejections.cap)
12228 js->pending_rejections.items[js->pending_rejections.len++] = p;
12229
12230 queue_promise_trigger(js, p);
12231}
12232
12233static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int nargs) {
12234 ant_value_t me = js->current_func;
12235 ant_value_t p = get_slot(me, SLOT_DATA);
12236 if (vtype(p) != T_PROMISE) return js_mkundef();
12237 js_resolve_promise(js, p, nargs > 0 ? args[0] : js_mkundef());
12238 return js_mkundef();
12239}
12240
12241static ant_value_t builtin_reject_internal(ant_t *js, ant_value_t *args, int nargs) {
12242 ant_value_t me = js->current_func;
12243 ant_value_t p = get_slot(me, SLOT_DATA);
12244 if (vtype(p) != T_PROMISE) return js_mkundef();
12245 js_reject_promise(js, p, nargs > 0 ? args[0] : js_mkundef());
12246 return js_mkundef();
12247}
12248
12249static ant_value_t builtin_Promise(ant_t *js, ant_value_t *args, int nargs) {
12250 if (vtype(js->new_target) == T_UNDEF) {
12251 return js_mkerr_typed(js, JS_ERR_TYPE, "Promise constructor cannot be invoked without 'new'");
12252 }
12253
12254 if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) {
12255 const char *val_str = nargs == 0 ? "undefined" : js_str(js, args[0]);
12256 return js_mkerr_typed(js, JS_ERR_TYPE, "Promise resolver %s is not a function", val_str);
12257 }
12258
12259 GC_ROOT_SAVE(root_mark, js);
12260 ant_value_t executor = args[0];
12261 GC_ROOT_PIN(js, executor);
12262
12263 ant_value_t p = js_mkpromise(js);
12264 if (is_err(p)) {
12265 GC_ROOT_RESTORE(js, root_mark);
12266 return p;
12267 }
12268 GC_ROOT_PIN(js, p);
12269
12270 ant_value_t new_target = js->new_target;
12271 ant_value_t p_obj = js_as_obj(p);
12272
12273 ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7);
12274 ant_value_t instance_proto = js_instance_proto_from_new_target(js, promise_proto);
12275 GC_ROOT_PIN(js, instance_proto);
12276
12277 if (vtype(new_target) == T_FUNC || vtype(new_target) == T_CFUNC) set_slot(p_obj, SLOT_CTOR, new_target);
12278 if (is_object_type(instance_proto)) js_set_proto_init(p_obj, instance_proto);
12279
12280 ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal);
12281 GC_ROOT_PIN(js, res_fn);
12282 if (is_err(res_fn)) {
12283 GC_ROOT_RESTORE(js, root_mark);
12284 return res_fn;
12285 }
12286
12287 ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal);
12288 GC_ROOT_PIN(js, rej_fn);
12289 if (is_err(rej_fn)) {
12290 GC_ROOT_RESTORE(js, root_mark);
12291 return rej_fn;
12292 }
12293
12294 ant_value_t exec_args[] = { res_fn, rej_fn };
12295 ant_value_t exec_result = sv_vm_call(js->vm, js, executor, js_mkundef(), exec_args, 2, NULL, false);
12296
12297 if (is_err(exec_result) || js->thrown_exists) {
12298 ant_value_t reject_val = js->thrown_exists ? js->thrown_value : exec_result;
12299 js->thrown_exists = false;
12300 js->thrown_value = js_mkundef();
12301 js->thrown_stack = js_mkundef();
12302 js_reject_promise(js, p, reject_val);
12303 }
12304
12305 GC_ROOT_RESTORE(js, root_mark);
12306 return p;
12307}
12308
12309static ant_value_t builtin_Promise_resolve(ant_t *js, ant_value_t *args, int nargs) {
12310 ant_value_t val = nargs > 0 ? args[0] : js_mkundef();
12311 if (vtype(val) == T_PROMISE) return val;
12312 ant_value_t p = js_mkpromise(js);
12313 js_resolve_promise(js, p, val);
12314 return p;
12315}
12316
12317static ant_value_t builtin_Promise_reject(ant_t *js, ant_value_t *args, int nargs) {
12318 ant_value_t val = nargs > 0 ? args[0] : js_mkundef();
12319 ant_value_t p = js_mkpromise(js);
12320 js_reject_promise(js, p, val);
12321 return p;
12322}
12323
12324static ant_value_t promise_species_noop_executor(ant_t *js, ant_value_t *args, int nargs) {
12325 return js_mkundef();
12326}
12327
12328static inline bool is_same_heap_value(ant_value_t a, ant_value_t b) {
12329 return vtype(a) == vtype(b) && vdata(a) == vdata(b);
12330}
12331
12332static inline bool is_constructor_value(ant_value_t value) {
12333 return vtype(value) == T_FUNC || vtype(value) == T_CFUNC;
12334}
12335
12336static void promise_init_derived_promise(
12337 ant_t *js,
12338 ant_value_t next_p,
12339 ant_value_t parent_p,
12340 ant_value_t species_ctor,
12341 ant_value_t promise_ctor
12342) {
12343 ant_value_t next_obj = js_as_obj(next_p);
12344
12345 if (is_constructor_value(species_ctor) && !is_same_heap_value(species_ctor, promise_ctor)) {
12346 ant_value_t species_proto = js_get(js, species_ctor, "prototype");
12347 if (is_object_type(species_proto)) js_set_proto_init(next_obj, species_proto);
12348 set_slot(next_obj, SLOT_CTOR, species_ctor);
12349 return;
12350 }
12351
12352 ant_value_t parent_obj = js_as_obj(parent_p);
12353 ant_value_t parent_proto = get_slot(parent_obj, SLOT_PROTO);
12354 if (vtype(parent_proto) == T_OBJ) js_set_proto_init(next_obj, parent_proto);
12355
12356 ant_value_t parent_ctor = get_slot(parent_obj, SLOT_CTOR);
12357 if (is_constructor_value(parent_ctor)) set_slot(next_obj, SLOT_CTOR, parent_ctor);
12358}
12359
12360static ant_value_t builtin_promise_then(ant_t *js, ant_value_t *args, int nargs) {
12361 ant_value_t p = js->this_val;
12362 if (vtype(p) != T_PROMISE) return js_mkerr(js, "not a promise");
12363
12364 GC_ROOT_SAVE(root_mark, js);
12365 GC_ROOT_PIN(js, p);
12366
12367 ant_value_t promise_ctor = js_get(js, js_glob(js), "Promise");
12368 GC_ROOT_PIN(js, promise_ctor);
12369 ant_value_t species_ctor = promise_ctor;
12370 GC_ROOT_PIN(js, species_ctor);
12371 ant_value_t p_obj = js_as_obj(p);
12372 ant_value_t ctor = js_get(js, p_obj, "constructor");
12373 GC_ROOT_PIN(js, ctor);
12374
12375 if (is_err(ctor)) {
12376 GC_ROOT_RESTORE(js, root_mark);
12377 return ctor;
12378 }
12379 if (vtype(ctor) == T_UNDEF) ctor = get_slot(p_obj, SLOT_CTOR);
12380
12381 ant_value_t species = get_ctor_species_value(js, ctor);
12382 GC_ROOT_PIN(js, species);
12383 if (is_err(species)) {
12384 GC_ROOT_RESTORE(js, root_mark);
12385 return species;
12386 }
12387
12388 if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) {
12389 species_ctor = species;
12390 } else if (vtype(species) == T_NULL) {
12391 species_ctor = promise_ctor;
12392 } else if (vtype(species) != T_UNDEF) {
12393 ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "Promise species is not a constructor");
12394 GC_ROOT_RESTORE(js, root_mark);
12395 return err;
12396 }
12397
12398 ant_value_t nextP = js_mkpromise(js);
12399 GC_ROOT_PIN(js, nextP);
12400 promise_init_derived_promise(js, nextP, p, species_ctor, promise_ctor);
12401
12402 ant_value_t onFulfilled = nargs > 0 ? args[0] : js_mkundef();
12403 ant_value_t onRejected = nargs > 1 ? args[1] : js_mkundef();
12404
12405 GC_ROOT_PIN(js, onFulfilled);
12406 GC_ROOT_PIN(js, onRejected);
12407
12408 ant_promise_state_t *next_pd = get_promise_data(js, nextP, false);
12409 if (next_pd) next_pd->trigger_parent = p;
12410
12411 ant_promise_state_t *pd = get_promise_data(js, p, false);
12412 if (pd) {
12413 promise_handler_t h = { onFulfilled, onRejected, nextP, NULL };
12414 if (!promise_handler_append(pd, &h)) {
12415 GC_ROOT_RESTORE(js, root_mark);
12416 return js_mkerr(js, "out of memory");
12417 }
12418
12419 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), nextP);
12420 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), onFulfilled);
12421 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), onRejected);
12422
12423 if (vtype(onRejected) == T_FUNC || vtype(onRejected) == T_CFUNC)
12424 js_mark_promise_rejection_handled_chain(js, p);
12425
12426 if (pd->state == 0)
12427 gc_root_pending_promise(js_obj_ptr(p));
12428 }
12429
12430 if (pd && pd->state != 0) queue_promise_trigger(js, p);
12431 GC_ROOT_RESTORE(js, root_mark);
12432
12433 return nextP;
12434}
12435
12436static ant_value_t builtin_promise_catch(ant_t *js, ant_value_t *args, int nargs) {
12437 ant_value_t args_then[] = { js_mkundef(), nargs > 0 ? args[0] : js_mkundef() };
12438 return builtin_promise_then(js, args_then, 2);
12439}
12440
12441static ant_value_t finally_value_thunk(ant_t *js, ant_value_t *args, int nargs) {
12442 ant_value_t me = js->current_func;
12443 return get_slot(me, SLOT_DATA);
12444}
12445
12446static ant_value_t finally_thrower(ant_t *js, ant_value_t *args, int nargs) {
12447 ant_value_t me = js->current_func;
12448 ant_value_t reason = get_slot(me, SLOT_DATA);
12449 ant_value_t rejected = js_mkpromise(js);
12450 js_reject_promise(js, rejected, reason);
12451 return rejected;
12452}
12453
12454static ant_value_t finally_identity_reject(ant_t *js, ant_value_t *args, int nargs) {
12455 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef();
12456 ant_value_t rejected = js_mkpromise(js);
12457 js_reject_promise(js, rejected, reason);
12458 return rejected;
12459}
12460
12461static ant_value_t finally_fulfilled_wrapper(ant_t *js, ant_value_t *args, int nargs) {
12462 GC_ROOT_SAVE(root_mark, js);
12463 ant_value_t me = js->current_func;
12464 ant_value_t callback = get_slot(me, SLOT_DATA);
12465 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
12466 GC_ROOT_PIN(js, callback);
12467 GC_ROOT_PIN(js, value);
12468
12469 ant_value_t result = js_mkundef();
12470 if (vtype(callback) == T_FUNC || vtype(callback) == T_CFUNC) {
12471 result = sv_vm_call(js->vm, js, callback, js_mkundef(), NULL, 0, NULL, false);
12472 if (is_err(result)) {
12473 GC_ROOT_RESTORE(js, root_mark);
12474 return result;
12475 }
12476 }
12477 GC_ROOT_PIN(js, result);
12478
12479 if (vtype(result) == T_PROMISE) {
12480 ant_value_t thunk_fn = make_data_cfunc(js, value, finally_value_thunk);
12481 GC_ROOT_PIN(js, thunk_fn);
12482
12483 if (is_err(thunk_fn)) {
12484 GC_ROOT_RESTORE(js, root_mark);
12485 return thunk_fn;
12486 }
12487
12488 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject);
12489 ant_value_t ret = js_promise_then(js, result, thunk_fn, identity_rej_fn);
12490 GC_ROOT_RESTORE(js, root_mark);
12491
12492 return ret;
12493 }
12494
12495 if (is_object_type(result)) {
12496 ant_value_t then_fn = js_get_thenable_then(js, result);
12497 GC_ROOT_PIN(js, then_fn);
12498
12499 if (vtype(then_fn) == T_FUNC || vtype(then_fn) == T_CFUNC) {
12500 ant_value_t thunk_fn = make_data_cfunc(js, value, finally_value_thunk);
12501 GC_ROOT_PIN(js, thunk_fn);
12502
12503 if (is_err(thunk_fn)) {
12504 GC_ROOT_RESTORE(js, root_mark);
12505 return thunk_fn;
12506 }
12507
12508 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject);
12509 ant_value_t call_args[] = { thunk_fn, identity_rej_fn };
12510 ant_value_t ret = sv_vm_call(js->vm, js, then_fn, result, call_args, 2, NULL, false);
12511 GC_ROOT_RESTORE(js, root_mark);
12512
12513 return ret;
12514 }}
12515
12516 GC_ROOT_RESTORE(js, root_mark);
12517 return value;
12518}
12519
12520static ant_value_t finally_rejected_wrapper(ant_t *js, ant_value_t *args, int nargs) {
12521 GC_ROOT_SAVE(root_mark, js);
12522
12523 ant_value_t me = js->current_func;
12524 ant_value_t callback = get_slot(me, SLOT_DATA);
12525 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef();
12526
12527 GC_ROOT_PIN(js, callback);
12528 GC_ROOT_PIN(js, reason);
12529
12530 ant_value_t result = js_mkundef();
12531 if (vtype(callback) == T_FUNC || vtype(callback) == T_CFUNC) {
12532 result = sv_vm_call(js->vm, js, callback, js_mkundef(), NULL, 0, NULL, false);
12533 if (is_err(result)) {
12534 GC_ROOT_RESTORE(js, root_mark);
12535 return result;
12536 }
12537 }
12538 GC_ROOT_PIN(js, result);
12539
12540 if (vtype(result) == T_PROMISE) {
12541 ant_value_t thrower_fn = make_data_cfunc(js, reason, finally_thrower);
12542 GC_ROOT_PIN(js, thrower_fn);
12543 if (is_err(thrower_fn)) {
12544 GC_ROOT_RESTORE(js, root_mark);
12545 return thrower_fn;
12546 }
12547 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject);
12548 ant_value_t ret = js_promise_then(js, result, thrower_fn, identity_rej_fn);
12549 GC_ROOT_RESTORE(js, root_mark);
12550 return ret;
12551 }
12552
12553 if (is_object_type(result)) {
12554 ant_value_t then_prop = js_get_thenable_then(js, result);
12555 GC_ROOT_PIN(js, then_prop);
12556
12557 if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) {
12558 ant_value_t thrower_fn = make_data_cfunc(js, reason, finally_thrower);
12559 GC_ROOT_PIN(js, thrower_fn);
12560
12561 if (is_err(thrower_fn)) {
12562 GC_ROOT_RESTORE(js, root_mark);
12563 return thrower_fn;
12564 }
12565
12566 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject);
12567 ant_value_t call_args[] = { thrower_fn, identity_rej_fn };
12568 ant_value_t ret = sv_vm_call(js->vm, js, then_prop, result, call_args, 2, NULL, false);
12569 GC_ROOT_RESTORE(js, root_mark);
12570
12571 return ret;
12572 }}
12573
12574 ant_value_t rejected = js_mkpromise(js);
12575 GC_ROOT_PIN(js, rejected);
12576 js_reject_promise(js, rejected, reason);
12577 GC_ROOT_RESTORE(js, root_mark);
12578
12579 return rejected;
12580}
12581
12582static ant_value_t builtin_promise_finally(ant_t *js, ant_value_t *args, int nargs) {
12583 GC_ROOT_SAVE(root_mark, js);
12584 ant_value_t callback = nargs > 0 ? args[0] : js_mkundef();
12585 GC_ROOT_PIN(js, callback);
12586
12587 ant_value_t fulfilled_fn = make_data_cfunc(js, callback, finally_fulfilled_wrapper);
12588 GC_ROOT_PIN(js, fulfilled_fn);
12589 if (is_err(fulfilled_fn)) {
12590 GC_ROOT_RESTORE(js, root_mark);
12591 return fulfilled_fn;
12592 }
12593
12594 ant_value_t rejected_fn = make_data_cfunc(js, callback, finally_rejected_wrapper);
12595 GC_ROOT_PIN(js, rejected_fn);
12596 if (is_err(rejected_fn)) {
12597 GC_ROOT_RESTORE(js, root_mark);
12598 return rejected_fn;
12599 }
12600
12601 ant_value_t args_then[] = { fulfilled_fn, rejected_fn };
12602 ant_value_t ret = builtin_promise_then(js, args_then, 2);
12603 GC_ROOT_RESTORE(js, root_mark);
12604
12605 return ret;
12606}
12607
12608static ant_value_t builtin_Promise_try(ant_t *js, ant_value_t *args, int nargs) {
12609 if (nargs == 0) return builtin_Promise_resolve(js, args, 0);
12610
12611 ant_value_t fn = args[0];
12612 ant_value_t *call_args = nargs > 1 ? &args[1] : NULL;
12613 int call_nargs = nargs > 1 ? nargs - 1 : 0;
12614 ant_value_t res = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, call_nargs, NULL, false);
12615
12616 if (is_err(res)) {
12617 ant_value_t reject_val = js->thrown_value;
12618 if (vtype(reject_val) == T_UNDEF) reject_val = res;
12619 js->thrown_exists = false;
12620 js->thrown_value = js_mkundef();
12621 js->thrown_stack = js_mkundef();
12622 ant_value_t rej_args[] = { reject_val };
12623 return builtin_Promise_reject(js, rej_args, 1);
12624 }
12625
12626 ant_value_t res_args[] = { res };
12627 return builtin_Promise_resolve(js, res_args, 1);
12628}
12629
12630static ant_value_t builtin_Promise_withResolvers(ant_t *js, ant_value_t *args, int nargs) {
12631 GC_ROOT_SAVE(root_mark, js);
12632 ant_value_t p = js_mkpromise(js);
12633 GC_ROOT_PIN(js, p);
12634
12635 ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal);
12636 GC_ROOT_PIN(js, res_fn);
12637 if (is_err(res_fn)) {
12638 GC_ROOT_RESTORE(js, root_mark);
12639 return res_fn;
12640 }
12641
12642 ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal);
12643 GC_ROOT_PIN(js, rej_fn);
12644 if (is_err(rej_fn)) {
12645 GC_ROOT_RESTORE(js, root_mark);
12646 return rej_fn;
12647 }
12648
12649 ant_value_t result = js_newobj(js);
12650 GC_ROOT_PIN(js, result);
12651 js_setprop(js, result, js_mkstr(js, "promise", 7), p);
12652 js_setprop(js, result, js_mkstr(js, "resolve", 7), res_fn);
12653 js_setprop(js, result, js_mkstr(js, "reject", 6), rej_fn);
12654
12655 GC_ROOT_RESTORE(js, root_mark);
12656 return result;
12657}
12658
12659static ant_value_t mkpromise_with_ctor(ant_t *js, ant_value_t ctor) {
12660 GC_ROOT_SAVE(root_mark, js);
12661 GC_ROOT_PIN(js, ctor);
12662 ant_value_t p = js_mkpromise(js);
12663 GC_ROOT_PIN(js, p);
12664 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) {
12665 GC_ROOT_RESTORE(js, root_mark);
12666 return p;
12667 }
12668
12669 ant_value_t proto = js_get(js, ctor, "prototype");
12670 if (is_err(proto)) {
12671 GC_ROOT_RESTORE(js, root_mark);
12672 return proto;
12673 }
12674 if (is_object_type(proto)) {
12675 ant_value_t p_obj = js_as_obj(p);
12676 set_slot(p_obj, SLOT_CTOR, ctor);
12677 js_set_proto_init(p_obj, proto);
12678 }
12679 GC_ROOT_RESTORE(js, root_mark);
12680 return p;
12681}
12682
12683static ant_value_t builtin_Promise_all_resolve_handler(ant_t *js, ant_value_t *args, int nargs) {
12684 ant_value_t me = js->current_func;
12685 ant_value_t tracker = js_get(js, me, "tracker");
12686 ant_value_t index_val = js_get(js, me, "index");
12687
12688 int index = (int)tod(index_val);
12689 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
12690
12691 ant_value_t results = js_get(js, tracker, "results");
12692 arr_set(js, results, (ant_offset_t)index, value);
12693
12694 ant_value_t remaining_val = js_get(js, tracker, "remaining");
12695 int remaining = (int)tod(remaining_val) - 1;
12696 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)remaining));
12697
12698 if (remaining == 0) {
12699 ant_value_t result_promise = get_slot(tracker, SLOT_DATA);
12700 js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results)));
12701 }
12702
12703 return js_mkundef();
12704}
12705
12706static ant_value_t builtin_Promise_all_reject_handler(ant_t *js, ant_value_t *args, int nargs) {
12707 ant_value_t me = js->current_func;
12708 ant_value_t tracker = js_get(js, me, "tracker");
12709 ant_value_t result_promise = get_slot(tracker, SLOT_DATA);
12710
12711 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef();
12712 js_reject_promise(js, result_promise, reason);
12713
12714 return js_mkundef();
12715}
12716
12717static ant_value_t promise_all_settled_make_result(ant_t *js, bool fulfilled, ant_value_t value) {
12718 ant_value_t result = mkobj(js, 0);
12719 if (is_err(result)) return result;
12720
12721 js_setprop(
12722 js, result,
12723 js_mkstr(js, "status", 6),
12724 js_mkstr(js, fulfilled ? "fulfilled" : "rejected", fulfilled ? 9 : 8)
12725 );
12726
12727 js_setprop(
12728 js, result,
12729 js_mkstr(js, fulfilled ? "value" : "reason", fulfilled ? 5 : 6), value
12730 );
12731
12732 return result;
12733}
12734
12735static ant_value_t promise_all_settled_store_result(
12736 ant_t *js,
12737 ant_value_t tracker,
12738 int index,
12739 bool fulfilled,
12740 ant_value_t value
12741) {
12742 ant_value_t results = js_get(js, tracker, "results");
12743 ant_value_t result = promise_all_settled_make_result(js, fulfilled, value);
12744 ant_value_t remaining_val = 0;
12745 int remaining = 0;
12746
12747 if (is_err(result)) return result;
12748 arr_set(js, results, (ant_offset_t)index, result);
12749
12750 remaining_val = js_get(js, tracker, "remaining");
12751 remaining = (int)tod(remaining_val) - 1;
12752 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)remaining));
12753
12754 if (remaining == 0) {
12755 ant_value_t result_promise = get_slot(tracker, SLOT_DATA);
12756 js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results)));
12757 }
12758
12759 return js_mkundef();
12760}
12761
12762static ant_value_t builtin_Promise_allSettled_resolve_handler(ant_t *js, ant_value_t *args, int nargs) {
12763 ant_value_t me = js->current_func;
12764 ant_value_t tracker = js_get(js, me, "tracker");
12765 ant_value_t index_val = js_get(js, me, "index");
12766 int index = (int)tod(index_val);
12767 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
12768
12769 return promise_all_settled_store_result(js, tracker, index, true, value);
12770}
12771
12772static ant_value_t builtin_Promise_allSettled_reject_handler(ant_t *js, ant_value_t *args, int nargs) {
12773 ant_value_t me = js->current_func;
12774 ant_value_t tracker = js_get(js, me, "tracker");
12775 ant_value_t index_val = js_get(js, me, "index");
12776 int index = (int)tod(index_val);
12777 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef();
12778
12779 return promise_all_settled_store_result(js, tracker, index, false, reason);
12780}
12781
12782typedef struct {
12783 ant_value_t tracker;
12784 int index;
12785} promise_all_iter_ctx_t;
12786
12787// TODO: move Promise combinator bookkeeping off JS-visible properties and into slots/native state
12788static iter_action_t promise_all_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) {
12789 GC_ROOT_SAVE(root_mark, js);
12790 promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx;
12791 ant_value_t item = value;
12792 GC_ROOT_PIN(js, item);
12793
12794 if (vtype(item) != T_PROMISE) {
12795 ant_value_t wrap_args[] = { item };
12796 item = builtin_Promise_resolve(js, wrap_args, 1);
12797 GC_ROOT_PIN(js, item);
12798 }
12799
12800 ant_value_t resolve_obj = mkobj(js, 0);
12801 if (is_err(resolve_obj)) {
12802 *out = resolve_obj;
12803 GC_ROOT_RESTORE(js, root_mark);
12804 return ITER_ERROR;
12805 }
12806 GC_ROOT_PIN(js, resolve_obj);
12807 set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_resolve_handler));
12808 js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)pctx->index));
12809 js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), pctx->tracker);
12810 ant_value_t resolve_fn = js_obj_to_func(resolve_obj);
12811 GC_ROOT_PIN(js, resolve_fn);
12812
12813 ant_value_t reject_obj = mkobj(js, 0);
12814 if (is_err(reject_obj)) {
12815 *out = reject_obj;
12816 GC_ROOT_RESTORE(js, root_mark);
12817 return ITER_ERROR;
12818 }
12819 GC_ROOT_PIN(js, reject_obj);
12820 set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_reject_handler));
12821 js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), pctx->tracker);
12822 ant_value_t reject_fn = js_obj_to_func(reject_obj);
12823 GC_ROOT_PIN(js, reject_fn);
12824
12825 ant_value_t then_args[] = { resolve_fn, reject_fn };
12826 ant_value_t saved_this = js->this_val;
12827 GC_ROOT_PIN(js, saved_this);
12828 js->this_val = item;
12829 ant_value_t then_result = builtin_promise_then(js, then_args, 2);
12830 js->this_val = saved_this;
12831 if (is_err(then_result)) {
12832 *out = then_result;
12833 GC_ROOT_RESTORE(js, root_mark);
12834 return ITER_ERROR;
12835 }
12836
12837 pctx->index++;
12838 GC_ROOT_RESTORE(js, root_mark);
12839 return ITER_CONTINUE;
12840}
12841
12842static iter_action_t promise_all_settled_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) {
12843 GC_ROOT_SAVE(root_mark, js);
12844 promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx;
12845 ant_value_t item = value;
12846 GC_ROOT_PIN(js, item);
12847
12848 if (vtype(item) != T_PROMISE) {
12849 ant_value_t wrap_args[] = { item };
12850 item = builtin_Promise_resolve(js, wrap_args, 1);
12851 GC_ROOT_PIN(js, item);
12852 }
12853
12854 ant_value_t resolve_obj = mkobj(js, 0);
12855 if (is_err(resolve_obj)) {
12856 *out = resolve_obj;
12857 GC_ROOT_RESTORE(js, root_mark);
12858 return ITER_ERROR;
12859 }
12860 GC_ROOT_PIN(js, resolve_obj);
12861 set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_allSettled_resolve_handler));
12862 js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)pctx->index));
12863 js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), pctx->tracker);
12864 ant_value_t resolve_fn = js_obj_to_func(resolve_obj);
12865 GC_ROOT_PIN(js, resolve_fn);
12866
12867 ant_value_t reject_obj = mkobj(js, 0);
12868 if (is_err(reject_obj)) {
12869 *out = reject_obj;
12870 GC_ROOT_RESTORE(js, root_mark);
12871 return ITER_ERROR;
12872 }
12873 GC_ROOT_PIN(js, reject_obj);
12874 set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_allSettled_reject_handler));
12875 js_setprop(js, reject_obj, js_mkstr(js, "index", 5), tov((double)pctx->index));
12876 js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), pctx->tracker);
12877 ant_value_t reject_fn = js_obj_to_func(reject_obj);
12878 GC_ROOT_PIN(js, reject_fn);
12879
12880 ant_value_t then_args[] = { resolve_fn, reject_fn };
12881 ant_value_t saved_this = js->this_val;
12882 GC_ROOT_PIN(js, saved_this);
12883 js->this_val = item;
12884 ant_value_t then_result = builtin_promise_then(js, then_args, 2);
12885 js->this_val = saved_this;
12886 if (is_err(then_result)) {
12887 *out = then_result;
12888 GC_ROOT_RESTORE(js, root_mark);
12889 return ITER_ERROR;
12890 }
12891
12892 pctx->index++;
12893 GC_ROOT_RESTORE(js, root_mark);
12894 return ITER_CONTINUE;
12895}
12896
12897static ant_value_t builtin_Promise_all(ant_t *js, ant_value_t *args, int nargs) {
12898 if (nargs < 1) return js_mkerr(js, "Promise.all requires an iterable");
12899
12900 GC_ROOT_SAVE(root_mark, js);
12901 ant_value_t iterable = args[0];
12902 GC_ROOT_PIN(js, iterable);
12903 uint8_t t = vtype(iterable);
12904 if (t != T_ARR && t != T_OBJ) {
12905 ant_value_t err = js_mkerr(js, "Promise.all requires an iterable");
12906 GC_ROOT_RESTORE(js, root_mark);
12907 return err;
12908 }
12909
12910 ant_value_t ctor = js->this_val;
12911 GC_ROOT_PIN(js, ctor);
12912 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef();
12913
12914 ant_value_t result_promise = mkpromise_with_ctor(js, ctor);
12915 GC_ROOT_PIN(js, result_promise);
12916 if (is_err(result_promise)) {
12917 GC_ROOT_RESTORE(js, root_mark);
12918 return result_promise;
12919 }
12920
12921 ant_value_t tracker = mkobj(js, 0);
12922 GC_ROOT_PIN(js, tracker);
12923 ant_value_t results = mkarr(js);
12924 GC_ROOT_PIN(js, results);
12925
12926 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov(0.0));
12927 js_setprop(js, tracker, js_mkstr(js, "results", 7), results);
12928 set_slot(tracker, SLOT_DATA, result_promise);
12929
12930 promise_all_iter_ctx_t ctx = { .tracker = tracker, .index = 0 };
12931 ant_value_t iter_result = iter_foreach(js, iterable, promise_all_iter_cb, &ctx);
12932
12933 if (is_err(iter_result)) {
12934 GC_ROOT_RESTORE(js, root_mark);
12935 return iter_result;
12936 }
12937
12938 int len = ctx.index;
12939 {
12940 ant_offset_t doff = get_dense_buf(results);
12941 if (doff) {
12942 if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, results, (ant_offset_t)len);
12943 if (doff) array_len_set(js, results, (ant_offset_t)len);
12944 }
12945 }
12946
12947 if (len == 0) {
12948 js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results)));
12949 GC_ROOT_RESTORE(js, root_mark);
12950 return result_promise;
12951 }
12952
12953 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len));
12954 GC_ROOT_RESTORE(js, root_mark);
12955 return result_promise;
12956}
12957
12958static ant_value_t builtin_Promise_allSettled(ant_t *js, ant_value_t *args, int nargs) {
12959 if (nargs < 1) return js_mkerr(js, "Promise.allSettled requires an iterable");
12960
12961 GC_ROOT_SAVE(root_mark, js);
12962 ant_value_t iterable = args[0];
12963 GC_ROOT_PIN(js, iterable);
12964 uint8_t t = vtype(iterable);
12965 if (t != T_ARR && t != T_OBJ) {
12966 ant_value_t err = js_mkerr(js, "Promise.allSettled requires an iterable");
12967 GC_ROOT_RESTORE(js, root_mark);
12968 return err;
12969 }
12970
12971 ant_value_t ctor = js->this_val;
12972 GC_ROOT_PIN(js, ctor);
12973 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef();
12974
12975 ant_value_t result_promise = mkpromise_with_ctor(js, ctor);
12976 GC_ROOT_PIN(js, result_promise);
12977 if (is_err(result_promise)) {
12978 GC_ROOT_RESTORE(js, root_mark);
12979 return result_promise;
12980 }
12981
12982 ant_value_t tracker = mkobj(js, 0);
12983 GC_ROOT_PIN(js, tracker);
12984 ant_value_t results = mkarr(js);
12985 GC_ROOT_PIN(js, results);
12986
12987 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov(0.0));
12988 js_setprop(js, tracker, js_mkstr(js, "results", 7), results);
12989 set_slot(tracker, SLOT_DATA, result_promise);
12990
12991 promise_all_iter_ctx_t ctx = { .tracker = tracker, .index = 0 };
12992 ant_value_t iter_result = iter_foreach(js, iterable, promise_all_settled_iter_cb, &ctx);
12993
12994 if (is_err(iter_result)) {
12995 GC_ROOT_RESTORE(js, root_mark);
12996 return iter_result;
12997 }
12998
12999 int len = ctx.index;
13000 ant_offset_t doff = get_dense_buf(results);
13001 if (doff) {
13002 if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, results, (ant_offset_t)len);
13003 if (doff) array_len_set(js, results, (ant_offset_t)len);
13004 }
13005
13006 if (len == 0) {
13007 js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results)));
13008 GC_ROOT_RESTORE(js, root_mark);
13009 return result_promise;
13010 }
13011
13012 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len));
13013 GC_ROOT_RESTORE(js, root_mark);
13014 return result_promise;
13015}
13016
13017typedef struct {
13018 ant_value_t result_promise;
13019 ant_value_t resolve_fn;
13020 ant_value_t reject_fn;
13021 bool settled;
13022} promise_race_iter_ctx_t;
13023
13024static iter_action_t promise_race_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) {
13025 GC_ROOT_SAVE(root_mark, js);
13026 promise_race_iter_ctx_t *pctx = (promise_race_iter_ctx_t *)ctx;
13027 ant_value_t item = value;
13028 GC_ROOT_PIN(js, item);
13029
13030 if (vtype(item) != T_PROMISE) {
13031 js_resolve_promise(js, pctx->result_promise, item);
13032 pctx->settled = true;
13033 GC_ROOT_RESTORE(js, root_mark);
13034 return ITER_BREAK;
13035 }
13036
13037 ant_promise_state_t *pd = get_promise_data(js, item, false);
13038 if (pd) {
13039 if (pd->state == 1) {
13040 js_resolve_promise(js, pctx->result_promise, pd->value);
13041 pctx->settled = true;
13042 GC_ROOT_RESTORE(js, root_mark);
13043 return ITER_BREAK;
13044 } else if (pd->state == 2) {
13045 js_reject_promise(js, pctx->result_promise, pd->value);
13046 pctx->settled = true;
13047 GC_ROOT_RESTORE(js, root_mark);
13048 return ITER_BREAK;
13049 }}
13050
13051 ant_value_t then_args[] = { pctx->resolve_fn, pctx->reject_fn };
13052 ant_value_t saved_this = js->this_val;
13053 GC_ROOT_PIN(js, saved_this);
13054 js->this_val = item;
13055 ant_value_t then_result = builtin_promise_then(js, then_args, 2);
13056 js->this_val = saved_this;
13057 if (is_err(then_result)) {
13058 *out = then_result;
13059 GC_ROOT_RESTORE(js, root_mark);
13060 return ITER_ERROR;
13061 }
13062
13063 GC_ROOT_RESTORE(js, root_mark);
13064 return ITER_CONTINUE;
13065}
13066
13067static ant_value_t builtin_Promise_race(ant_t *js, ant_value_t *args, int nargs) {
13068 if (nargs < 1) return js_mkerr(js, "Promise.race requires an iterable");
13069
13070 GC_ROOT_SAVE(root_mark, js);
13071 ant_value_t iterable = args[0];
13072 GC_ROOT_PIN(js, iterable);
13073 uint8_t t = vtype(iterable);
13074 if (t != T_ARR && t != T_OBJ) {
13075 ant_value_t err = js_mkerr(js, "Promise.race requires an iterable");
13076 GC_ROOT_RESTORE(js, root_mark);
13077 return err;
13078 }
13079
13080 ant_value_t ctor = js->this_val;
13081 GC_ROOT_PIN(js, ctor);
13082 if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef();
13083 ant_value_t result_promise = mkpromise_with_ctor(js, ctor);
13084 GC_ROOT_PIN(js, result_promise);
13085 if (is_err(result_promise)) {
13086 GC_ROOT_RESTORE(js, root_mark);
13087 return result_promise;
13088 }
13089
13090 ant_value_t resolve_fn = make_data_cfunc(js, result_promise, builtin_resolve_internal);
13091 GC_ROOT_PIN(js, resolve_fn);
13092 if (is_err(resolve_fn)) {
13093 GC_ROOT_RESTORE(js, root_mark);
13094 return resolve_fn;
13095 }
13096
13097 ant_value_t reject_fn = make_data_cfunc(js, result_promise, builtin_reject_internal);
13098 GC_ROOT_PIN(js, reject_fn);
13099 if (is_err(reject_fn)) {
13100 GC_ROOT_RESTORE(js, root_mark);
13101 return reject_fn;
13102 }
13103
13104 promise_race_iter_ctx_t ctx = {
13105 .result_promise = result_promise,
13106 .resolve_fn = resolve_fn,
13107 .reject_fn = reject_fn,
13108 .settled = false
13109 };
13110
13111 ant_value_t iter_result = iter_foreach(js, iterable, promise_race_iter_cb, &ctx);
13112 if (is_err(iter_result)) {
13113 GC_ROOT_RESTORE(js, root_mark);
13114 return iter_result;
13115 }
13116
13117 GC_ROOT_RESTORE(js, root_mark);
13118 return result_promise;
13119}
13120
13121static ant_value_t mk_aggregate_error(ant_t *js, ant_value_t errors) {
13122 GC_ROOT_SAVE(root_mark, js);
13123 GC_ROOT_PIN(js, errors);
13124 ant_value_t args[] = { errors, js_mkstr(js, "All promises were rejected", 26) };
13125 ant_offset_t off = lkp(js, js_glob(js), "AggregateError", 14);
13126 ant_value_t ctor = off ? propref_load(js, off) : js_mkundef();
13127 GC_ROOT_PIN(js, ctor);
13128 ant_value_t ret = sv_vm_call(js->vm, js, ctor, js_mkundef(), args, 2, NULL, false);
13129 GC_ROOT_RESTORE(js, root_mark);
13130 return ret;
13131}
13132
13133static bool promise_any_try_resolve(ant_t *js, ant_value_t tracker, ant_value_t value) {
13134 if (js_truthy(js, js_get(js, tracker, "resolved"))) return false;
13135 js_set(js, tracker, "resolved", js_true);
13136 js_resolve_promise(js, get_slot(tracker, SLOT_DATA), value);
13137 return true;
13138}
13139
13140static void promise_any_record_rejection(ant_t *js, ant_value_t tracker, int index, ant_value_t reason) {
13141 ant_value_t errors = js_get(js, tracker, "errors");
13142 arr_set(js, errors, (ant_offset_t)index, reason);
13143
13144 int remaining = (int)tod(js_get(js, tracker, "remaining")) - 1;
13145 js_set(js, tracker, "remaining", tov((double)remaining));
13146
13147 if (remaining == 0) js_reject_promise(js, get_slot(tracker, SLOT_DATA), mk_aggregate_error(js, errors));
13148}
13149
13150static ant_value_t builtin_Promise_any_resolve_handler(ant_t *js, ant_value_t *args, int nargs) {
13151 ant_value_t tracker = js_get(js, js->this_val, "tracker");
13152 promise_any_try_resolve(js, tracker, nargs > 0 ? args[0] : js_mkundef());
13153 return js_mkundef();
13154}
13155
13156static ant_value_t builtin_Promise_any_reject_handler(ant_t *js, ant_value_t *args, int nargs) {
13157 ant_value_t tracker = js_get(js, js->this_val, "tracker");
13158 if (js_truthy(js, js_get(js, tracker, "resolved"))) return js_mkundef();
13159
13160 int index = (int)tod(js_get(js, js->this_val, "index"));
13161 promise_any_record_rejection(js, tracker, index, nargs > 0 ? args[0] : js_mkundef());
13162 return js_mkundef();
13163}
13164
13165static ant_value_t builtin_Promise_any(ant_t *js, ant_value_t *args, int nargs) {
13166 if (nargs < 1) return js_mkerr(js, "Promise.any requires an array");
13167
13168 GC_ROOT_SAVE(root_mark, js);
13169 ant_value_t arr = args[0];
13170 GC_ROOT_PIN(js, arr);
13171 if (vtype(arr) != T_ARR) {
13172 ant_value_t err = js_mkerr(js, "Promise.any requires an array");
13173 GC_ROOT_RESTORE(js, root_mark);
13174 return err;
13175 }
13176
13177 int len = (int)get_array_length(js, arr);
13178
13179 if (len == 0) {
13180 ant_value_t reject_args[] = { mk_aggregate_error(js, mkarr(js)) };
13181 ant_value_t ret = builtin_Promise_reject(js, reject_args, 1);
13182 GC_ROOT_RESTORE(js, root_mark);
13183 return ret;
13184 }
13185
13186 ant_value_t result_promise = js_mkpromise(js);
13187 GC_ROOT_PIN(js, result_promise);
13188 ant_value_t tracker = mkobj(js, 0);
13189 GC_ROOT_PIN(js, tracker);
13190 ant_value_t errors = mkarr(js);
13191 GC_ROOT_PIN(js, errors);
13192
13193 set_slot(tracker, SLOT_DATA, result_promise);
13194
13195 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len));
13196 js_setprop(js, tracker, js_mkstr(js, "errors", 6), errors);
13197 js_setprop(js, tracker, js_mkstr(js, "resolved", 8), js_false);
13198
13199 {
13200 ant_offset_t doff = get_dense_buf(errors);
13201 if (doff) {
13202 if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, errors, (ant_offset_t)len);
13203 if (doff) array_len_set(js, errors, (ant_offset_t)len);
13204 }
13205 }
13206
13207 for (int i = 0; i < len; i++) {
13208 ant_value_t item = arr_get(js, arr, (ant_offset_t)i);
13209 GC_ROOT_PIN(js, item);
13210 if (vtype(item) != T_PROMISE) {
13211 promise_any_try_resolve(js, tracker, item);
13212 GC_ROOT_RESTORE(js, root_mark);
13213 return result_promise;
13214 }
13215
13216 ant_promise_state_t *pd = get_promise_data(js, item, false);
13217 if (pd) {
13218 js_mark_promise_rejection_handled_chain(js, item);
13219 if (pd->state == 1) {
13220 promise_any_try_resolve(js, tracker, pd->value);
13221 GC_ROOT_RESTORE(js, root_mark);
13222 return result_promise;
13223 } else if (pd->state == 2) {
13224 promise_any_record_rejection(js, tracker, i, pd->value);
13225 continue;
13226 }
13227 }
13228
13229 ant_value_t resolve_obj = mkobj(js, 0);
13230 if (is_err(resolve_obj)) {
13231 GC_ROOT_RESTORE(js, root_mark);
13232 return resolve_obj;
13233 }
13234 GC_ROOT_PIN(js, resolve_obj);
13235 set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_resolve_handler));
13236 js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), tracker);
13237
13238 ant_value_t reject_obj = mkobj(js, 0);
13239 if (is_err(reject_obj)) {
13240 GC_ROOT_RESTORE(js, root_mark);
13241 return reject_obj;
13242 }
13243 GC_ROOT_PIN(js, reject_obj);
13244 set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_reject_handler));
13245 js_setprop(js, reject_obj, js_mkstr(js, "index", 5), tov((double)i));
13246 js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), tracker);
13247
13248 ant_value_t resolve_fn = js_obj_to_func(resolve_obj);
13249 GC_ROOT_PIN(js, resolve_fn);
13250 ant_value_t reject_fn = js_obj_to_func(reject_obj);
13251 GC_ROOT_PIN(js, reject_fn);
13252 ant_value_t then_args[] = { resolve_fn, reject_fn };
13253 ant_value_t saved_this = js->this_val;
13254 GC_ROOT_PIN(js, saved_this);
13255 js->this_val = item;
13256 ant_value_t then_result = builtin_promise_then(js, then_args, 2);
13257 js->this_val = saved_this;
13258 if (is_err(then_result)) {
13259 GC_ROOT_RESTORE(js, root_mark);
13260 return then_result;
13261 }
13262 }
13263
13264 GC_ROOT_RESTORE(js, root_mark);
13265 return result_promise;
13266}
13267
13268static ant_value_t handle_proxy_instanceof(ant_t *js, ant_value_t l, ant_value_t r, uint8_t ltype) {
13269 ant_value_t target = proxy_read_target(js, r);
13270 uint8_t ttype = vtype(target);
13271
13272 if (ttype != T_FUNC && ttype != T_CFUNC) {
13273 return js_mkerr_typed(js, JS_ERR_TYPE, "Right-hand side of 'instanceof' is not callable");
13274 }
13275
13276 {
13277 ant_value_t has_instance = js_get_sym(js, r, get_hasInstance_sym());
13278 if (is_err(has_instance)) return has_instance;
13279 uint8_t hit = vtype(has_instance);
13280 if (hit == T_FUNC || hit == T_CFUNC) {
13281 ant_value_t args[1] = { l };
13282 ant_value_t result = sv_vm_call(js->vm, js, has_instance, r, args, 1, NULL, false);
13283 if (is_err(result)) return result;
13284 return js_bool(js_truthy(js, result));
13285 }
13286 if (hit != T_UNDEF && hit != T_NULL) {
13287 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable");
13288 }
13289 }
13290
13291 ant_value_t proto_val = proxy_get(js, r, "prototype", 9);
13292 uint8_t pt = vtype(proto_val);
13293
13294 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) {
13295 return mkval(T_BOOL, 0);
13296 }
13297
13298 if (ltype != T_OBJ && ltype != T_ARR && ltype != T_FUNC && ltype != T_PROMISE && ltype != T_GENERATOR) {
13299 return mkval(T_BOOL, 0);
13300 }
13301
13302 ant_value_t current = get_proto(js, l);
13303 return mkval(T_BOOL, proto_chain_contains_cycle_safe(js, current, proto_val) ? 1 : 0);
13304}
13305
13306static ant_value_t handle_cfunc_instanceof(ant_value_t l, ant_value_t r, uint8_t ltype) {
13307 ant_value_t (*fn)(ant_t *, ant_value_t *, int) = js_as_cfunc(r);
13308
13309 if (fn == builtin_Object) return mkval(T_BOOL, ltype == T_OBJ ? 1 : 0);
13310 if (fn == builtin_Function) return mkval(T_BOOL, (ltype == T_FUNC || ltype == T_CFUNC) ? 1 : 0);
13311 if (fn == builtin_String) return mkval(T_BOOL, ltype == T_STR ? 1 : 0);
13312 if (fn == builtin_Number) return mkval(T_BOOL, ltype == T_NUM ? 1 : 0);
13313 if (fn == builtin_Boolean) return mkval(T_BOOL, ltype == T_BOOL ? 1 : 0);
13314 if (fn == builtin_Array) return mkval(T_BOOL, ltype == T_ARR ? 1 : 0);
13315 if (fn == builtin_Promise) return mkval(T_BOOL, ltype == T_PROMISE ? 1 : 0);
13316
13317 return mkval(T_BOOL, 0);
13318}
13319
13320static bool proto_chain_contains_cycle_safe(ant_t *js, ant_value_t start, ant_value_t target) {
13321 if (!is_object_type(start) || !is_object_type(target)) return false;
13322
13323 bool found = false;
13324 ant_value_t slow = start;
13325 ant_value_t fast = start;
13326
13327 while (is_object_type(slow)) {
13328 if (same_object_identity(slow, target)) {
13329 found = true;
13330 break;
13331 }
13332 slow = get_proto(js, slow);
13333
13334 if (is_object_type(fast)) fast = get_proto(js, fast);
13335 if (is_object_type(fast)) fast = get_proto(js, fast);
13336
13337 if (
13338 is_object_type(slow) &&
13339 is_object_type(fast) &&
13340 same_object_identity(slow, fast)
13341 ) break;
13342 }
13343
13344 return found;
13345}
13346
13347static ant_value_t walk_prototype_chain(ant_t *js, ant_value_t l, ant_value_t ctor_proto) {
13348 ant_value_t current = get_proto(js, l);
13349 return mkval(T_BOOL, proto_chain_contains_cycle_safe(js, current, ctor_proto) ? 1 : 0);
13350}
13351
13352static inline ant_object_t *cached_function_proto_obj(ant_t *js) {
13353 static ant_object_t *cached = NULL;
13354 if (cached) return cached;
13355 ant_value_t proto = get_ctor_proto(js, "Function", 8);
13356 if (!is_object_type(proto)) return NULL;
13357 cached = js_obj_ptr(js_as_obj(proto));
13358 return cached;
13359}
13360
13361ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r) {
13362 uint8_t ltype = vtype(l);
13363 uint8_t rtype = vtype(r);
13364
13365 if (rtype != T_FUNC && rtype != T_CFUNC) {
13366 if (is_proxy(r)) return handle_proxy_instanceof(js, l, r, ltype);
13367 return js_mkerr_typed(js, JS_ERR_TYPE, "Right-hand side of 'instanceof' is not callable");
13368 }
13369
13370 if (rtype == T_CFUNC) {
13371 return handle_cfunc_instanceof(l, r, ltype);
13372 }
13373
13374 ant_value_t func_obj = js_func_obj(r);
13375 ant_offset_t has_instance_sym_off = (ant_offset_t)vdata(get_hasInstance_sym());
13376 bool use_slow_has_instance = false;
13377 ant_offset_t own_has_instance = lkp_sym(js, func_obj, has_instance_sym_off);
13378 if (own_has_instance != 0) {
13379 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, own_has_instance);
13380 if (prop_meta && (prop_meta->has_getter || prop_meta->has_setter)) {
13381 use_slow_has_instance = true;
13382 } else {
13383 ant_value_t has_instance = propref_load(js, own_has_instance);
13384 uint8_t hit = vtype(has_instance);
13385 if (hit == T_FUNC || hit == T_CFUNC) {
13386 ant_value_t args[1] = { l };
13387 ant_value_t result = sv_vm_call(js->vm, js, has_instance, r, args, 1, NULL, false);
13388 if (is_err(result)) return result;
13389 return js_bool(js_truthy(js, result));
13390 }
13391 if (hit != T_UNDEF && hit != T_NULL) {
13392 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable");
13393 }
13394 }
13395 } else {
13396 ant_object_t *func_proto_ptr = cached_function_proto_obj(js);
13397 if (func_proto_ptr && func_proto_ptr->shape &&
13398 ant_shape_lookup_symbol(func_proto_ptr->shape, has_instance_sym_off) >= 0) {
13399 use_slow_has_instance = true;
13400 } else if (func_proto_ptr && func_proto_ptr->is_exotic) {
13401 ant_value_t func_proto_obj = mkval(T_OBJ, (uintptr_t)func_proto_ptr);
13402 if (lookup_sym_descriptor(func_proto_obj, has_instance_sym_off))
13403 use_slow_has_instance = true;
13404 }
13405 }
13406
13407 if (use_slow_has_instance) {
13408 ant_value_t has_instance = js_get_sym(js, r, get_hasInstance_sym());
13409 if (is_err(has_instance)) return has_instance;
13410 uint8_t hit = vtype(has_instance);
13411 if (hit == T_FUNC || hit == T_CFUNC) {
13412 ant_value_t args[1] = { l };
13413 ant_value_t result = sv_vm_call(js->vm, js, has_instance, r, args, 1, NULL, false);
13414 if (is_err(result)) return result;
13415 return js_bool(js_truthy(js, result));
13416 }
13417 if (hit != T_UNDEF && hit != T_NULL) {
13418 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable");
13419 }
13420 }
13421
13422 ant_offset_t proto_off = lkp_interned(js, func_obj, js->intern.prototype, 9);
13423 if (proto_off == 0) return mkval(T_BOOL, 0);
13424
13425 ant_value_t ctor_proto = propref_load(js, proto_off);
13426 uint8_t pt = vtype(ctor_proto);
13427 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) return mkval(T_BOOL, 0);
13428
13429 if (ltype == T_STR || ltype == T_NUM || ltype == T_BOOL) {
13430 ant_value_t type_proto = get_prototype_for_type(js, ltype);
13431 return mkval(T_BOOL, vdata(ctor_proto) == vdata(type_proto) ? 1 : 0);
13432 }
13433
13434 if (ltype != T_OBJ && ltype != T_ARR && ltype != T_FUNC && ltype != T_PROMISE && ltype != T_GENERATOR) {
13435 return mkval(T_BOOL, 0);
13436 }
13437
13438 return walk_prototype_chain(js, l, ctor_proto);
13439}
13440
13441ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r) {
13442 ant_offset_t prop_len;
13443 const char *prop_name;
13444 char num_buf[32];
13445
13446 ant_value_t key = js_to_primitive(js, l, 1);
13447 if (is_err(key)) return key;
13448
13449 bool is_sym = (vtype(key) == T_SYMBOL);
13450
13451 if (is_sym) {
13452 const char *d = js_sym_desc(key);
13453 prop_name = d ? d : "symbol";
13454 prop_len = (ant_offset_t)strlen(prop_name);
13455 } else if (vtype(key) == T_NUM) {
13456 prop_len = (ant_offset_t)strnum(key, num_buf, sizeof(num_buf));
13457 prop_name = num_buf;
13458 } else {
13459 ant_value_t key_str = js_tostring_val(js, key);
13460 if (is_err(key_str)) return key_str;
13461 ant_offset_t prop_off = vstr(js, key_str, &prop_len);
13462 prop_name = (char *)(uintptr_t)(prop_off);
13463 }
13464
13465 if (!is_object_type(r)) {
13466 if (vtype(r) == T_CFUNC) return mkval(T_BOOL, 0);
13467 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot use 'in' operator to search for '%.*s' in non-object", (int)prop_len, prop_name);
13468 }
13469
13470 if (is_proxy(r)) {
13471 ant_value_t result = is_sym ? proxy_has_val(js, r, key) : proxy_has(js, r, prop_name, prop_len);
13472 if (is_err(result)) return result;
13473 return js_bool(js_truthy(js, result));
13474 }
13475
13476 if (!is_sym && vtype(r) == T_ARR) {
13477 unsigned long idx;
13478 ant_offset_t arr_len = get_array_length(js, r);
13479 if (parse_array_index(prop_name, prop_len, arr_len, &idx)) return mkval(T_BOOL, arr_has(js, r, (ant_offset_t)idx) ? 1 : 0);
13480 if (is_length_key(prop_name, prop_len)) return mkval(T_BOOL, 1);
13481 }
13482
13483 ant_offset_t found = is_sym ? lkp_sym_proto(js, r, (ant_offset_t)vdata(key)) : lkp_proto(js, r, prop_name, prop_len);
13484 return mkval(T_BOOL, found != 0 ? 1 : 0);
13485}
13486
13487static ant_value_t builtin_import_tla_resolve(ant_t *js, ant_value_t *args, int nargs) {
13488 ant_value_t me = js->current_func;
13489 return get_slot(me, SLOT_DATA);
13490}
13491
13492static inline bool js_has_module_filename(const char *filename) {
13493 return filename && filename[0];
13494}
13495
13496static ant_value_t js_get_import_func(ant_t *js) {
13497 ant_value_t glob = js_glob(js);
13498 ant_offset_t import_off = lkp(js, glob, "import", 6);
13499 if (import_off == 0) return js_mkundef();
13500 return propref_load(js, import_off);
13501}
13502
13503static ant_value_t js_get_module_ctx_import_meta(ant_t *js, ant_value_t module_ctx) {
13504 if (!is_object_type(module_ctx)) return js_mkundef();
13505 return js_get(js, module_ctx, "meta");
13506}
13507
13508static const char *js_get_module_ctx_filename(ant_t *js, ant_value_t module_ctx) {
13509 if (!is_object_type(module_ctx)) return NULL;
13510
13511 ant_value_t filename = js_get(js, module_ctx, "filename");
13512 if (vtype(filename) != T_STR) return NULL;
13513 return js_getstr(js, filename, NULL);
13514}
13515
13516static ant_value_t js_module_ctx_from_func(ant_value_t func) {
13517 if (vtype(func) != T_FUNC) return js_mkundef();
13518 ant_value_t module_ctx = get_slot(js_func_obj(func), SLOT_MODULE_CTX);
13519 return is_object_type(module_ctx) ? module_ctx : js_mkundef();
13520}
13521
13522static ant_value_t js_get_execution_module_ctx(ant_t *js) {
13523 sv_vm_t *vm = sv_vm_get_active(js);
13524 if (vm && vm->fp >= 0) {
13525 ant_value_t module_ctx = js_module_ctx_from_func(vm->frames[vm->fp].callee);
13526 if (is_object_type(module_ctx)) return module_ctx;
13527 }
13528
13529 return js_module_eval_active_ctx(js);
13530}
13531
13532static ant_value_t js_get_import_owner_module_ctx(ant_t *js) {
13533 ant_value_t module_ctx = js_module_ctx_from_func(js_getcurrentfunc(js));
13534 if (is_object_type(module_ctx)) return module_ctx;
13535 return js_get_execution_module_ctx(js);
13536}
13537
13538static const char *js_get_execution_module_filename(ant_t *js) {
13539 const char *filename = js_get_module_ctx_filename(js, js_get_execution_module_ctx(js));
13540 if (js_has_module_filename(filename)) return filename;
13541 return js->filename;
13542}
13543
13544ant_value_t js_builtin_import(ant_t *js, ant_value_t *args, int nargs) {
13545 if (nargs < 1) return js_mkerr(js, "import() requires a string specifier");
13546
13547 ant_value_t module_ctx = js_get_import_owner_module_ctx(js);
13548 const char *base_path = js_get_module_ctx_filename(js, module_ctx);
13549
13550 if (!js_has_module_filename(base_path))
13551 base_path = js_get_execution_module_filename(js);
13552
13553 ant_value_t tla_promise = js_mkundef();
13554 ant_value_t ns = js_esm_import_dynamic(js, args[0], base_path, &tla_promise);
13555
13556 if (is_err(ns)) return builtin_Promise_reject(js, &ns, 1);
13557
13558 if (vtype(tla_promise) == T_PROMISE) {
13559 ant_value_t resolve_fn = make_data_cfunc(js, ns, builtin_import_tla_resolve);
13560 ant_value_t saved = js->this_val;
13561
13562 js->this_val = tla_promise;
13563 ant_value_t then_args[] = { resolve_fn };
13564
13565 ant_value_t result = builtin_promise_then(js, then_args, 1);
13566 js->this_val = saved;
13567
13568 return result;
13569 }
13570
13571 ant_value_t promise_args[] = { ns };
13572 return builtin_Promise_resolve(js, promise_args, 1);
13573}
13574
13575static ant_value_t js_get_import_meta_prop(ant_t *js) {
13576 ant_value_t import_fn = js_get_import_func(js);
13577 if (vtype(import_fn) != T_FUNC) return js_mkundef();
13578 return js_get(js, js_func_obj(import_fn), "meta");
13579}
13580
13581static void js_set_import_meta_prop(ant_t *js, ant_value_t import_meta) {
13582 ant_value_t import_fn = js_get_import_func(js);
13583 if (vtype(import_fn) != T_FUNC) return;
13584 setprop_cstr(js, js_func_obj(import_fn), "meta", 4, import_meta);
13585}
13586
13587static void js_set_import_module_ctx(ant_t *js, ant_value_t module_ctx) {
13588 if (!is_object_type(module_ctx)) return;
13589 ant_value_t import_fn = js_get_import_func(js);
13590 if (vtype(import_fn) != T_FUNC) return;
13591 js_set_slot_wb(js, js_func_obj(import_fn), SLOT_MODULE_CTX, module_ctx);
13592}
13593
13594static ant_value_t js_get_current_import_meta(ant_t *js) {
13595 ant_value_t import_meta = js_get_module_ctx_import_meta(
13596 js,
13597 js_get_execution_module_ctx(js)
13598 );
13599 if (vtype(import_meta) == T_OBJ) return import_meta;
13600 return js_get_import_meta_prop(js);
13601}
13602
13603static ant_value_t builtin_import_meta_resolve(ant_t *js, ant_value_t *args, int nargs) {
13604 if (nargs < 1) return js_mkerr(js, "import.meta.resolve() requires a string specifier");
13605
13606 const char *base_path = NULL;
13607 ant_value_t import_meta = js_getthis(js);
13608 ant_value_t module_ctx = js_mkundef();
13609
13610 if (is_object_type(import_meta)) module_ctx = js_get_slot(import_meta, SLOT_MODULE_CTX);
13611
13612 if (!is_object_type(module_ctx))
13613 module_ctx = js_get_import_owner_module_ctx(js);
13614
13615 base_path = js_get_module_ctx_filename(js, module_ctx);
13616 if (!js_has_module_filename(base_path))
13617 base_path = js_get_execution_module_filename(js);
13618
13619 return js_esm_resolve_specifier(js, args[0], base_path);
13620}
13621
13622static inline void js_set_import_meta_special_dirname(
13623 ant_t *js,
13624 ant_value_t import_meta,
13625 const char *filename,
13626 bool is_builtin
13627) {
13628 char *filename_copy = strdup(filename);
13629 if (!filename_copy) return;
13630
13631 char *last_slash = strrchr(filename_copy, '/');
13632 char *scheme_end = strstr(filename_copy, "://");
13633
13634 if ((is_builtin && last_slash) || (!is_builtin && last_slash && scheme_end && last_slash > scheme_end + 2)) {
13635 *last_slash = '\0';
13636 ant_value_t dirname_val = js_mkstr(js, filename_copy, strlen(filename_copy));
13637 if (!is_err(dirname_val)) setprop_cstr(js, import_meta, "dirname", 7, dirname_val);
13638 }
13639
13640 free(filename_copy);
13641}
13642
13643static inline void js_set_import_meta_path_dirname(
13644 ant_t *js,
13645 ant_value_t import_meta,
13646 const char *filename
13647) {
13648 char *filename_copy = strdup(filename);
13649 if (!filename_copy) return;
13650
13651 char *dir = dirname(filename_copy);
13652 if (dir) {
13653 ant_value_t dirname_val = js_mkstr(js, dir, strlen(dir));
13654 if (!is_err(dirname_val)) setprop_cstr(js, import_meta, "dirname", 7, dirname_val);
13655 }
13656
13657 free(filename_copy);
13658}
13659
13660static ant_value_t js_create_import_meta_for_context(
13661 ant_t *js,
13662 ant_value_t module_ctx,
13663 const char *filename,
13664 bool is_main
13665) {
13666 if (!filename) return js_mkundef();
13667
13668 ant_value_t import_meta = mkobj(js, 0);
13669 if (is_err(import_meta)) return import_meta;
13670
13671 js_set_slot_wb(js, import_meta, SLOT_MODULE_CTX, module_ctx);
13672
13673 bool is_url = esm_is_url(filename);
13674 bool is_builtin = esm_has_builtin_scheme(filename);
13675
13676 ant_value_t url_val = (is_url || is_builtin)
13677 ? js_mkstr(js, filename, strlen(filename))
13678 : js_esm_make_file_url(js, filename);
13679 if (!is_err(url_val)) setprop_cstr(js, import_meta, "url", 3, url_val);
13680
13681 ant_value_t filename_val = js_get(js, module_ctx, "filename");
13682 if (vtype(filename_val) == T_STR)
13683 setprop_cstr(js, import_meta, "filename", 8, filename_val);
13684
13685 if (is_url || is_builtin) js_set_import_meta_special_dirname(js, import_meta, filename, is_builtin);
13686 else js_set_import_meta_path_dirname(js, import_meta, filename);
13687
13688 setprop_cstr(js, import_meta, "main", 4, is_main ? js_true : js_false);
13689
13690 ant_value_t resolve_fn = js_heavy_mkfun(js, builtin_import_meta_resolve, js_mkundef());
13691 if (vtype(resolve_fn) == T_FUNC)
13692 js_set_slot_wb(js, js_func_obj(resolve_fn), SLOT_MODULE_CTX, module_ctx);
13693 setprop_cstr(js, import_meta, "resolve", 7, resolve_fn);
13694
13695 return import_meta;
13696}
13697
13698ant_value_t js_create_module_context(ant_t *js, const char *filename, bool is_main) {
13699 GC_ROOT_SAVE(root_mark, js);
13700 if (!js_has_module_filename(filename)) {
13701 GC_ROOT_RESTORE(js, root_mark);
13702 return js_mkundef();
13703 }
13704
13705 ant_value_t module_ctx = js_mkobj(js);
13706 if (is_err(module_ctx)) {
13707 GC_ROOT_RESTORE(js, root_mark);
13708 return module_ctx;
13709 }
13710 GC_ROOT_PIN(js, module_ctx);
13711
13712 ant_value_t filename_val = js_mkstr(js, filename, strlen(filename));
13713 if (is_err(filename_val)) {
13714 GC_ROOT_RESTORE(js, root_mark);
13715 return filename_val;
13716 }
13717
13718 GC_ROOT_PIN(js, filename_val);
13719 setprop_cstr(js, module_ctx, "filename", 8, filename_val);
13720 setprop_cstr(js, module_ctx, "displayName", 11, filename_val);
13721
13722 ant_value_t import_meta = js_create_import_meta_for_context(js, module_ctx, filename, is_main);
13723 if (is_err(import_meta)) {
13724 GC_ROOT_RESTORE(js, root_mark);
13725 return import_meta;
13726 }
13727
13728 GC_ROOT_PIN(js, import_meta);
13729 setprop_cstr(js, module_ctx, "meta", 4, import_meta);
13730 GC_ROOT_RESTORE(js, root_mark);
13731
13732 return module_ctx;
13733}
13734
13735ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main) {
13736 ant_value_t module_ctx = js_create_module_context(js, filename, is_main);
13737 if (is_err(module_ctx)) return module_ctx;
13738 return js_get_module_ctx_import_meta(js, module_ctx);
13739}
13740
13741ant_value_t js_get_module_import_binding(ant_t *js) {
13742 GC_ROOT_SAVE(root_mark, js);
13743
13744 ant_value_t module_ctx = js_get_execution_module_ctx(js);
13745 ant_value_t import_meta = js_get_module_ctx_import_meta(js, module_ctx);
13746
13747 GC_ROOT_PIN(js, module_ctx);
13748 GC_ROOT_PIN(js, import_meta);
13749
13750 if (!is_object_type(module_ctx) || vtype(import_meta) != T_OBJ) {
13751 GC_ROOT_RESTORE(js, root_mark);
13752 return js_mkundef();
13753 }
13754
13755 ant_value_t import_obj = js_mkobj(js);
13756 if (is_err(import_obj)) {
13757 GC_ROOT_RESTORE(js, root_mark);
13758 return import_obj;
13759 }
13760
13761 GC_ROOT_PIN(js, import_obj);
13762 ant_value_t function_proto = js_get_slot(js_glob(js), SLOT_FUNC_PROTO);
13763
13764 if (vtype(function_proto) == T_UNDEF)
13765 function_proto = js_get_ctor_proto(js, "Function", 8);
13766 GC_ROOT_PIN(js, function_proto);
13767
13768 if (is_object_type(function_proto)) js_set_proto_wb(js, import_obj, function_proto);
13769 set_slot(import_obj, SLOT_CFUNC, js_mkfun(js_builtin_import));
13770
13771 js_set_slot_wb(js, import_obj, SLOT_MODULE_CTX, module_ctx);
13772 setprop_cstr(js, import_obj, "meta", 4, import_meta);
13773
13774 ant_value_t import_fn = js_obj_to_func(import_obj);
13775 GC_ROOT_RESTORE(js, root_mark);
13776
13777 return import_fn;
13778}
13779
13780void js_setup_import_meta(ant_t *js, const char *filename) {
13781 if (!filename) return;
13782
13783 ant_value_t module_ctx = js_create_module_context(js, filename, true);
13784 ant_value_t import_meta = js_get_module_ctx_import_meta(js, module_ctx);
13785 if (is_err(import_meta) || vtype(import_meta) == T_UNDEF) return;
13786
13787 js_set_import_module_ctx(js, module_ctx);
13788 js_set_import_meta_prop(js, import_meta);
13789}
13790
13791void js_module_eval_ctx_push(ant_t *js, ant_module_t *ctx) {
13792 if (!js || !ctx) return;
13793
13794 ctx->prev = js->module;
13795 ctx->prev_import_meta_prop = js_get_import_meta_prop(js);
13796 js->module = ctx;
13797
13798 ant_value_t import_meta = js_get_module_ctx_import_meta(js, ctx->module_ctx);
13799 if (vtype(import_meta) != T_UNDEF) js_set_import_meta_prop(js, import_meta);
13800}
13801
13802void js_module_eval_ctx_pop(ant_t *js, ant_module_t *ctx) {
13803 if (!js || !ctx) return;
13804
13805 if (js->module == ctx) {
13806 js_set_import_meta_prop(js, ctx->prev_import_meta_prop);
13807 js->module = ctx->prev;
13808 }
13809}
13810
13811static ant_proxy_state_t *get_proxy_data(ant_value_t obj) {
13812 if (vtype(obj) != T_OBJ) return NULL;
13813 ant_object_t *ptr = js_obj_ptr(obj);
13814 return ptr ? ant_object_proxy_state(ptr) : NULL;
13815}
13816
13817bool is_proxy(ant_value_t obj) {
13818 ant_object_t *ptr = js_obj_ptr(obj);
13819 if (!ptr || !ptr->is_exotic) return false;
13820 return get_proxy_data(obj) != NULL;
13821}
13822
13823bool js_is_constructor(ant_value_t value) {
13824 ant_value_t slow = value;
13825 ant_value_t fast = value;
13826
13827 while (true) {
13828 uint8_t t = vtype(slow);
13829 if (t == T_FUNC) {
13830 ant_object_t *obj = js_obj_ptr(js_func_obj(slow));
13831 return obj && obj->is_constructor;
13832 }
13833 if (t != T_OBJ) return false;
13834
13835 ant_object_t *obj = js_obj_ptr(slow);
13836 if (!obj) return false;
13837 if (obj->is_constructor) return true;
13838 if (!obj->is_exotic) return false;
13839
13840 ant_proxy_state_t *data = get_proxy_data(slow);
13841 if (!data) return false;
13842 slow = data->target;
13843
13844 for (int i = 0; i < 2; i++) {
13845 if (vtype(fast) != T_OBJ) { fast = js_mknull(); break; }
13846 ant_object_t *fobj = js_obj_ptr(fast);
13847 if (!fobj || !fobj->is_exotic) { fast = js_mknull(); break; }
13848 ant_proxy_state_t *fdata = get_proxy_data(fast);
13849 if (!fdata) { fast = js_mknull(); break; }
13850 fast = fdata->target;
13851 }
13852
13853 if (same_object_identity(slow, fast)) return false;
13854 }
13855}
13856
13857static ant_value_t proxy_read_target(ant_t *js, ant_value_t obj) {
13858 ant_proxy_state_t *data = get_proxy_data(obj);
13859 return data ? data->target : obj;
13860}
13861
13862static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj) {
13863 ant_value_t src = is_proxy(obj) ? proxy_read_target(js, obj) : obj;
13864 if (vtype(src) == T_ARR) return get_array_length(js, src);
13865 ant_offset_t off = lkp_interned(js, src, js->intern.length, 6);
13866 if (off == 0) return 0;
13867 ant_value_t len_val = propref_load(js, off);
13868 return vtype(len_val) == T_NUM ? (ant_offset_t)tod(len_val) : 0;
13869}
13870
13871static ant_value_t proxy_aware_get_elem(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
13872 ant_value_t src = is_proxy(obj) ? proxy_read_target(js, obj) : obj;
13873 ant_offset_t off = lkp(js, src, key, key_len);
13874 return off ? propref_load(js, off) : js_mkundef();
13875}
13876
13877static ant_value_t throw_proxy_error(ant_t *js, const char *message) {
13878 return js_mkerr_typed(js, JS_ERR_TYPE, "%s", message);
13879}
13880
13881static bool proxy_target_is_extensible(ant_value_t obj) {
13882 uint8_t t = vtype(obj);
13883 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return false;
13884
13885 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj));
13886 if (!ptr) return false;
13887 if (ptr->frozen || ptr->sealed) return false;
13888 return ptr->extensible != 0;
13889}
13890
13891static bool proxy_target_prop_is_nonconfig(ant_t *js, ant_value_t target, ant_offset_t prop_off) {
13892 (void)target;
13893 return is_nonconfig_prop(js, prop_off);
13894}
13895
13896static bool proxy_target_prop_is_const(ant_t *js, ant_value_t target, ant_offset_t prop_off) {
13897 (void)target;
13898 return is_const_prop(js, prop_off);
13899}
13900
13901static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) {
13902 ant_proxy_state_t *data = get_proxy_data(proxy);
13903 if (!data) return js_mkundef();
13904 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'get' on a proxy that has been revoked");
13905
13906 ant_value_t target = data->target;
13907 ant_value_t handler = data->handler;
13908
13909 ant_offset_t get_trap_off = vtype(handler) == T_OBJ
13910 ? lkp_interned(js, handler, js->intern.get, 3)
13911 : 0;
13912
13913 if (get_trap_off != 0) {
13914 ant_value_t get_trap = propref_load(js, get_trap_off);
13915 if (vtype(get_trap) == T_FUNC || vtype(get_trap) == T_CFUNC) {
13916 ant_value_t key_val = js_mkstr(js, key, key_len);
13917
13918 ant_value_t args[3] = { target, key_val, proxy };
13919 ant_value_t result = sv_vm_call(js->vm, js, get_trap, js_mkundef(), args, 3, NULL, false);
13920 if (is_err(result)) return result;
13921
13922 ant_offset_t prop_off = lkp(js, target, key, key_len);
13923 if (prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off) &&
13924 proxy_target_prop_is_const(js, target, prop_off)) {
13925 ant_value_t target_value = propref_load(js, prop_off);
13926 if (!strict_eq_values(js, result, target_value))
13927 return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned invalid value for non-configurable, non-writable property");
13928 }
13929
13930 prop_meta_t meta;
13931 bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta);
13932 if (has_meta && !meta.configurable) {
13933 if (!meta.has_getter && !meta.has_setter && !meta.writable && prop_off != 0) {
13934 ant_value_t target_value = propref_load(js, prop_off);
13935 if (!strict_eq_values(js, result, target_value))
13936 return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned invalid value for non-configurable, non-writable property");
13937 }
13938 if ((meta.has_getter || meta.has_setter) && !meta.has_getter && vtype(result) != T_UNDEF)
13939 return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned non-undefined for property with undefined getter");
13940 }
13941
13942 return result;
13943 }
13944 }
13945
13946 char key_buf[256];
13947 size_t len = key_len < sizeof(key_buf) - 1 ? key_len : sizeof(key_buf) - 1;
13948 memcpy(key_buf, key, len);
13949 key_buf[len] = '\0';
13950
13951 ant_offset_t off = lkp(js, target, key_buf, len);
13952 if (off != 0) return propref_load(js, off);
13953
13954 ant_offset_t proto_off = lkp_proto(js, target, key_buf, len);
13955 if (proto_off != 0) return propref_load(js, proto_off);
13956
13957 return js_mkundef();
13958}
13959
13960static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size_t key_len, ant_value_t value) {
13961 ant_proxy_state_t *data = get_proxy_data(proxy);
13962 if (!data) return js_mkundef();
13963 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'set' on a proxy that has been revoked");
13964
13965 ant_value_t target = data->target;
13966 ant_value_t handler = data->handler;
13967
13968 ant_offset_t set_trap_off = vtype(handler) == T_OBJ ? lkp_interned(js, handler, js->intern.set, 3) : 0;
13969 if (set_trap_off != 0) {
13970 ant_value_t set_trap = propref_load(js, set_trap_off);
13971 if (vtype(set_trap) == T_FUNC || vtype(set_trap) == T_CFUNC) {
13972 ant_value_t key_val = js_mkstr(js, key, key_len);
13973 ant_value_t args[4] = { target, key_val, value, proxy };
13974 ant_value_t result = sv_vm_call(js->vm, js, set_trap, js_mkundef(), args, 4, NULL, false);
13975 if (is_err(result)) return result;
13976 if (js_truthy(js, result)) {
13977 ant_offset_t prop_off = lkp(js, target, key, key_len);
13978 if (prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off) &&
13979 proxy_target_prop_is_const(js, target, prop_off)) {
13980 ant_value_t target_value = propref_load(js, prop_off);
13981 if (!strict_eq_values(js, value, target_value))
13982 return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for non-configurable, non-writable property with different value");
13983 }
13984
13985 prop_meta_t meta;
13986 bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta);
13987 if (has_meta && !meta.configurable) {
13988 if (!meta.has_getter && !meta.has_setter && !meta.writable && prop_off != 0) {
13989 ant_value_t target_value = propref_load(js, prop_off);
13990 if (!strict_eq_values(js, value, target_value))
13991 return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for non-configurable, non-writable property with different value");
13992 }
13993 if ((meta.has_getter || meta.has_setter) && !meta.has_setter)
13994 return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for property with undefined setter");
13995 }
13996 }
13997 return js_true;
13998 }
13999 }
14000
14001 ant_value_t key_str = js_mkstr(js, key, key_len);
14002 js_setprop(js, target, key_str, value);
14003 return js_true;
14004}
14005
14006static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) {
14007 ant_proxy_state_t *data = get_proxy_data(proxy);
14008 if (!data) return js_false;
14009 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'has' on a proxy that has been revoked");
14010
14011 ant_value_t target = data->target;
14012 ant_value_t handler = data->handler;
14013
14014 ant_offset_t has_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "has", 3) : 0;
14015 if (has_trap_off != 0) {
14016 ant_value_t has_trap = propref_load(js, has_trap_off);
14017 if (vtype(has_trap) == T_FUNC || vtype(has_trap) == T_CFUNC) {
14018 ant_value_t key_val = js_mkstr(js, key, key_len);
14019 ant_value_t args[2] = { target, key_val };
14020 ant_value_t result = sv_vm_call(js->vm, js, has_trap, js_mkundef(), args, 2, NULL, false);
14021 if (is_err(result)) return result;
14022
14023 if (!js_truthy(js, result)) {
14024 ant_offset_t prop_off = lkp(js, target, key, key_len);
14025 prop_meta_t meta;
14026 bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta);
14027 bool has_own = (prop_off != 0) || has_meta;
14028
14029 if ((prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off)) || (has_meta && !meta.configurable))
14030 return js_mkerr_typed(js, JS_ERR_TYPE, "'has' on proxy: trap returned falsy for non-configurable property");
14031
14032 if (has_own && !proxy_target_is_extensible(target))
14033 return js_mkerr_typed(js, JS_ERR_TYPE, "'has' on proxy: trap returned falsy for existing property on non-extensible target");
14034 }
14035
14036 return result;
14037 }
14038 }
14039
14040 char key_buf[256];
14041 size_t len = key_len < sizeof(key_buf) - 1 ? key_len : sizeof(key_buf) - 1;
14042 memcpy(key_buf, key, len);
14043 key_buf[len] = '\0';
14044
14045 ant_offset_t off = lkp_proto(js, target, key_buf, len);
14046 return js_bool(off != 0);
14047}
14048
14049static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) {
14050 ant_proxy_state_t *data = get_proxy_data(proxy);
14051 if (!data) return js_true;
14052 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'deleteProperty' on a proxy that has been revoked");
14053
14054 ant_value_t target = data->target;
14055 ant_value_t handler = data->handler;
14056
14057 ant_offset_t delete_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "deleteProperty", 14) : 0;
14058 if (delete_trap_off != 0) {
14059 ant_value_t delete_trap = propref_load(js, delete_trap_off);
14060 if (vtype(delete_trap) == T_FUNC || vtype(delete_trap) == T_CFUNC) {
14061 ant_value_t key_val = js_mkstr(js, key, key_len);
14062 ant_value_t args[2] = { target, key_val };
14063 ant_value_t result = sv_vm_call(js->vm, js, delete_trap, js_mkundef(), args, 2, NULL, false);
14064 if (is_err(result)) return result;
14065 if (js_truthy(js, result)) {
14066 ant_offset_t prop_off = lkp(js, target, key, key_len);
14067 if (prop_off != 0 && is_nonconfig_prop(js, prop_off))
14068 return js_mkerr_typed(js, JS_ERR_TYPE, "'deleteProperty' on proxy: trap returned truthy for non-configurable property");
14069 prop_meta_t meta;
14070 if (lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta) && !meta.configurable)
14071 return js_mkerr_typed(js, JS_ERR_TYPE, "'deleteProperty' on proxy: trap returned truthy for non-configurable property");
14072 }
14073 return result;
14074 }
14075 }
14076
14077 ant_value_t key_str = js_mkstr(js, key, key_len);
14078 js_setprop(js, target, key_str, js_mkundef());
14079 return js_true;
14080}
14081
14082static ant_value_t proxy_get_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) {
14083 ant_proxy_state_t *data = get_proxy_data(proxy);
14084 if (!data) return js_mkundef();
14085 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'get' on a proxy that has been revoked");
14086
14087 ant_value_t target = data->target;
14088 ant_value_t handler = data->handler;
14089 ant_value_t prop_key = key_val;
14090
14091 if (vtype(prop_key) != T_SYMBOL) {
14092 if (is_object_type(prop_key)) {
14093 prop_key = js_to_primitive(js, prop_key, 1);
14094 if (is_err(prop_key)) return prop_key;
14095 }
14096
14097 if (vtype(prop_key) != T_SYMBOL) {
14098 prop_key = js_tostring_val(js, prop_key);
14099 if (is_err(prop_key)) return prop_key;
14100 }}
14101
14102 ant_offset_t get_trap_off = vtype(handler) == T_OBJ
14103 ? lkp_interned(js, handler, js->intern.get, 3) : 0;
14104
14105 if (get_trap_off != 0) {
14106 ant_value_t get_trap = propref_load(js, get_trap_off);
14107 if (vtype(get_trap) == T_FUNC || vtype(get_trap) == T_CFUNC) {
14108 ant_value_t args[3] = { target, prop_key, proxy };
14109 return sv_vm_call(js->vm, js, get_trap, js_mkundef(), args, 3, NULL, false);
14110 }}
14111
14112 if (vtype(prop_key) == T_SYMBOL) {
14113 ant_offset_t off = lkp_sym_proto(js, target, (ant_offset_t)vdata(prop_key));
14114 return off != 0 ? propref_load(js, off) : js_mkundef();
14115 }
14116
14117 ant_offset_t key_len = 0;
14118 ant_offset_t key_off = vstr(js, prop_key, &key_len);
14119 const char *key_ptr = (const char *)(uintptr_t)key_off;
14120
14121 return proxy_get(js, proxy, key_ptr, (size_t)key_len);
14122}
14123
14124static ant_value_t proxy_get_prototype_of(ant_t *js, ant_value_t proxy) {
14125 ant_proxy_state_t *data = get_proxy_data(proxy);
14126 if (!data) return js_mknull();
14127
14128 if (data->revoked)
14129 return throw_proxy_error(js, "Cannot perform 'getPrototypeOf' on a proxy that has been revoked");
14130
14131 ant_value_t target = data->target;
14132 ant_value_t handler = data->handler;
14133 ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "getPrototypeOf", 14) : 0;
14134
14135 if (trap_off != 0) {
14136 ant_value_t trap = propref_load(js, trap_off);
14137 if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) {
14138 ant_value_t args[1] = { target };
14139 ant_value_t result = sv_vm_call(js->vm, js, trap, js_mkundef(), args, 1, NULL, false);
14140 if (is_err(result)) return result;
14141 if (is_object_type(result) || vtype(result) == T_NULL) return result;
14142 return js_mkerr_typed(js, JS_ERR_TYPE, "'getPrototypeOf' on proxy: trap returned neither object nor null");
14143 }}
14144
14145 return get_proto(js, target);
14146}
14147
14148static ant_value_t proxy_has_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) {
14149 ant_proxy_state_t *data = get_proxy_data(proxy);
14150 if (!data) return js_false;
14151 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'has' on a proxy that has been revoked");
14152
14153 ant_value_t target = data->target;
14154 ant_value_t handler = data->handler;
14155 ant_value_t prop_key = key_val;
14156
14157 if (vtype(prop_key) != T_SYMBOL) {
14158 if (is_object_type(prop_key)) {
14159 prop_key = js_to_primitive(js, prop_key, 1);
14160 if (is_err(prop_key)) return prop_key;
14161 }
14162
14163 if (vtype(prop_key) != T_SYMBOL) {
14164 prop_key = js_tostring_val(js, prop_key);
14165 if (is_err(prop_key)) return prop_key;
14166 }}
14167
14168 ant_offset_t has_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "has", 3) : 0;
14169 if (has_trap_off != 0) {
14170 ant_value_t has_trap = propref_load(js, has_trap_off);
14171 if (vtype(has_trap) == T_FUNC || vtype(has_trap) == T_CFUNC) {
14172 ant_value_t args[2] = { target, prop_key };
14173 return sv_vm_call(js->vm, js, has_trap, js_mkundef(), args, 2, NULL, false);
14174 }}
14175
14176 if (vtype(prop_key) == T_SYMBOL) {
14177 ant_offset_t off = lkp_sym_proto(js, target, (ant_offset_t)vdata(prop_key));
14178 return js_bool(off != 0);
14179 }
14180
14181 ant_offset_t key_len = 0;
14182 ant_offset_t key_off = vstr(js, prop_key, &key_len);
14183 const char *key_ptr = (const char *)(uintptr_t)key_off;
14184
14185 return proxy_has(js, proxy, key_ptr, (size_t)key_len);
14186}
14187
14188static ant_value_t proxy_get_own_property_descriptor(ant_t *js, ant_value_t proxy, ant_value_t key_val) {
14189 ant_proxy_state_t *data = get_proxy_data(proxy);
14190 if (!data) return js_mkundef();
14191
14192 if (data->revoked)
14193 return throw_proxy_error(js, "Cannot perform 'getOwnPropertyDescriptor' on a proxy that has been revoked");
14194
14195 ant_value_t target = data->target;
14196 ant_value_t handler = data->handler;
14197 ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "getOwnPropertyDescriptor", 24) : 0;
14198
14199 if (trap_off != 0) {
14200 ant_value_t trap = propref_load(js, trap_off);
14201 if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) {
14202 ant_value_t args[2] = { target, key_val };
14203 ant_value_t result = sv_vm_call(js->vm, js, trap, js_mkundef(), args, 2, NULL, false);
14204 if (is_err(result)) return result;
14205 if (vtype(result) == T_UNDEF || vtype(result) == T_OBJ) return result;
14206 return js_mkerr_typed(js, JS_ERR_TYPE, "'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined");
14207 }}
14208
14209 ant_value_t args[2] = { target, key_val };
14210 return builtin_object_getOwnPropertyDescriptor(js, args, 2);
14211}
14212
14213static ant_value_t proxy_has_own(ant_t *js, ant_value_t proxy, ant_value_t key_val) {
14214 ant_value_t desc = proxy_get_own_property_descriptor(js, proxy, key_val);
14215 if (is_err(desc)) return desc;
14216 return js_bool(vtype(desc) != T_UNDEF);
14217}
14218
14219static ant_value_t proxy_define_property(ant_t *js, ant_value_t proxy, ant_value_t key_val, ant_value_t descriptor) {
14220 ant_proxy_state_t *data = get_proxy_data(proxy);
14221 if (!data) return js_false;
14222 if (data->revoked)
14223 return throw_proxy_error(js, "Cannot perform 'defineProperty' on a proxy that has been revoked");
14224
14225 ant_value_t target = data->target;
14226 ant_value_t handler = data->handler;
14227 ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "defineProperty", 14) : 0;
14228
14229 if (trap_off != 0) {
14230 ant_value_t trap = propref_load(js, trap_off);
14231 if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) {
14232 ant_value_t args[3] = { target, key_val, descriptor };
14233 return sv_vm_call(js->vm, js, trap, js_mkundef(), args, 3, NULL, false);
14234 }}
14235
14236 ant_value_t args[3] = { target, key_val, descriptor };
14237 ant_value_t result = builtin_object_defineProperty(js, args, 3);
14238 if (is_err(result)) return result;
14239 return js_true;
14240}
14241
14242static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) {
14243 ant_proxy_state_t *data = get_proxy_data(proxy);
14244 if (!data) return js_true;
14245 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'deleteProperty' on a proxy that has been revoked");
14246
14247 ant_value_t target = data->target;
14248 ant_value_t handler = data->handler;
14249
14250 ant_offset_t delete_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "deleteProperty", 14) : 0;
14251 if (delete_trap_off != 0) {
14252 ant_value_t delete_trap = propref_load(js, delete_trap_off);
14253 if (vtype(delete_trap) == T_FUNC || vtype(delete_trap) == T_CFUNC) {
14254 ant_value_t args[2] = { target, key_val };
14255 ant_value_t result = sv_vm_call(js->vm, js, delete_trap, js_mkundef(), args, 2, NULL, false);
14256 if (is_err(result)) return result;
14257 if (js_truthy(js, result) && vtype(key_val) == T_SYMBOL) {
14258 ant_offset_t prop_off = lkp_sym(js, target, (ant_offset_t)vdata(key_val));
14259 if (prop_off != 0 && is_nonconfig_prop(js, prop_off))
14260 return js_mkerr_typed(js, JS_ERR_TYPE, "'deleteProperty' on proxy: trap returned truthy for non-configurable property");
14261 } return result;
14262 }
14263 }
14264 if (vtype(key_val) == T_SYMBOL) return js_delete_sym_prop(js, target, key_val);
14265 return js_true;
14266}
14267
14268ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, ant_value_t *args, int argc) {
14269 ant_proxy_state_t *data = get_proxy_data(proxy);
14270 if (!data) return js_mkerr_typed(js, JS_ERR_TYPE, "object is not a function");
14271 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'apply' on a proxy that has been revoked");
14272
14273 ant_value_t target = data->target;
14274 ant_value_t handler = data->handler;
14275 uint8_t target_type = vtype(target);
14276
14277 if (target_type != T_FUNC && target_type != T_CFUNC && !(target_type == T_OBJ && is_proxy(target)))
14278 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not a function", typestr(target_type));
14279
14280 ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "apply", 5) : 0;
14281 if (trap_off != 0) {
14282 ant_value_t trap = propref_load(js, trap_off);
14283 if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) {
14284 ant_value_t args_arr = mkarr(js);
14285 for (int i = 0; i < argc; i++)
14286 js_arr_push(js, args_arr, args[i]);
14287 ant_value_t trap_args[3] = { target, this_arg, args_arr };
14288 return sv_vm_call(js->vm, js, trap, handler, trap_args, 3, NULL, false);
14289 }
14290 }
14291
14292 return sv_vm_call(js->vm, js, target, this_arg, args, argc, NULL, false);
14293}
14294
14295ant_value_t js_proxy_construct(ant_t *js, ant_value_t proxy, ant_value_t *args, int argc, ant_value_t new_target) {
14296 ant_proxy_state_t *data = get_proxy_data(proxy);
14297 if (!data) return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor");
14298 if (data->revoked) return throw_proxy_error(js, "Cannot perform 'construct' on a proxy that has been revoked");
14299
14300 ant_value_t target = data->target;
14301 ant_value_t handler = data->handler;
14302
14303 if (!js_is_constructor(target))
14304 return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor");
14305 if (vtype(target) == T_OBJ && is_proxy(target))
14306 return js_proxy_construct(js, target, args, argc, new_target);
14307 if (vtype(target) != T_FUNC)
14308 return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor");
14309
14310 ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "construct", 9) : 0;
14311 if (trap_off != 0) {
14312 ant_value_t trap = propref_load(js, trap_off);
14313 if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) {
14314 ant_value_t args_arr = mkarr(js);
14315 for (int i = 0; i < argc; i++)
14316 js_arr_push(js, args_arr, args[i]);
14317 ant_value_t trap_args[3] = { target, args_arr, new_target };
14318 ant_value_t result = sv_vm_call(js->vm, js, trap, js_mkundef(), trap_args, 3, NULL, false);
14319 if (is_err(result)) return result;
14320 if (!is_object_type(result))
14321 return js_mkerr_typed(js, JS_ERR_TYPE, "'construct' on proxy: trap returned non-Object");
14322 return result;
14323 }
14324 }
14325
14326 ant_value_t obj = mkobj(js, 0);
14327 ant_value_t proto = js_getprop_fallback(js, target, "prototype");
14328 if (is_object_type(proto)) js_set_proto_init(obj, proto);
14329 ant_value_t saved = js->new_target;
14330 js->new_target = new_target;
14331 ant_value_t ctor_this = obj;
14332 ant_value_t result = sv_vm_call(js->vm, js, target, obj, args, argc, &ctor_this, true);
14333 js->new_target = saved;
14334 if (is_err(result)) return result;
14335 return is_object_type(result) ? result : (is_object_type(ctor_this) ? ctor_this : obj);
14336}
14337
14338static ant_value_t mkproxy(ant_t *js, ant_value_t target, ant_value_t handler) {
14339 ant_value_t proxy_obj = mkobj(js, 0);
14340 ant_object_t *proxy_ptr = js_obj_ptr(proxy_obj);
14341 if (!proxy_ptr) return js_mkerr(js, "out of memory");
14342
14343 ant_proxy_state_t *data = (ant_proxy_state_t *)ant_calloc(sizeof(ant_proxy_state_t));
14344 if (!data) return js_mkerr(js, "out of memory");
14345
14346 data->target = target;
14347 data->handler = handler;
14348 data->revoked = false;
14349
14350 proxy_ptr->is_exotic = 1;
14351 js_mark_constructor(proxy_obj, js_is_constructor(target));
14352
14353 ant_object_sidecar_t *sidecar = ant_object_ensure_sidecar(proxy_ptr);
14354 if (!sidecar) {
14355 free(data);
14356 return js_mkerr(js, "out of memory");
14357 }
14358
14359 sidecar->proxy_state = data;
14360 return proxy_obj;
14361}
14362
14363static ant_value_t create_proxy_checked(ant_t *js, ant_value_t *args, int nargs, bool require_new) {
14364 if (require_new && vtype(js->new_target) == T_UNDEF) {
14365 return js_mkerr_typed(js, JS_ERR_TYPE, "Proxy constructor requires 'new'");
14366 }
14367 if (nargs < 2) return js_mkerr(js, "Proxy requires two arguments: target and handler");
14368
14369 ant_value_t target = args[0];
14370 ant_value_t handler = args[1];
14371
14372 uint8_t target_type = vtype(target);
14373 if (target_type != T_OBJ && target_type != T_FUNC && target_type != T_ARR) {
14374 return js_mkerr(js, "Proxy target must be an object");
14375 }
14376
14377 uint8_t handler_type = vtype(handler);
14378 if (handler_type != T_OBJ && handler_type != T_FUNC) {
14379 return js_mkerr(js, "Proxy handler must be an object");
14380 }
14381
14382 return mkproxy(js, target, handler);
14383}
14384
14385static ant_value_t builtin_Proxy(ant_t *js, ant_value_t *args, int nargs) {
14386 return create_proxy_checked(js, args, nargs, true);
14387}
14388
14389static ant_value_t proxy_revoke_fn(ant_t *js, ant_value_t *args, int nargs) {
14390 (void)args; (void)nargs;
14391 ant_value_t func = js->current_func;
14392 ant_value_t ref_slot = get_slot(func, SLOT_PROXY_REF);
14393
14394 if (vtype(ref_slot) != T_UNDEF && vdata(ref_slot) != 0) {
14395 ant_proxy_state_t *data = get_proxy_data(ref_slot);
14396 if (data) data->revoked = true;
14397 }
14398
14399 return js_mkundef();
14400}
14401
14402static ant_value_t builtin_Proxy_revocable(ant_t *js, ant_value_t *args, int nargs) {
14403 ant_value_t proxy = create_proxy_checked(js, args, nargs, false);
14404 if (is_err(proxy)) return proxy;
14405
14406 ant_value_t revoke_obj = mkobj(js, 0);
14407 set_slot(revoke_obj, SLOT_CFUNC, js_mkfun(proxy_revoke_fn));
14408 set_slot(revoke_obj, SLOT_PROXY_REF, proxy);
14409
14410 ant_value_t revoke_func = js_obj_to_func(revoke_obj);
14411
14412 ant_value_t result = mkobj(js, 0);
14413 js_setprop(js, result, js_mkstr(js, "proxy", 5), proxy);
14414 js_setprop(js, result, js_mkstr(js, "revoke", 6), revoke_func);
14415
14416 return result;
14417}
14418
14419ant_t *js_create(void *buf, size_t len) {
14420 ANT_ASSERT(
14421 (uintptr_t)buf <= ((1ULL << 53) - 1),
14422 "pointer exceeds 53-bit double-precision integer limit"
14423 );
14424
14425 ant_t *js = NULL;
14426
14427 if (len < sizeof(*js)) return js;
14428 memset(buf, 0, len);
14429
14430 js = (ant_t *)buf;
14431 rt->js = js;
14432 js_init_intern_cache(js);
14433
14434 if (!fixed_arena_init(&js->obj_arena, sizeof(ant_object_t), offsetof(ant_object_t, mark_epoch), ANT_ARENA_MAX)) return NULL;
14435 if (!fixed_arena_init(&js->closure_arena, sizeof(sv_closure_t), offsetof(sv_closure_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) {
14436 fixed_arena_destroy(&js->obj_arena);
14437 return NULL;
14438 }
14439
14440 if (!fixed_arena_init(&js->upvalue_arena, sizeof(sv_upvalue_t), offsetof(sv_upvalue_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) {
14441 fixed_arena_destroy(&js->closure_arena);
14442 fixed_arena_destroy(&js->obj_arena);
14443 return NULL;
14444 }
14445
14446 js->c_root_cap = 64;
14447 js->c_roots = calloc(js->c_root_cap, sizeof(*js->c_roots));
14448
14449 if (!js->c_roots) {
14450 fixed_arena_destroy(&js->upvalue_arena);
14451 fixed_arena_destroy(&js->closure_arena);
14452 fixed_arena_destroy(&js->obj_arena);
14453 return NULL;
14454 }
14455
14456 js->global = mkobj(js, 0);
14457 js->this_val = js->global;
14458 js->new_target = js_mkundef();
14459 js->length_str = ANT_STRING("length");
14460
14461 ant_value_t glob = js->global;
14462 ant_value_t object_proto = js_mkobj(js);
14463 set_proto(js, object_proto, js_mknull());
14464
14465 defmethod(js, object_proto, "toString", 8, js_mkfun(builtin_object_toString));
14466 defmethod(js, object_proto, "valueOf", 7, js_mkfun(builtin_object_valueOf));
14467 defmethod(js, object_proto, "toLocaleString", 14, js_mkfun(builtin_object_toLocaleString));
14468 defmethod(js, object_proto, "hasOwnProperty", 14, js_mkfun(builtin_object_hasOwnProperty));
14469 defmethod(js, object_proto, "isPrototypeOf", 13, js_mkfun(builtin_object_isPrototypeOf));
14470 defmethod(js, object_proto, "propertyIsEnumerable", 20, js_mkfun(builtin_object_propertyIsEnumerable));
14471 defmethod(js, object_proto, "__defineGetter__", 16, js_mkfun(builtin_object___defineGetter__));
14472 defmethod(js, object_proto, "__defineSetter__", 16, js_mkfun(builtin_object___defineSetter__));
14473 defmethod(js, object_proto, "__lookupGetter__", 16, js_mkfun(builtin_object___lookupGetter__));
14474 defmethod(js, object_proto, "__lookupSetter__", 16, js_mkfun(builtin_object___lookupSetter__));
14475
14476 ant_value_t proto_getter = js_mkfun(builtin_proto_getter);
14477 ant_value_t proto_setter = js_mkfun(builtin_proto_setter);
14478 js_set_accessor_desc(js, object_proto, STR_PROTO, STR_PROTO_LEN, proto_getter, proto_setter, JS_DESC_C);
14479
14480 ant_value_t function_proto_obj = js_mkobj(js);
14481 set_proto(js, function_proto_obj, object_proto);
14482 set_slot(function_proto_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty));
14483
14484 defmethod(js, function_proto_obj, "call", 4, js_mkfun(builtin_function_call));
14485 defmethod(js, function_proto_obj, "apply", 5, js_mkfun(builtin_function_apply));
14486 defmethod(js, function_proto_obj, "bind", 4, js_mkfun(builtin_function_bind));
14487 defmethod(js, function_proto_obj, "toString", 8, js_mkfun(builtin_function_toString));
14488
14489 ant_value_t function_proto = js_obj_to_func(function_proto_obj);
14490 set_slot(glob, SLOT_FUNC_PROTO, function_proto);
14491
14492 ant_value_t array_proto = alloc_array_with_proto(js, object_proto);
14493
14494 defmethod(js, array_proto, "push", 4, js_mkfun(builtin_array_push));
14495 defmethod(js, array_proto, "pop", 3, js_mkfun(builtin_array_pop));
14496 defmethod(js, array_proto, "slice", 5, js_mkfun(builtin_array_slice));
14497 defmethod(js, array_proto, "join", 4, js_mkfun(builtin_array_join));
14498 defmethod(js, array_proto, "includes", 8, js_mkfun(builtin_array_includes));
14499 defmethod(js, array_proto, "every", 5, js_mkfun(builtin_array_every));
14500 defmethod(js, array_proto, "reverse", 7, js_mkfun(builtin_array_reverse));
14501 defmethod(js, array_proto, "map", 3, js_mkfun(builtin_array_map));
14502 defmethod(js, array_proto, "filter", 6, js_mkfun(builtin_array_filter));
14503 defmethod(js, array_proto, "reduce", 6, js_mkfun(builtin_array_reduce));
14504 defmethod(js, array_proto, "flat", 4, js_mkfun(builtin_array_flat));
14505 defmethod(js, array_proto, "concat", 6, js_mkfun(builtin_array_concat));
14506 defmethod(js, array_proto, "at", 2, js_mkfun(builtin_array_at));
14507 defmethod(js, array_proto, "fill", 4, js_mkfun(builtin_array_fill));
14508 defmethod(js, array_proto, "find", 4, js_mkfun(builtin_array_find));
14509 defmethod(js, array_proto, "findIndex", 9, js_mkfun(builtin_array_findIndex));
14510 defmethod(js, array_proto, "findLast", 8, js_mkfun(builtin_array_findLast));
14511 defmethod(js, array_proto, "findLastIndex", 13, js_mkfun(builtin_array_findLastIndex));
14512 defmethod(js, array_proto, "flatMap", 7, js_mkfun(builtin_array_flatMap));
14513 defmethod(js, array_proto, "forEach", 7, js_mkfun(builtin_array_forEach));
14514 defmethod(js, array_proto, "indexOf", 7, js_mkfun(builtin_array_indexOf));
14515 defmethod(js, array_proto, "lastIndexOf", 11, js_mkfun(builtin_array_lastIndexOf));
14516 defmethod(js, array_proto, "reduceRight", 11, js_mkfun(builtin_array_reduceRight));
14517 defmethod(js, array_proto, "shift", 5, js_mkfun(builtin_array_shift));
14518 defmethod(js, array_proto, "unshift", 7, js_mkfun(builtin_array_unshift));
14519 defmethod(js, array_proto, "some", 4, js_mkfun(builtin_array_some));
14520 defmethod(js, array_proto, "sort", 4, js_mkfun(builtin_array_sort));
14521 defmethod(js, array_proto, "splice", 6, js_mkfun(builtin_array_splice));
14522 defmethod(js, array_proto, "copyWithin", 10, js_mkfun(builtin_array_copyWithin));
14523 defmethod(js, array_proto, "toReversed", 10, js_mkfun(builtin_array_toReversed));
14524 defmethod(js, array_proto, "toSorted", 8, js_mkfun(builtin_array_toSorted));
14525 defmethod(js, array_proto, "toSpliced", 9, js_mkfun(builtin_array_toSpliced));
14526 defmethod(js, array_proto, "with", 4, js_mkfun(builtin_array_with));
14527 defmethod(js, array_proto, "keys", 4, js_mkfun(builtin_array_keys));
14528 defmethod(js, array_proto, "values", 6, js_mkfun(builtin_array_values));
14529 defmethod(js, array_proto, "entries", 7, js_mkfun(builtin_array_entries));
14530 defmethod(js, array_proto, "toString", 8, js_mkfun(builtin_array_toString));
14531 defmethod(js, array_proto, "toLocaleString", 14, js_mkfun(builtin_array_toLocaleString));
14532
14533 ant_value_t string_proto = js_mkobj(js);
14534 set_proto(js, string_proto, object_proto);
14535
14536 defmethod(js, string_proto, "indexOf", 7, js_mkfun(builtin_string_indexOf));
14537 defmethod(js, string_proto, "substring", 9, js_mkfun(builtin_string_substring));
14538 defmethod(js, string_proto, "substr", 6, js_mkfun(builtin_string_substr));
14539 defmethod(js, string_proto, "split", 5, js_mkfun(builtin_string_split));
14540 defmethod(js, string_proto, "slice", 5, js_mkfun(builtin_string_slice));
14541 defmethod(js, string_proto, "includes", 8, js_mkfun(builtin_string_includes));
14542 defmethod(js, string_proto, "startsWith", 10, js_mkfun(builtin_string_startsWith));
14543 defmethod(js, string_proto, "endsWith", 8, js_mkfun(builtin_string_endsWith));
14544 defmethod(js, string_proto, "template", 8, js_mkfun(builtin_string_template));
14545 defmethod(js, string_proto, "charCodeAt", 10, js_mkfun(builtin_string_charCodeAt));
14546 defmethod(js, string_proto, "codePointAt", 11, js_mkfun(builtin_string_codePointAt));
14547 defmethod(js, string_proto, "toLowerCase", 11, js_mkfun(builtin_string_toLowerCase));
14548 defmethod(js, string_proto, "toUpperCase", 11, js_mkfun(builtin_string_toUpperCase));
14549 defmethod(js, string_proto, "toLocaleLowerCase", 17, js_mkfun(builtin_string_toLowerCase));
14550 defmethod(js, string_proto, "toLocaleUpperCase", 17, js_mkfun(builtin_string_toUpperCase));
14551 defmethod(js, string_proto, "trim", 4, js_mkfun(builtin_string_trim));
14552 defmethod(js, string_proto, "trimStart", 9, js_mkfun(builtin_string_trimStart));
14553 defmethod(js, string_proto, "trimEnd", 7, js_mkfun(builtin_string_trimEnd));
14554 defmethod(js, string_proto, "trimLeft", 8, js_mkfun(builtin_string_trimStart));
14555 defmethod(js, string_proto, "trimRight", 9, js_mkfun(builtin_string_trimEnd));
14556 defmethod(js, string_proto, "repeat", 6, js_mkfun(builtin_string_repeat));
14557 defmethod(js, string_proto, "padStart", 8, js_mkfun(builtin_string_padStart));
14558 defmethod(js, string_proto, "padEnd", 6, js_mkfun(builtin_string_padEnd));
14559 defmethod(js, string_proto, "charAt", 6, js_mkfun(builtin_string_charAt));
14560 defmethod(js, string_proto, "at", 2, js_mkfun(builtin_string_at));
14561 defmethod(js, string_proto, "lastIndexOf", 11, js_mkfun(builtin_string_lastIndexOf));
14562 defmethod(js, string_proto, "concat", 6, js_mkfun(builtin_string_concat));
14563 defmethod(js, string_proto, "localeCompare", 13, js_mkfun(builtin_string_localeCompare));
14564 defmethod(js, string_proto, "normalize", 9, js_mkfun(builtin_string_normalize));
14565 defmethod(js, string_proto, "valueOf", 7, js_mkfun(builtin_string_valueOf));
14566 defmethod(js, string_proto, "toString", 8, js_mkfun(builtin_string_toString));
14567
14568 ant_value_t number_proto = js_mkobj(js);
14569 set_proto(js, number_proto, object_proto);
14570
14571 defmethod(js, number_proto, "toString", 8, js_mkfun(builtin_number_toString));
14572 defmethod(js, number_proto, "toFixed", 7, js_mkfun(builtin_number_toFixed));
14573 defmethod(js, number_proto, "toPrecision", 11, js_mkfun(builtin_number_toPrecision));
14574 defmethod(js, number_proto, "toExponential", 13, js_mkfun(builtin_number_toExponential));
14575 defmethod(js, number_proto, "valueOf", 7, js_mkfun(builtin_number_valueOf));
14576 defmethod(js, number_proto, "toLocaleString", 14, js_mkfun(builtin_number_toLocaleString));
14577
14578 ant_value_t boolean_proto = js_mkobj(js);
14579 set_proto(js, boolean_proto, object_proto);
14580
14581 defmethod(js, boolean_proto, "valueOf", 7, js_mkfun(builtin_boolean_valueOf));
14582 defmethod(js, boolean_proto, "toString", 8, js_mkfun(builtin_boolean_toString));
14583
14584 ant_value_t error_proto = js_mkobj(js);
14585 set_proto(js, error_proto, object_proto);
14586
14587 js_setprop(js, error_proto, ANT_STRING("name"), ANT_STRING("Error"));
14588 js_setprop(js, error_proto, ANT_STRING("message"), js_mkstr(js, "", 0));
14589 defmethod(js, error_proto, "toString", 8, js_mkfun(builtin_Error_toString));
14590
14591 ant_value_t err_ctor_obj = mkobj(js, 0);
14592 set_proto(js, err_ctor_obj, function_proto);
14593 set_slot(err_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Error));
14594 js_setprop_nonconfigurable(js, err_ctor_obj, "prototype", 9, error_proto);
14595 js_setprop(js, err_ctor_obj, ANT_STRING("name"), ANT_STRING("Error"));
14596
14597 ant_value_t err_ctor_func = js_obj_to_func(err_ctor_obj);
14598 js_setprop(js, glob, ANT_STRING("Error"), err_ctor_func);
14599 js_setprop(js, error_proto, js_mkstr(js, "constructor", 11), err_ctor_func);
14600 js_set_descriptor(js, error_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14601 defmethod(js, err_ctor_func, "isError", 7, js_mkfun(builtin_error_isError));
14602 defmethod(js, err_ctor_func, "captureStackTrace", 17, js_mkfun(builtin_error_captureStackTrace));
14603 js_setprop(js, err_ctor_func, ANT_STRING("stackTraceLimit"), js_mknum(10));
14604
14605 #define REGISTER_ERROR_SUBTYPE(name_str) do { \
14606 ant_value_t proto = js_mkobj(js); \
14607 set_proto(js, proto, error_proto); \
14608 js_setprop(js, proto, ANT_STRING("name"), ANT_STRING(name_str)); \
14609 ant_value_t ctor = mkobj(js, 0); \
14610 set_proto(js, ctor, function_proto); \
14611 set_slot(ctor, SLOT_CFUNC, js_mkfun(builtin_Error)); \
14612 js_setprop_nonconfigurable(js, ctor, "prototype", 9, proto); \
14613 js_setprop(js, ctor, ANT_STRING("name"), ANT_STRING(name_str)); \
14614 ant_value_t ctor_func = js_obj_to_func(ctor); \
14615 js_setprop(js, proto, ANT_STRING("constructor"), ctor_func); \
14616 js_set_descriptor(js, proto, "constructor", 11, JS_DESC_W | JS_DESC_C); \
14617 js_setprop(js, glob, ANT_STRING(name_str), ctor_func); \
14618 } while(0)
14619
14620 REGISTER_ERROR_SUBTYPE("EvalError");
14621 REGISTER_ERROR_SUBTYPE("RangeError");
14622 REGISTER_ERROR_SUBTYPE("ReferenceError");
14623 REGISTER_ERROR_SUBTYPE("SyntaxError");
14624 REGISTER_ERROR_SUBTYPE("TypeError");
14625 REGISTER_ERROR_SUBTYPE("URIError");
14626 REGISTER_ERROR_SUBTYPE("InternalError");
14627
14628 #undef REGISTER_ERROR_SUBTYPE
14629
14630 ant_value_t proto = js_mkobj(js);
14631 set_proto(js, proto, error_proto);
14632 js_setprop(js, proto, ANT_STRING("name"), ANT_STRING("AggregateError"));
14633 ant_value_t ctor = mkobj(js, 0);
14634 set_proto(js, ctor, function_proto);
14635 set_slot(ctor, SLOT_CFUNC, js_mkfun(builtin_AggregateError));
14636 js_setprop_nonconfigurable(js, ctor, "prototype", 9, proto);
14637 js_setprop(js, ctor, ANT_STRING("name"), ANT_STRING("AggregateError"));
14638 js_setprop(js, proto, ANT_STRING("constructor"), js_obj_to_func(ctor));
14639 js_set_descriptor(js, proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14640 js_setprop(js, glob, ANT_STRING("AggregateError"), js_obj_to_func(ctor));
14641
14642 ant_value_t suppressed_proto = js_mkobj(js);
14643 set_proto(js, suppressed_proto, error_proto);
14644 js_setprop(js, suppressed_proto, ANT_STRING("name"), ANT_STRING("SuppressedError"));
14645
14646 ant_value_t suppressed_ctor = mkobj(js, 0);
14647 set_proto(js, suppressed_ctor, function_proto);
14648 set_slot(suppressed_ctor, SLOT_CFUNC, js_mkfun(builtin_SuppressedError));
14649 js_setprop_nonconfigurable(js, suppressed_ctor, "prototype", 9, suppressed_proto);
14650 js_setprop(js, suppressed_ctor, ANT_STRING("name"), ANT_STRING("SuppressedError"));
14651
14652 ant_value_t suppressed_ctor_func = js_obj_to_func(suppressed_ctor);
14653 js_setprop(js, suppressed_proto, ANT_STRING("constructor"), suppressed_ctor_func);
14654 js_set_descriptor(js, suppressed_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14655 js_setprop(js, glob, ANT_STRING("SuppressedError"), suppressed_ctor_func);
14656
14657 ant_value_t disposable_stack_proto = js_mkobj(js);
14658 set_proto(js, disposable_stack_proto, object_proto);
14659 defmethod(js, disposable_stack_proto, "use", 3, js_mkfun(builtin_DisposableStack_use));
14660 defmethod(js, disposable_stack_proto, "adopt", 5, js_mkfun(builtin_DisposableStack_adopt));
14661 defmethod(js, disposable_stack_proto, "defer", 5, js_mkfun(builtin_DisposableStack_defer));
14662 defmethod(js, disposable_stack_proto, "move", 4, js_mkfun(builtin_DisposableStack_move));
14663 defmethod(js, disposable_stack_proto, "dispose", 7, js_mkfun(builtin_DisposableStack_dispose));
14664
14665 ant_value_t disposable_stack_ctor = mkobj(js, 0);
14666 set_proto(js, disposable_stack_ctor, function_proto);
14667 set_slot(disposable_stack_ctor, SLOT_CFUNC, js_mkfun(builtin_DisposableStack));
14668 js_setprop_nonconfigurable(js, disposable_stack_ctor, "prototype", 9, disposable_stack_proto);
14669 js_setprop(js, disposable_stack_ctor, ANT_STRING("name"), ANT_STRING("DisposableStack"));
14670
14671 ant_value_t disposable_stack_ctor_func = js_obj_to_func(disposable_stack_ctor);
14672 js_setprop(js, disposable_stack_proto, ANT_STRING("constructor"), disposable_stack_ctor_func);
14673 js_set_descriptor(js, disposable_stack_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14674 js_setprop(js, glob, ANT_STRING("DisposableStack"), disposable_stack_ctor_func);
14675
14676 ant_value_t async_disposable_stack_proto = js_mkobj(js);
14677 set_proto(js, async_disposable_stack_proto, object_proto);
14678 defmethod(js, async_disposable_stack_proto, "use", 3, js_mkfun(builtin_AsyncDisposableStack_use));
14679 defmethod(js, async_disposable_stack_proto, "adopt", 5, js_mkfun(builtin_AsyncDisposableStack_adopt));
14680 defmethod(js, async_disposable_stack_proto, "defer", 5, js_mkfun(builtin_AsyncDisposableStack_defer));
14681 defmethod(js, async_disposable_stack_proto, "move", 4, js_mkfun(builtin_AsyncDisposableStack_move));
14682 defmethod(js, async_disposable_stack_proto, "disposeAsync", 12, js_mkfun(builtin_AsyncDisposableStack_disposeAsync));
14683
14684 ant_value_t async_disposable_stack_ctor = mkobj(js, 0);
14685 set_proto(js, async_disposable_stack_ctor, function_proto);
14686 set_slot(async_disposable_stack_ctor, SLOT_CFUNC, js_mkfun(builtin_AsyncDisposableStack));
14687 js_setprop_nonconfigurable(js, async_disposable_stack_ctor, "prototype", 9, async_disposable_stack_proto);
14688 js_setprop(js, async_disposable_stack_ctor, ANT_STRING("name"), ANT_STRING("AsyncDisposableStack"));
14689
14690 ant_value_t async_disposable_stack_ctor_func = js_obj_to_func(async_disposable_stack_ctor);
14691 js_setprop(js, async_disposable_stack_proto, ANT_STRING("constructor"), async_disposable_stack_ctor_func);
14692 js_set_descriptor(js, async_disposable_stack_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14693 js_setprop(js, glob, ANT_STRING("AsyncDisposableStack"), async_disposable_stack_ctor_func);
14694
14695 ant_value_t promise_proto = js_mkobj(js);
14696 set_proto(js, promise_proto, object_proto);
14697 defmethod(js, promise_proto, "then", 4, js_mkfun(builtin_promise_then));
14698 defmethod(js, promise_proto, "catch", 5, js_mkfun(builtin_promise_catch));
14699 defmethod(js, promise_proto, "finally", 7, js_mkfun(builtin_promise_finally));
14700
14701 ant_value_t obj_func_obj = mkobj(js, 0);
14702 set_proto(js, obj_func_obj, function_proto);
14703 set_slot(obj_func_obj, SLOT_BUILTIN, tov(BUILTIN_OBJECT));
14704 js_mark_constructor(obj_func_obj, true);
14705
14706 defmethod(js, obj_func_obj, "keys", 4, js_mkfun(builtin_object_keys));
14707 defmethod(js, obj_func_obj, "values", 6, js_mkfun(builtin_object_values));
14708 defmethod(js, obj_func_obj, "entries", 7, js_mkfun(builtin_object_entries));
14709 defmethod(js, obj_func_obj, "is", 2, js_mkfun(builtin_object_is));
14710 defmethod(js, obj_func_obj, "getPrototypeOf", 14, js_mkfun(builtin_object_getPrototypeOf));
14711 defmethod(js, obj_func_obj, "setPrototypeOf", 14, js_mkfun(builtin_object_setPrototypeOf));
14712 defmethod(js, obj_func_obj, "create", 6, js_mkfun(builtin_object_create));
14713 defmethod(js, obj_func_obj, "hasOwn", 6, js_mkfun(builtin_object_hasOwn));
14714 defmethod(js, obj_func_obj, "groupBy", 7, js_mkfun(builtin_object_groupBy));
14715 defmethod(js, obj_func_obj, "defineProperty", 14, js_mkfun(builtin_object_defineProperty));
14716 defmethod(js, obj_func_obj, "defineProperties", 16, js_mkfun(builtin_object_defineProperties));
14717 defmethod(js, obj_func_obj, "assign", 6, js_mkfun(builtin_object_assign));
14718 defmethod(js, obj_func_obj, "freeze", 6, js_mkfun(builtin_object_freeze));
14719 defmethod(js, obj_func_obj, "isFrozen", 8, js_mkfun(builtin_object_isFrozen));
14720 defmethod(js, obj_func_obj, "seal", 4, js_mkfun(builtin_object_seal));
14721 defmethod(js, obj_func_obj, "isSealed", 8, js_mkfun(builtin_object_isSealed));
14722 defmethod(js, obj_func_obj, "fromEntries", 11, js_mkfun(builtin_object_fromEntries));
14723 defmethod(js, obj_func_obj, "getOwnPropertyDescriptor", 24, js_mkfun(builtin_object_getOwnPropertyDescriptor));
14724 defmethod(js, obj_func_obj, "getOwnPropertyDescriptors", 25, js_mkfun(builtin_object_getOwnPropertyDescriptors));
14725 defmethod(js, obj_func_obj, "getOwnPropertyNames", 19, js_mkfun(builtin_object_getOwnPropertyNames));
14726 defmethod(js, obj_func_obj, "getOwnPropertySymbols", 21, js_mkfun(builtin_object_getOwnPropertySymbols));
14727 defmethod(js, obj_func_obj, "isExtensible", 12, js_mkfun(builtin_object_isExtensible));
14728 defmethod(js, obj_func_obj, "preventExtensions", 17, js_mkfun(builtin_object_preventExtensions));
14729
14730 js_setprop(js, obj_func_obj, ANT_STRING("name"), ANT_STRING("Object"));
14731 js_setprop_readonly_nonconfigurable(js, obj_func_obj, "prototype", 9, object_proto);
14732 ant_value_t obj_func = js_obj_to_func(obj_func_obj);
14733 js_setprop(js, glob, js_mkstr(js, "Object", 6), obj_func);
14734
14735 ant_value_t func_ctor_obj = mkobj(js, 0);
14736 set_proto(js, func_ctor_obj, function_proto);
14737 set_slot(func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Function));
14738 js_setprop_nonconfigurable(js, func_ctor_obj, "prototype", 9, function_proto);
14739 js_setprop(js, func_ctor_obj, js->length_str, tov(1.0));
14740 js_set_descriptor(js, func_ctor_obj, "length", 6, JS_DESC_C);
14741 js_setprop(js, func_ctor_obj, ANT_STRING("name"), ANT_STRING("Function"));
14742 ant_value_t func_ctor_func = js_obj_to_func(func_ctor_obj);
14743 js_setprop(js, glob, js_mkstr(js, "Function", 8), func_ctor_func);
14744
14745 ant_value_t async_func_proto_obj = js_mkobj(js);
14746 set_proto(js, async_func_proto_obj, function_proto);
14747 set_slot(async_func_proto_obj, SLOT_ASYNC, js_true);
14748 ant_value_t async_func_proto = js_obj_to_func(async_func_proto_obj);
14749 set_slot(glob, SLOT_ASYNC_PROTO, async_func_proto);
14750
14751 ant_value_t async_func_ctor_obj = mkobj(js, 0);
14752 set_proto(js, async_func_ctor_obj, function_proto);
14753 set_slot(async_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_AsyncFunction));
14754 js_setprop_nonconfigurable(js, async_func_ctor_obj, "prototype", 9, async_func_proto);
14755 js_setprop(js, async_func_ctor_obj, js->length_str, tov(1.0));
14756 js_set_descriptor(js, async_func_ctor_obj, "length", 6, JS_DESC_C);
14757 js_setprop(js, async_func_ctor_obj, ANT_STRING("name"), ANT_STRING("AsyncFunction"));
14758 ant_value_t async_func_ctor = js_obj_to_func(async_func_ctor_obj);
14759
14760 js_setprop(js, async_func_proto_obj, js_mkstr(js, "constructor", 11), async_func_ctor);
14761 js_set_descriptor(js, async_func_proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C);
14762
14763 ant_value_t generator_func_proto_obj = js_mkobj(js);
14764 set_proto(js, generator_func_proto_obj, function_proto);
14765 ant_value_t generator_func_proto = js_obj_to_func(generator_func_proto_obj);
14766 set_slot(glob, SLOT_GENERATOR_PROTO, generator_func_proto);
14767
14768 ant_value_t generator_func_ctor_obj = mkobj(js, 0);
14769 set_proto(js, generator_func_ctor_obj, function_proto);
14770 set_slot(generator_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_GeneratorFunction));
14771 js_setprop_nonconfigurable(js, generator_func_ctor_obj, "prototype", 9, generator_func_proto);
14772 js_setprop(js, generator_func_ctor_obj, js->length_str, tov(1.0));
14773 js_set_descriptor(js, generator_func_ctor_obj, "length", 6, JS_DESC_C);
14774 js_setprop(js, generator_func_ctor_obj, ANT_STRING("name"), ANT_STRING("GeneratorFunction"));
14775 ant_value_t generator_func_ctor = js_obj_to_func(generator_func_ctor_obj);
14776
14777 js_setprop(js, generator_func_proto_obj, js_mkstr(js, "constructor", 11), generator_func_ctor);
14778 js_set_descriptor(js, generator_func_proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C);
14779
14780 ant_value_t async_generator_func_proto_obj = js_mkobj(js);
14781 set_proto(js, async_generator_func_proto_obj, function_proto);
14782 set_slot(async_generator_func_proto_obj, SLOT_ASYNC, js_true);
14783
14784 ant_value_t async_generator_func_proto = js_obj_to_func(async_generator_func_proto_obj);
14785 set_slot(glob, SLOT_ASYNC_GENERATOR_PROTO, async_generator_func_proto);
14786
14787 ant_value_t async_generator_func_ctor_obj = mkobj(js, 0);
14788 set_proto(js, async_generator_func_ctor_obj, function_proto);
14789 set_slot(async_generator_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_AsyncGeneratorFunction));
14790 js_setprop_nonconfigurable(js, async_generator_func_ctor_obj, "prototype", 9, async_generator_func_proto);
14791 js_setprop(js, async_generator_func_ctor_obj, js->length_str, tov(1.0));
14792 js_set_descriptor(js, async_generator_func_ctor_obj, "length", 6, JS_DESC_C);
14793 js_setprop(js, async_generator_func_ctor_obj, ANT_STRING("name"), ANT_STRING("AsyncGeneratorFunction"));
14794
14795 ant_value_t async_generator_func_ctor = js_obj_to_func(async_generator_func_ctor_obj);
14796 js_setprop(js, async_generator_func_proto_obj, js_mkstr(js, "constructor", 11), async_generator_func_ctor);
14797 js_set_descriptor(js, async_generator_func_proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C);
14798
14799 ant_value_t str_ctor_obj = mkobj(js, 0);
14800 set_proto(js, str_ctor_obj, function_proto);
14801 set_slot(str_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_String));
14802 js_setprop_nonconfigurable(js, str_ctor_obj, "prototype", 9, string_proto);
14803 defmethod(js, str_ctor_obj, "fromCharCode", 12, js_mkfun(builtin_string_fromCharCode));
14804 defmethod(js, str_ctor_obj, "fromCodePoint", 13, js_mkfun(builtin_string_fromCodePoint));
14805 defmethod(js, str_ctor_obj, "raw", 3, js_mkfun(builtin_string_raw));
14806 js_setprop(js, str_ctor_obj, ANT_STRING("name"), ANT_STRING("String"));
14807
14808 ant_value_t str_ctor_func = js_obj_to_func(str_ctor_obj);
14809 js_setprop(js, glob, js_mkstr(js, "String", 6), str_ctor_func);
14810
14811 ant_value_t number_ctor_obj = mkobj(js, 0);
14812 set_proto(js, number_ctor_obj, function_proto);
14813
14814 set_slot(number_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Number));
14815 defmethod(js, number_ctor_obj, "isNaN", 5, js_mkfun(builtin_Number_isNaN));
14816 defmethod(js, number_ctor_obj, "isFinite", 8, js_mkfun(builtin_Number_isFinite));
14817 defmethod(js, number_ctor_obj, "isInteger", 9, js_mkfun(builtin_Number_isInteger));
14818 defmethod(js, number_ctor_obj, "isSafeInteger", 13, js_mkfun(builtin_Number_isSafeInteger));
14819 ant_value_t number_parse_int = defmethod(js, number_ctor_obj, "parseInt", 8, js_mkfun(builtin_parseInt));
14820 ant_value_t number_parse_float = defmethod(js, number_ctor_obj, "parseFloat", 10, js_mkfun(builtin_parseFloat));
14821
14822 js_setprop(js, number_ctor_obj, js_mkstr(js, "MAX_VALUE", 9), tov(1.7976931348623157e+308));
14823 js_setprop(js, number_ctor_obj, js_mkstr(js, "MIN_VALUE", 9), tov(5e-324));
14824 js_setprop(js, number_ctor_obj, js_mkstr(js, "MAX_SAFE_INTEGER", 16), tov(9007199254740991.0));
14825 js_setprop(js, number_ctor_obj, js_mkstr(js, "MIN_SAFE_INTEGER", 16), tov(-9007199254740991.0));
14826 js_setprop(js, number_ctor_obj, js_mkstr(js, "POSITIVE_INFINITY", 17), tov(JS_INF));
14827 js_setprop(js, number_ctor_obj, js_mkstr(js, "NEGATIVE_INFINITY", 17), tov(JS_NEG_INF));
14828 js_setprop(js, number_ctor_obj, js_mkstr(js, "NaN", 3), tov(JS_NAN));
14829 js_setprop(js, number_ctor_obj, js_mkstr(js, "EPSILON", 7), tov(2.220446049250313e-16));
14830
14831 js_setprop_nonconfigurable(js, number_ctor_obj, "prototype", 9, number_proto);
14832 js_setprop(js, number_ctor_obj, ANT_STRING("name"), ANT_STRING("Number"));
14833 ant_value_t number_ctor_func = js_obj_to_func(number_ctor_obj);
14834 js_setprop(js, glob, js_mkstr(js, "Number", 6), number_ctor_func);
14835
14836 ant_value_t bool_ctor_obj = mkobj(js, 0);
14837 set_proto(js, bool_ctor_obj, function_proto);
14838 set_slot(bool_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Boolean));
14839 js_setprop_nonconfigurable(js, bool_ctor_obj, "prototype", 9, boolean_proto);
14840 js_setprop(js, bool_ctor_obj, ANT_STRING("name"), ANT_STRING("Boolean"));
14841 ant_value_t bool_ctor_func = js_obj_to_func(bool_ctor_obj);
14842 js_setprop(js, glob, js_mkstr(js, "Boolean", 7), bool_ctor_func);
14843
14844 ant_value_t arr_ctor_obj = mkobj(js, 0);
14845 set_proto(js, arr_ctor_obj, function_proto);
14846 set_slot(arr_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Array));
14847 js_setprop_readonly_nonconfigurable(js, arr_ctor_obj, "prototype", 9, array_proto);
14848 defmethod(js, arr_ctor_obj, "isArray", 7, js_mkfun(builtin_Array_isArray));
14849 defmethod(js, arr_ctor_obj, "isTemplateObject", 16, js_mkfun(builtin_Array_isTemplateObject));
14850 defmethod(js, arr_ctor_obj, "from", 4, js_mkfun(builtin_Array_from));
14851 defmethod(js, arr_ctor_obj, "of", 2, js_mkfun(builtin_Array_of));
14852 js_setprop(js, arr_ctor_obj, js->length_str, tov(1.0));
14853 js_set_descriptor(js, arr_ctor_obj, "length", 6, JS_DESC_C);
14854 js_setprop(js, arr_ctor_obj, ANT_STRING("name"), ANT_STRING("Array"));
14855 ant_value_t arr_ctor_func = js_obj_to_func(arr_ctor_obj);
14856 js_setprop(js, glob, js_mkstr(js, "Array", 5), arr_ctor_func);
14857
14858 ant_value_t proxy_ctor_obj = mkobj(js, 0);
14859 set_proto(js, proxy_ctor_obj, function_proto);
14860 set_slot(proxy_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Proxy));
14861 js_mark_constructor(proxy_ctor_obj, true);
14862 defmethod(js, proxy_ctor_obj, "revocable", 9, js_mkfun(builtin_Proxy_revocable));
14863 js_setprop(js, proxy_ctor_obj, ANT_STRING("name"), ANT_STRING("Proxy"));
14864 js_setprop(js, glob, js_mkstr(js, "Proxy", 5), js_obj_to_func(proxy_ctor_obj));
14865
14866 ant_value_t p_ctor_obj = mkobj(js, 0);
14867 set_proto(js, p_ctor_obj, function_proto);
14868 set_slot(p_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Promise));
14869
14870 defmethod(js, p_ctor_obj, "resolve", 7, js_mkfun(builtin_Promise_resolve));
14871 defmethod(js, p_ctor_obj, "reject", 6, js_mkfun(builtin_Promise_reject));
14872 defmethod(js, p_ctor_obj, "try", 3, js_mkfun(builtin_Promise_try));
14873 defmethod(js, p_ctor_obj, "withResolvers", 13, js_mkfun(builtin_Promise_withResolvers));
14874 defmethod(js, p_ctor_obj, "all", 3, js_mkfun(builtin_Promise_all));
14875 defmethod(js, p_ctor_obj, "allSettled", 10, js_mkfun(builtin_Promise_allSettled));
14876 defmethod(js, p_ctor_obj, "race", 4, js_mkfun(builtin_Promise_race));
14877 defmethod(js, p_ctor_obj, "any", 3, js_mkfun(builtin_Promise_any));
14878
14879 js_setprop_nonconfigurable(js, p_ctor_obj, "prototype", 9, promise_proto);
14880 js_setprop(js, p_ctor_obj, ANT_STRING("name"), ANT_STRING("Promise"));
14881 js_setprop(js, glob, js_mkstr(js, "Promise", 7), js_obj_to_func(p_ctor_obj));
14882
14883 defalias(js, glob, "parseInt", 8, number_parse_int);
14884 defalias(js, glob, "parseFloat", 10, number_parse_float);
14885 defmethod(js, glob, "eval", 4, js_mkfun(builtin_eval));
14886 defmethod(js, glob, "isNaN", 5, js_mkfun(builtin_global_isNaN));
14887 defmethod(js, glob, "isFinite", 8, js_mkfun(builtin_global_isFinite));
14888 defmethod(js, glob, "btoa", 4, js_mkfun(builtin_btoa));
14889 defmethod(js, glob, "atob", 4, js_mkfun(builtin_atob));
14890
14891 js_setprop(js, glob, js_mkstr(js, "NaN", 3), tov(JS_NAN));
14892 js_set_descriptor(js, glob, "NaN", 3, 0);
14893 js_setprop(js, glob, js_mkstr(js, "Infinity", 8), tov(JS_INF));
14894 js_set_descriptor(js, glob, "Infinity", 8, 0);
14895 js_setprop(js, glob, js_mkstr(js, "undefined", 9), js_mkundef());
14896 js_set_descriptor(js, glob, "undefined", 9, 0);
14897
14898 ant_value_t import_obj = mkobj(js, 0);
14899 set_proto(js, import_obj, function_proto);
14900
14901 set_slot(import_obj, SLOT_CFUNC, js_mkfun(js_builtin_import));
14902 js_setprop(js, glob, js_mkstr(js, "import", 6), js_obj_to_func(import_obj));
14903
14904 js_setprop(js, object_proto, js_mkstr(js, "constructor", 11), obj_func);
14905 js_set_descriptor(js, object_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14906
14907 js_setprop(js, function_proto, js_mkstr(js, "constructor", 11), func_ctor_func);
14908 js_set_descriptor(js, js_as_obj(function_proto), "constructor", 11, JS_DESC_W | JS_DESC_C);
14909
14910 js_setprop(js, array_proto, js_mkstr(js, "constructor", 11), arr_ctor_func);
14911 js_set_descriptor(js, js_as_obj(array_proto), "constructor", 11, JS_DESC_W | JS_DESC_C);
14912
14913 js_setprop(js, string_proto, js_mkstr(js, "constructor", 11), str_ctor_func);
14914 js_set_descriptor(js, string_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14915
14916 js_setprop(js, number_proto, js_mkstr(js, "constructor", 11), number_ctor_func);
14917 js_set_descriptor(js, number_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14918
14919 js_setprop(js, boolean_proto, js_mkstr(js, "constructor", 11), bool_ctor_func);
14920 js_set_descriptor(js, boolean_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
14921
14922 set_proto(js, glob, object_proto);
14923
14924 js->sym.object_proto = object_proto;
14925 js->sym.array_proto = array_proto;
14926 js->owns_mem = false;
14927 js->max_size = 0;
14928
14929 return js;
14930}
14931
14932ant_t *js_create_dynamic() {
14933 ant_t *js = (ant_t *)calloc(1, sizeof(*js));
14934 if (js == NULL) return NULL;
14935 if (js_create(js, sizeof(*js)) == NULL) {
14936 free(js);
14937 return NULL;
14938 }
14939 js->owns_mem = true;
14940 js->vm = sv_vm_create(js, SV_VM_MAIN);
14941 return js;
14942}
14943
14944void js_destroy(ant_t *js) {
14945 if (js == NULL) return;
14946 if (js->vm) {
14947 sv_vm_destroy(js->vm);
14948 js->vm = NULL;
14949 }
14950
14951 js_esm_cleanup_module_cache();
14952 code_arena_reset();
14953 cleanup_lmdb_module();
14954
14955 ant_object_t *lists[] = { js->objects, js->objects_old, js->permanent_objects };
14956 for (int i = 0; i < 3; i++) for (ant_object_t *obj = lists[i]; obj;) {
14957 ant_object_t *next = obj->next;
14958 gc_object_free(js, obj);
14959 obj = next;
14960 }
14961
14962 js->objects = NULL;
14963 js->objects_old = NULL;
14964 js->permanent_objects = NULL;
14965
14966 cleanup_buffer_module();
14967 cleanup_atomics_module(js);
14968
14969 fixed_arena_destroy(&js->obj_arena);
14970 fixed_arena_destroy(&js->closure_arena);
14971 fixed_arena_destroy(&js->upvalue_arena);
14972
14973 free(js->prop_refs);
14974 js->prop_refs = NULL;
14975 js->prop_refs_len = js->prop_refs_cap = 0;
14976
14977 free(js->c_roots);
14978 js->c_roots = NULL;
14979 js->c_root_count = js->c_root_cap = 0;
14980
14981 free(js->pending_rejections.items);
14982 js->pending_rejections.items = NULL;
14983 js->pending_rejections.len = js->pending_rejections.cap = 0;
14984
14985 free(js->cfunc_promote_cache.cfunc_ptr);
14986 free(js->cfunc_promote_cache.promoted);
14987 js->cfunc_promote_cache.cfunc_ptr = NULL;
14988 js->cfunc_promote_cache.promoted = NULL;
14989 js->cfunc_promote_cache.len = js->cfunc_promote_cache.cap = 0;
14990
14991 for (uint16_t i = 0; i < js->cfunc_name_cache.len; i++) free(
14992 (void *)(uintptr_t)vdata(js->cfunc_name_cache.named[i])
14993 );
14994
14995 free(js->cfunc_name_cache.base_meta);
14996 free(js->cfunc_name_cache.name_ptr);
14997 free(js->cfunc_name_cache.named);
14998
14999 js->cfunc_name_cache.base_meta = NULL;
15000 js->cfunc_name_cache.name_ptr = NULL;
15001 js->cfunc_name_cache.named = NULL;
15002 js->cfunc_name_cache.len = js->cfunc_name_cache.cap = 0;
15003
15004 js_pool_destroy(&js->pool.rope);
15005 js_pool_destroy(&js->pool.symbol);
15006 js_pool_destroy(&js->pool.permanent);
15007
15008 js_class_pool_destroy(&js->pool.bigint);
15009 js_string_pool_destroy(&js->pool.string);
15010
15011 destroy_runtime(js);
15012 if (js->owns_mem) free(js);
15013}
15014
15015inline double js_getnum(ant_value_t value) { return tod(value); }
15016inline void js_setstackbase(ant_t *js, void *base) { js->cstk.base = base; js->cstk.main_base = base; }
15017inline void js_setstacklimit(ant_t *js, size_t max) { js->cstk.limit = max; }
15018inline void js_set_filename(ant_t *js, const char *filename) { js->filename = filename; }
15019
15020inline ant_value_t js_mkundef(void) { return mkval(T_UNDEF, 0); }
15021inline ant_value_t js_mknull(void) { return mkval(T_NULL, 0); }
15022inline ant_value_t js_mknum(double value) { return tov(value); }
15023inline ant_value_t js_mkobj(ant_t *js) { return mkobj(js, 0); }
15024inline ant_value_t js_glob(ant_t *js) { return js->global; }
15025
15026ant_value_t js_mkfun_meta(const ant_cfunc_meta_t *meta) {
15027 return mkval(T_CFUNC, (uintptr_t)meta);
15028}
15029
15030ant_value_t js_mkfun_dyn(ant_cfunc_t fn) {
15031 typedef struct dyn_cfunc_meta_entry {
15032 ant_cfunc_t fn;
15033 ant_cfunc_meta_t meta;
15034 struct dyn_cfunc_meta_entry *next;
15035 } dyn_cfunc_meta_entry_t;
15036
15037 static dyn_cfunc_meta_entry_t *head = NULL;
15038 for (dyn_cfunc_meta_entry_t *it = head; it; it = it->next) {
15039 if (it->fn == fn) return js_mkfun_meta(&it->meta);
15040 }
15041
15042 dyn_cfunc_meta_entry_t *entry = ant_calloc(sizeof(dyn_cfunc_meta_entry_t));
15043 if (!entry) return mkval(T_ERR, 0);
15044
15045 entry->fn = fn;
15046 entry->meta.fn = fn;
15047 entry->next = head;
15048 head = entry;
15049
15050 return js_mkfun_meta(&entry->meta);
15051}
15052
15053inline ant_value_t js_getthis(ant_t *js) { return js->this_val; }
15054inline void js_setthis(ant_t *js, ant_value_t val) { js->this_val = val; }
15055inline ant_value_t js_getcurrentfunc(ant_t *js) { return js->current_func; }
15056
15057static ant_value_t js_cfunc_name_value(ant_t *js, ant_value_t cfunc) {
15058 const ant_cfunc_meta_t *meta = js_as_cfunc_meta(cfunc);
15059 if (!meta || !meta->name) return js_mkundef();
15060 return js_mkstr(js, meta->name, strlen(meta->name));
15061}
15062
15063static ant_value_t js_cfunc_length_value(ant_value_t cfunc) {
15064 return tov((double)js_cfunc_length(cfunc));
15065}
15066
15067static bool js_cfunc_has_prototype(ant_value_t cfunc) {
15068 const ant_cfunc_meta_t *meta = js_as_cfunc_meta(cfunc);
15069 return meta && (meta->flags & CFUNC_HAS_PROTOTYPE) != 0;
15070}
15071
15072static ant_value_t setup_func_prototype_property(ant_t *js, ant_value_t func, bool mark_constructor) {
15073 ant_value_t func_obj = (vtype(func) == T_FUNC)
15074 ? js_func_obj(func)
15075 : js_as_obj(func);
15076
15077 ant_value_t proto_obj = mkobj(js, 0);
15078 if (is_err(proto_obj)) return proto_obj;
15079
15080 ant_value_t object_proto = js->sym.object_proto;
15081 if (vtype(object_proto) == T_OBJ) js_set_proto_init(proto_obj, object_proto);
15082
15083 ant_value_t constructor_key = js_mkstr(js, "constructor", 11);
15084 if (is_err(constructor_key)) return constructor_key;
15085
15086 ant_value_t res = mkprop(js, proto_obj, constructor_key, func, 0);
15087 if (is_err(res)) return res;
15088 js_set_descriptor(js, proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C);
15089
15090 ant_value_t prototype_key = js_mkstr(js, "prototype", 9);
15091 if (is_err(prototype_key)) return prototype_key;
15092
15093 ant_offset_t existing = lkp(js, func_obj, "prototype", 9);
15094 if (existing > 0) {
15095 if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant");
15096 js_saveval(js, existing, proto_obj);
15097 } else {
15098 res = mkprop(js, func_obj, prototype_key, proto_obj, 0);
15099 if (is_err(res)) return res;
15100 }
15101
15102 js_set_descriptor(js, func_obj, "prototype", 9, JS_DESC_W);
15103 if (mark_constructor) js_mark_constructor(func_obj, true);
15104
15105 return js_mkundef();
15106}
15107
15108ant_value_t js_cfunc_expose_named(ant_t *js, ant_value_t cfunc, const char *name, size_t name_len) {
15109 if (vtype(cfunc) != T_CFUNC || !name) return cfunc;
15110
15111 const ant_cfunc_meta_t *base = js_as_cfunc_meta(cfunc);
15112 if (!base) return cfunc;
15113
15114 if (
15115 base->name &&
15116 strlen(base->name) == name_len &&
15117 memcmp(base->name, name, name_len) == 0
15118 ) return cfunc;
15119
15120 const char *interned = intern_string(name, name_len);
15121 if (!interned) return js_mkerr(js, "oom");
15122
15123 for (uint16_t i = 0; i < js->cfunc_name_cache.len; i++) if (
15124 js->cfunc_name_cache.base_meta[i] == base &&
15125 js->cfunc_name_cache.name_ptr[i] == interned
15126 ) return js->cfunc_name_cache.named[i];
15127
15128 ant_cfunc_meta_t *named_meta = ant_calloc(sizeof(ant_cfunc_meta_t));
15129 if (!named_meta) return js_mkerr(js, "oom");
15130
15131 named_meta->fn = base->fn;
15132 named_meta->name = interned;
15133 named_meta->length = base->length;
15134 named_meta->flags = base->flags;
15135
15136 if (js->cfunc_name_cache.len >= js->cfunc_name_cache.cap) {
15137 uint16_t new_cap = js->cfunc_name_cache.cap ? js->cfunc_name_cache.cap * 2 : 8;
15138
15139 const ant_cfunc_meta_t **new_base = realloc(
15140 js->cfunc_name_cache.base_meta,
15141 new_cap * sizeof(ant_cfunc_meta_t *)
15142 );
15143
15144 const char **new_names = realloc(js->cfunc_name_cache.name_ptr, new_cap * sizeof(const char *));
15145 ant_value_t *new_named = realloc(js->cfunc_name_cache.named, new_cap * sizeof(ant_value_t));
15146
15147 if (!new_base || !new_names || !new_named) {
15148 free(named_meta);
15149 return js_mkerr(js, "oom");
15150 }
15151
15152 js->cfunc_name_cache.base_meta = new_base;
15153 js->cfunc_name_cache.name_ptr = new_names;
15154 js->cfunc_name_cache.named = new_named;
15155 js->cfunc_name_cache.cap = new_cap;
15156 }
15157
15158 ant_value_t named = js_mkfun_meta(named_meta);
15159 uint16_t idx = js->cfunc_name_cache.len++;
15160 js->cfunc_name_cache.base_meta[idx] = base;
15161 js->cfunc_name_cache.name_ptr[idx] = interned;
15162 js->cfunc_name_cache.named[idx] = named;
15163
15164 return named;
15165}
15166
15167static ant_value_t js_expose_cfunc_for_key(ant_t *js, ant_value_t value, const char *key, size_t key_len) {
15168 if (vtype(value) != T_CFUNC) return value;
15169 return js_cfunc_expose_named(js, value, key, key_len);
15170}
15171
15172ant_value_t js_cfunc_promote(ant_t *js, ant_value_t cfunc) {
15173 uintptr_t ptr = vdata(cfunc);
15174 const ant_cfunc_meta_t *meta = js_as_cfunc_meta(cfunc);
15175
15176 for (uint8_t i = 0; i < js->cfunc_promote_cache.len; i++) {
15177 if (js->cfunc_promote_cache.cfunc_ptr[i] == ptr)
15178 return js->cfunc_promote_cache.promoted[i];
15179 }
15180
15181 ant_value_t fn_obj = mkobj(js, 0);
15182 set_slot(fn_obj, SLOT_CFUNC, cfunc);
15183
15184 ant_value_t proto = get_prototype_for_type(js, T_CFUNC);
15185 if (is_object_type(proto)) {
15186 ant_object_t *obj_ptr = js_obj_ptr(js_as_obj(fn_obj));
15187 if (obj_ptr) obj_ptr->proto = proto;
15188 }
15189
15190 ant_value_t length_result = js_setprop(js, fn_obj, ANT_STRING("length"), js_cfunc_length_value(cfunc));
15191 if (is_err(length_result)) return length_result;
15192 js_set_descriptor(js, fn_obj, "length", 6, JS_DESC_C);
15193
15194 if (meta && meta->name) {
15195 ant_value_t name_result = js_setprop(js, fn_obj, ANT_STRING("name"), js_cfunc_name_value(js, cfunc));
15196 if (is_err(name_result)) return name_result;
15197 js_set_descriptor(js, fn_obj, "name", 4, JS_DESC_C);
15198 }
15199
15200 ant_value_t promoted = js_obj_to_func(fn_obj);
15201
15202 if (js_cfunc_has_prototype(cfunc)) {
15203 ant_value_t proto_result = setup_func_prototype_property(js, promoted, false);
15204 if (is_err(proto_result)) return proto_result;
15205 }
15206
15207 if (js->cfunc_promote_cache.len >= js->cfunc_promote_cache.cap) {
15208 uint8_t new_cap = js->cfunc_promote_cache.cap ? js->cfunc_promote_cache.cap * 2 : 4;
15209 uintptr_t *new_ptrs = realloc(js->cfunc_promote_cache.cfunc_ptr, new_cap * sizeof(uintptr_t));
15210 ant_value_t *new_vals = realloc(js->cfunc_promote_cache.promoted, new_cap * sizeof(ant_value_t));
15211
15212 if (new_ptrs && new_vals) {
15213 js->cfunc_promote_cache.cfunc_ptr = new_ptrs;
15214 js->cfunc_promote_cache.promoted = new_vals;
15215 js->cfunc_promote_cache.cap = new_cap;
15216 }
15217 }
15218
15219 if (js->cfunc_promote_cache.len < js->cfunc_promote_cache.cap) {
15220 uint8_t idx = js->cfunc_promote_cache.len++;
15221 js->cfunc_promote_cache.cfunc_ptr[idx] = ptr;
15222 js->cfunc_promote_cache.promoted[idx] = promoted;
15223 }
15224
15225 return promoted;
15226}
15227
15228ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_params_t), ant_value_t data) {
15229 ant_value_t cfunc = js_mkfun_dyn(fn);
15230 ant_value_t fn_obj = mkobj(js, 0);
15231
15232 set_slot(fn_obj, SLOT_CFUNC, cfunc);
15233 set_slot(fn_obj, SLOT_DATA, data);
15234
15235 return js_obj_to_func(fn_obj);
15236}
15237
15238ant_value_t js_heavy_mkfun_native(ant_t *js, ant_value_t (*fn)(ant_params_t), void *ptr, uint32_t tag) {
15239 ant_value_t cfunc = js_mkfun_dyn(fn);
15240 ant_value_t fn_obj = mkobj(js, 0);
15241
15242 set_slot(fn_obj, SLOT_CFUNC, cfunc);
15243 js_set_native(fn_obj, ptr, tag);
15244
15245 return js_obj_to_func(fn_obj);
15246}
15247
15248void js_set(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) {
15249 size_t key_len = strlen(key);
15250 val = js_expose_cfunc_for_key(js, val, key, key_len);
15251 if (is_err(val)) return;
15252
15253 if (vtype(obj) == T_OBJ) {
15254 ant_offset_t existing = lkp(js, obj, key, key_len);
15255 if (existing > 0) {
15256 if (is_const_prop(js, existing)) {
15257 js_mkerr(js, "assignment to constant");
15258 return;
15259 }
15260 js_saveval(js, existing, val);
15261 } else {
15262 ant_value_t key_str = js_mkstr(js, key, key_len);
15263 mkprop(js, obj, key_str, val, 0);
15264 }
15265 } else if (vtype(obj) == T_FUNC) {
15266 ant_value_t func_obj = js_func_obj(obj);
15267 ant_offset_t existing = lkp(js, func_obj, key, key_len);
15268 if (existing > 0) {
15269 if (is_const_prop(js, existing)) {
15270 js_mkerr(js, "assignment to constant");
15271 return;
15272 }
15273 js_saveval(js, existing, val);
15274 } else {
15275 ant_value_t key_str = js_mkstr(js, key, key_len);
15276 mkprop(js, func_obj, key_str, val, 0);
15277 }
15278 }
15279}
15280
15281void js_set_exact(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) {
15282 size_t key_len = strlen(key);
15283 const char *interned = intern_string(key, key_len);
15284
15285 if (!interned) {
15286 js_mkerr(js, "oom");
15287 return;
15288 }
15289
15290 if (vtype(obj) == T_OBJ) {
15291 ant_offset_t existing = lkp(js, obj, key, key_len);
15292 if (existing > 0) {
15293 if (is_const_prop(js, existing)) {
15294 js_mkerr(js, "assignment to constant");
15295 return;
15296 }
15297 js_saveval(js, existing, val);
15298 } else mkprop_interned_exact(js, obj, interned, val, 0);
15299 } else if (vtype(obj) == T_FUNC) {
15300 ant_value_t func_obj = js_func_obj(obj);
15301 ant_offset_t existing = lkp(js, func_obj, key, key_len);
15302 if (existing > 0) {
15303 if (is_const_prop(js, existing)) {
15304 js_mkerr(js, "assignment to constant");
15305 return;
15306 }
15307 js_saveval(js, existing, val);
15308 } else mkprop_interned_exact(js, func_obj, interned, val, 0);
15309 }
15310}
15311
15312void js_set_sym(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t val) {
15313 if (vtype(sym) != T_SYMBOL) return;
15314 ant_offset_t sym_off = (ant_offset_t)vdata(sym);
15315
15316 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj);
15317 else if (is_object_type(obj)) obj = js_as_obj(obj);
15318 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) return;
15319
15320 ant_offset_t existing = lkp_sym(js, obj, sym_off);
15321 if (existing > 0) {
15322 if (is_const_prop(js, existing)) return;
15323 js_saveval(js, existing, val);
15324 } else mkprop(js, obj, sym, val, 0);
15325}
15326
15327void js_set_symbol(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) {
15328 ant_value_t sym = js_mksym_for(js, key);
15329 if (is_err(sym)) return;
15330 js_set_sym(js, obj, sym, val);
15331}
15332
15333ant_value_t js_get_sym_with_receiver(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t receiver) {
15334 if (vtype(sym) != T_SYMBOL) return js_mkundef();
15335 ant_offset_t sym_off = (ant_offset_t)vdata(sym);
15336
15337 if (vtype(obj) == T_CFUNC) {
15338 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
15339 if (vtype(promoted) == T_FUNC) return js_get_sym_with_receiver(js, promoted, sym, receiver);
15340 }
15341
15342 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj);
15343 uint8_t ot = vtype(obj);
15344
15345 if (!is_object_type(obj)) {
15346 if (ot == T_STR || ot == T_NUM || ot == T_BOOL || ot == T_BIGINT || ot == T_SYMBOL || ot == T_CFUNC) {
15347 ant_value_t proto = get_prototype_for_type(js, ot);
15348 if (!is_object_type(proto)) return js_mkundef();
15349 obj = js_as_obj(proto);
15350 } else return js_mkundef();
15351 } else obj = js_as_obj(obj);
15352
15353 if (is_proxy(obj))
15354 return proxy_get_val(js, obj, sym);
15355
15356 ant_value_t cur = obj;
15357 proto_overflow_guard_t guard;
15358 proto_overflow_guard_init(&guard);
15359
15360 while (is_object_type(cur)) {
15361 ant_value_t cur_obj = js_as_obj(cur);
15362 prop_meta_t meta;
15363 if (lookup_symbol_prop_meta(cur_obj, sym_off, &meta)) {
15364 if (meta.has_getter) {
15365 ant_value_t g = meta.getter;
15366 if (vtype(g) == T_FUNC || vtype(g) == T_CFUNC)
15367 return sv_vm_call(js->vm, js, g, receiver, NULL, 0, NULL, false);
15368 return js_mkundef();
15369 }
15370 if (meta.has_setter && !meta.has_getter) return js_mkundef();
15371 break;
15372 }
15373
15374 ant_value_t proto = get_proto(js, cur_obj);
15375 if (!is_object_type(proto)) break;
15376 cur = js_as_obj(proto);
15377 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
15378 }
15379
15380 ant_offset_t off = lkp_sym_proto(js, obj, sym_off);
15381 if (off == 0) return js_mkundef();
15382 return propref_load(js, off);
15383}
15384
15385ant_value_t js_get_sym(ant_t *js, ant_value_t obj, ant_value_t sym) {
15386 return js_get_sym_with_receiver(js, obj, sym, obj);
15387}
15388
15389ant_value_t js_get_symbol(ant_t *js, ant_value_t obj, const char *key) {
15390 ant_value_t sym = js_mksym_for(js, key);
15391 if (is_err(sym)) return sym;
15392 return js_get_sym(js, obj, sym);
15393}
15394
15395static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t *out) {
15396 size_t key_len = strlen(key);
15397
15398 if (vtype(obj) == T_CFUNC) {
15399 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
15400 if (vtype(promoted) == T_FUNC) return js_try_get(js, promoted, key, out);
15401 if (js_cfunc_try_get_own(js, obj, key, key_len, out)) return true;
15402 ant_offset_t off = lkp_proto(js, obj, key, key_len);
15403 if (off != 0) {
15404 *out = propref_load(js, off);
15405 return true;
15406 }
15407 return false;
15408 }
15409
15410 if (vtype(obj) == T_FUNC) {
15411 if (sv_vm_is_strict(js->vm) &&
15412 ((key_len == 6 && memcmp(key, "caller", 6) == 0) ||
15413 (key_len == 9 && memcmp(key, "arguments", 9) == 0))) {
15414 *out = js_mkerr_typed(
15415 js, JS_ERR_TYPE,
15416 "'%.*s' not allowed on functions in strict mode",
15417 (int)key_len, key
15418 );
15419 return true;
15420 }
15421
15422 ant_value_t func_obj = js_func_obj(obj);
15423 ant_value_t import_meta = js_get_module_ctx_import_meta(js, js_get_slot(func_obj, SLOT_MODULE_CTX));
15424 if (vtype(import_meta) == T_UNDEF) import_meta = js_get_current_import_meta(js);
15425 if (key_len == 4 && memcmp(key, "meta", 4) == 0 && vtype(import_meta) != T_UNDEF) {
15426 ant_value_t cfunc = js_get_slot(func_obj, SLOT_CFUNC);
15427 if (vtype(cfunc) == T_CFUNC && js_cfunc_same_entrypoint(cfunc, js_builtin_import)) {
15428 *out = import_meta;
15429 return true;
15430 }
15431 }
15432 ant_offset_t off = lkp(js, func_obj, key, key_len);
15433 if (off == 0) {
15434 ant_value_t accessor_result;
15435 if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) {
15436 *out = accessor_result;
15437 return true;
15438 }
15439 return false;
15440 }
15441
15442 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
15443 if (prop_meta && prop_meta->has_getter) {
15444 ant_value_t accessor_result;
15445 if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) {
15446 *out = accessor_result;
15447 return true;
15448 }
15449 }
15450
15451 *out = propref_load(js, off);
15452 return true;
15453 }
15454
15455 if (array_obj_ptr(obj)) {
15456 if (
15457 ((key_len == 6 && memcmp(key, "callee", 6) == 0) ||
15458 (key_len == 6 && memcmp(key, "caller", 6) == 0)) &&
15459 get_slot(obj, SLOT_STRICT_ARGS) == js_true
15460 ) {
15461 *out = js_mkerr_typed(js, JS_ERR_TYPE, "'%.*s' not allowed on strict arguments", (int)key_len, key);
15462 return true;
15463 }
15464
15465 if (is_length_key(key, key_len)) {
15466 *out = tov((double)get_array_length(js, obj));
15467 return true;
15468 }
15469
15470 unsigned long idx;
15471 ant_offset_t arr_len = get_array_length(js, obj);
15472
15473 if (parse_array_index(key, key_len, arr_len, &idx)) {
15474 if (js_arguments_state(obj)) {
15475 *out = js_arguments_getter(js, obj, key, key_len);
15476 return true;
15477 }
15478 if (arr_has(js, obj, (ant_offset_t)idx)) {
15479 *out = arr_get(js, obj, (ant_offset_t)idx);
15480 return true;
15481 }
15482
15483 return false;
15484 }
15485
15486 ant_value_t arr_obj = js_as_obj(obj);
15487 ant_offset_t off = lkp(js, arr_obj, key, key_len);
15488
15489 if (off == 0) {
15490 ant_value_t accessor_result;
15491 if (try_accessor_getter(js, arr_obj, key, key_len, &accessor_result)) {
15492 *out = accessor_result; return true;
15493 }
15494
15495 return false;
15496 }
15497
15498 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
15499 if (prop_meta && prop_meta->has_getter) {
15500 ant_value_t accessor_result;
15501 if (try_accessor_getter(js, arr_obj, key, key_len, &accessor_result)) {
15502 *out = accessor_result;
15503 return true;
15504 }}
15505
15506 *out = propref_load(js, off);
15507 return true;
15508 }
15509
15510 uint8_t t = vtype(obj);
15511 bool is_promise = (t == T_PROMISE);
15512 if (t == T_OBJ && is_proxy(obj)) {
15513 *out = proxy_get(js, obj, key, key_len);
15514 return true;
15515 }
15516
15517 if (t == T_STR || t == T_NUM || t == T_BOOL) {
15518 if (t == T_STR && is_length_key(key, key_len)) {
15519 ant_offset_t byte_len = 0; ant_offset_t str_off = vstr(js, obj, &byte_len);
15520 const char *str_data = (const char *)(uintptr_t)(str_off);
15521 *out = tov((double)utf16_strlen(str_data, byte_len));
15522 return true;
15523 }
15524
15525 if (t == T_STR && js_try_get_string_index(js, obj, key, key_len, out)) return true;
15526 ant_value_t boxed = mkobj(js, 0);
15527
15528 js_set_slot(js_as_obj(boxed), SLOT_PRIMITIVE, obj);
15529 obj = boxed; t = T_OBJ;
15530 }
15531
15532 if (is_promise) obj = js_as_obj(obj);
15533 else if (t != T_OBJ) return false;
15534 ant_offset_t off = lkp(js, obj, key, key_len);
15535
15536 if (off == 0) {
15537 ant_value_t result = try_dynamic_getter(js, obj, key, key_len);
15538 if (vtype(result) != T_UNDEF) { *out = result; return true; }
15539 }
15540
15541 if (off == 0 && is_promise) {
15542 ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7);
15543 if (vtype(promise_proto) != T_UNDEF && vtype(promise_proto) != T_NULL) {
15544 off = lkp(js, promise_proto, key, key_len);
15545 if (off != 0) { *out = propref_load(js, off); return true; }
15546 }
15547 }
15548
15549 if (off == 0) {
15550 ant_value_t accessor_result;
15551 if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) {
15552 *out = accessor_result; return true;
15553 }
15554 return false;
15555 }
15556
15557 const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off);
15558 if (prop_meta && prop_meta->has_getter) {
15559 ant_value_t accessor_result;
15560 if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) {
15561 *out = accessor_result; return true;
15562 }
15563 }
15564
15565 *out = propref_load(js, off);
15566 return true;
15567}
15568
15569ant_value_t js_get(ant_t *js, ant_value_t obj, const char *key) {
15570 ant_value_t val;
15571 if (js_try_get(js, obj, key, &val)) return val;
15572 return js_mkundef();
15573}
15574
15575ant_value_t js_getprop_proto(ant_t *js, ant_value_t obj, const char *key) {
15576 if (vtype(obj) == T_CFUNC) {
15577 ant_value_t promoted = js_cfunc_lookup_promoted(js, obj);
15578 if (vtype(promoted) == T_FUNC) obj = promoted;
15579 }
15580 size_t key_len = strlen(key);
15581 ant_offset_t off = lkp_proto(js, obj, key, key_len);
15582 return off == 0 ? js_mkundef() : propref_load(js, off);
15583}
15584
15585ant_value_t js_getprop_fallback(ant_t *js, ant_value_t obj, const char *name) {
15586 ant_value_t val;
15587 if (js_try_get(js, obj, name, &val)) return val;
15588
15589 return js_getprop_proto(js, obj, name);
15590}
15591
15592ant_value_t js_getprop_super(ant_t *js, ant_value_t super_obj, ant_value_t receiver, const char *name) {
15593 if (!name) return js_mkundef();
15594
15595 if (vtype(super_obj) == T_FUNC) super_obj = js_func_obj(super_obj);
15596 if (!is_object_type(super_obj)) return js_mkundef();
15597
15598 size_t key_len = strlen(name);
15599 if (is_proxy(super_obj)) return proxy_get(js, super_obj, name, key_len);
15600
15601 const char *key_intern = intern_string(name, key_len);
15602 if (!key_intern) return js_mkundef();
15603
15604 ant_value_t cur = super_obj;
15605 proto_overflow_guard_t guard;
15606 proto_overflow_guard_init(&guard);
15607 while (is_object_type(cur)) {
15608 ant_value_t cur_obj = js_as_obj(cur);
15609 ant_object_t *cur_ptr = js_obj_ptr(cur_obj);
15610 bool handled = false;
15611
15612 if (cur_ptr && cur_ptr->shape) {
15613 int32_t slot = ant_shape_lookup_interned(cur_ptr->shape, key_intern);
15614 if (slot >= 0) {
15615 const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot);
15616 if (prop && prop->has_getter) {
15617 ant_value_t getter = prop->getter;
15618 if (vtype(getter) == T_FUNC || vtype(getter) == T_CFUNC)
15619 return sv_vm_call(js->vm, js, getter, receiver, NULL, 0, NULL, false);
15620 return js_mkundef();
15621 }
15622 if (prop && prop->has_setter) return js_mkundef();
15623 handled = true;
15624 }
15625 }
15626
15627 if (!handled && cur_ptr && cur_ptr->is_exotic) {
15628 descriptor_entry_t *desc = lookup_descriptor(cur_obj, name, key_len);
15629 if (desc) {
15630 if (desc->has_getter) {
15631 ant_value_t getter = desc->getter;
15632 if (vtype(getter) == T_FUNC || vtype(getter) == T_CFUNC)
15633 return sv_vm_call(js->vm, js, getter, receiver, NULL, 0, NULL, false);
15634 return js_mkundef();
15635 }
15636 if (desc->has_setter) return js_mkundef();
15637 }
15638 }
15639
15640 ant_offset_t prop_off = lkp_interned(js, cur_obj, key_intern, key_len);
15641 if (prop_off != 0) return propref_load(js, prop_off);
15642
15643 ant_value_t proto = get_proto(js, cur_obj);
15644 if (!is_object_type(proto)) break;
15645 cur = proto;
15646 if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break;
15647 }
15648
15649 return js_mkundef();
15650}
15651
15652typedef struct {
15653 bool (*callback)(ant_t *js, ant_value_t value, void *udata);
15654 void *udata;
15655} js_iter_ctx_t;
15656
15657static iter_action_t js_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) {
15658 js_iter_ctx_t *ictx = (js_iter_ctx_t *)ctx;
15659 return ictx->callback(js, value, ictx->udata) ? ITER_CONTINUE : ITER_BREAK;
15660}
15661
15662bool js_iter(ant_t *js, ant_value_t iterable, bool (*callback)(ant_t *js, ant_value_t value, void *udata), void *udata) {
15663 js_iter_ctx_t ctx = { .callback = callback, .udata = udata };
15664 ant_value_t result = iter_foreach(js, iterable, js_iter_cb, &ctx);
15665 return !is_err(result);
15666}
15667
15668char *js_getstr(ant_t *js, ant_value_t value, size_t *len) {
15669 if (vtype(value) != T_STR) return NULL;
15670 ant_offset_t n, off = vstr(js, value, &n);
15671 if (len != NULL) *len = n;
15672 return (char *)(uintptr_t)off;
15673}
15674
15675void js_merge_obj(ant_t *js, ant_value_t dst, ant_value_t src) {
15676 if (vtype(dst) != T_OBJ || vtype(src) != T_OBJ) return;
15677 ant_value_t as_src = js_as_obj(src);
15678 ant_object_t *src_obj = js_obj_ptr(as_src);
15679 if (!src_obj || !src_obj->shape) return;
15680
15681 uint32_t count = ant_shape_count(src_obj->shape);
15682 for (uint32_t i = 0; i < count; i++) {
15683 const ant_shape_prop_t *prop = ant_shape_prop_at(src_obj->shape, i);
15684 if (!prop || prop->type != ANT_SHAPE_KEY_STRING) continue;
15685
15686 const char *key = prop->key.interned;
15687 ant_value_t val = (i < src_obj->prop_count) ? ant_object_prop_get_unchecked(src_obj, i) : js_mkundef();
15688 js_setprop(js, dst, js_mkstr(js, key, strlen(key)), val);
15689 }
15690}
15691
15692bool js_chkargs(ant_value_t *args, int nargs, const char *spec) {
15693 int i = 0, ok = 1;
15694 for (; ok && i < nargs && spec[i]; i++) {
15695 uint8_t t = vtype(args[i]), c = (uint8_t) spec[i];
15696 ok = (c == 'b' && t == T_BOOL) || (c == 'd' && t == T_NUM) ||
15697 (c == 's' && t == T_STR) || (c == 'j');
15698 }
15699 if (spec[i] != '\0' || i != nargs) ok = 0;
15700 return ok;
15701}
15702
15703static ant_value_t js_eval_bytecode_mode(ant_t *js, const char *buf, size_t len, sv_compile_mode_t mode, bool parse_strict) {
15704 if (len == (size_t)~0U) len = strlen(buf);
15705
15706 code_arena_mark_t parse_mark = parse_arena_mark();
15707 sv_ast_t *program = sv_parse(js, buf, (ant_offset_t)len, parse_strict);
15708
15709 if (!program) {
15710 parse_arena_rewind(parse_mark);
15711 if (js->thrown_exists) return mkval(T_ERR, 0);
15712 return js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "Unexpected parse error");
15713 }
15714
15715 if (mode == SV_COMPILE_MODULE) {
15716 ant_value_t ns = js_module_eval_active_ns(js);
15717 if (is_object_type(ns)) esm_predeclare_exports(js, program, ns);
15718 }
15719
15720 sv_func_t *func = sv_compile(js, program, mode, buf, (ant_offset_t)len);
15721 parse_arena_rewind(parse_mark);
15722 if (!func) {
15723 if (js->thrown_exists) return mkval(T_ERR, 0);
15724 return js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "Unexpected compile error");
15725 }
15726
15727 js_clear_error_site(js);
15728 ant_value_t result;
15729 // TODO: this-newtarget-frame-migration
15730 ant_value_t saved_this = js->this_val;
15731
15732 if (sv_dump_bytecode_unlikely) sv_disasm(js, func, js->filename);
15733 if (func->is_tla) result = sv_execute_entry_tla(js, func, js->this_val);
15734 else result = sv_execute_entry(sv_vm_get_active(js), func, js->this_val, NULL, 0);
15735
15736 js->this_val = saved_this;
15737 return result;
15738}
15739
15740ant_value_t js_eval_bytecode(ant_t *js, const char *buf, size_t len) {
15741 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_SCRIPT, false);
15742}
15743
15744ant_value_t js_eval_bytecode_module(ant_t *js, const char *buf, size_t len) {
15745 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_MODULE, false);
15746}
15747
15748ant_value_t js_eval_bytecode_eval(ant_t *js, const char *buf, size_t len) {
15749 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_EVAL, false);
15750}
15751
15752ant_value_t js_eval_bytecode_eval_with_strict(ant_t *js, const char *buf, size_t len, bool inherit_strict) {
15753 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_EVAL, inherit_strict);
15754}
15755
15756ant_value_t js_eval_bytecode_repl(ant_t *js, const char *buf, size_t len) {
15757 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_REPL, false);
15758}
15759
15760static inline ant_value_t sv_call_cfunc(ant_params_t, ant_bind_t) {
15761 ant_value_t saved_this = js->this_val;
15762 js->this_val = this_val;
15763 ant_value_t res = js_as_cfunc(func)(js, args, nargs);
15764 js->this_val = saved_this;
15765 return res;
15766}
15767
15768static inline ant_value_t sv_call_slot_cfunc(ant_params_t, ant_bind_t, ant_value_t cfunc_slot) {
15769 ant_value_t saved_func = js->current_func;
15770 ant_value_t saved_this = js->this_val;
15771 js->current_func = func;
15772 js->this_val = this_val;
15773 ant_value_t res = js_as_cfunc(cfunc_slot)(js, args, nargs);
15774 js->current_func = saved_func;
15775 js->this_val = saved_this;
15776 return res;
15777}
15778
15779
15780static inline ant_value_t sv_call_object_builtin(ant_params_t, ant_value_t this_val) {
15781 ant_value_t saved_this = js->this_val;
15782 js->this_val = this_val;
15783 ant_value_t res = builtin_Object(js, args, nargs);
15784 js->this_val = saved_this;
15785 return res;
15786}
15787
15788ant_value_t sv_call_native(
15789 ant_t *js, ant_value_t func, ant_value_t this_val,
15790 ant_value_t *args, int nargs
15791) {
15792 if (vtype(func) == T_CFUNC) return sv_call_cfunc(js, args, nargs, func, this_val);
15793
15794 if (vtype(func) == T_FUNC) {
15795 ant_value_t func_obj = js_func_obj(func);
15796 ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC);
15797
15798 if (vtype(cfunc_slot) == T_CFUNC)
15799 return sv_call_slot_cfunc(js, args, nargs, func, this_val, cfunc_slot);
15800
15801 ant_value_t builtin_slot = get_slot(func_obj, SLOT_BUILTIN);
15802 if (vtype(builtin_slot) == T_NUM && (int)tod(builtin_slot) == BUILTIN_OBJECT)
15803 return sv_call_object_builtin(js, args, nargs, this_val);
15804 }
15805
15806 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not a function", typestr(vtype(func)));
15807}
15808
15809typedef struct {
15810 ant_t *js;
15811 ant_object_t *obj;
15812 uint32_t index;
15813} prop_iter_ctx_t;
15814
15815ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj) {
15816 ant_iter_t iter = {.ctx = NULL, .off = 0};
15817 uint8_t t = vtype(obj);
15818 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return iter;
15819
15820 prop_iter_ctx_t *ctx = calloc(1, sizeof(*ctx));
15821 if (!ctx) return iter;
15822
15823 ctx->js = js;
15824 ctx->obj = js_obj_ptr(js_as_obj(obj));
15825 ctx->index = 0;
15826
15827 if (!ctx->obj || !ctx->obj->shape) {
15828 free(ctx);
15829 return iter;
15830 }
15831
15832 iter.ctx = ctx;
15833 return iter;
15834}
15835
15836bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value) {
15837 ant_iter_key_t meta = {0};
15838
15839 while (js_prop_iter_next_key(iter, &meta, value)) {
15840 if (meta.is_symbol) continue;
15841 if (key) *key = meta.str;
15842 if (key_len) *key_len = meta.key_len;
15843 return true;
15844 }
15845
15846 return false;
15847}
15848
15849bool js_prop_iter_next_key(ant_iter_t *iter, ant_iter_key_t *key_out, ant_value_t *value) {
15850 if (!iter || !iter->ctx) return false;
15851 prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx;
15852
15853 ant_object_t *obj = ctx->obj;
15854 if (!obj || !obj->shape) return false;
15855
15856 uint32_t count = ant_shape_count(obj->shape);
15857 while (ctx->index < count) {
15858 uint32_t i = ctx->index++;
15859 const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i);
15860 if (!prop) continue;
15861 if (i >= obj->prop_count) continue;
15862
15863 if (key_out) {
15864 key_out->slot = i;
15865 key_out->is_symbol = (prop->type == ANT_SHAPE_KEY_SYMBOL);
15866 if (key_out->is_symbol) {
15867 key_out->str = NULL;
15868 key_out->key_len = 0;
15869 key_out->sym_off = prop->key.sym_off;
15870 } else {
15871 key_out->str = prop->key.interned;
15872 key_out->key_len = strlen(prop->key.interned);
15873 key_out->sym_off = 0;
15874 }
15875 }
15876
15877 if (value) *value = ant_object_prop_get_unchecked(obj, i);
15878 iter->off = i + 1;
15879
15880 return true;
15881 }
15882
15883 return false;
15884}
15885
15886bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value) {
15887 ant_iter_key_t meta = {0};
15888 ant_t *js = NULL;
15889
15890 if (!iter || !iter->ctx) return false;
15891 js = ((prop_iter_ctx_t *)iter->ctx)->js;
15892 if (!js_prop_iter_next_key(iter, &meta, value)) return false;
15893
15894 if (key_out) {
15895 if (meta.is_symbol) *key_out = mkval(T_SYMBOL, meta.sym_off);
15896 else *key_out = js_mkstr(js, meta.str, meta.key_len);
15897 }
15898
15899 return true;
15900}
15901
15902void js_prop_iter_end(ant_iter_t *iter) {
15903 if (!iter) return;
15904 free(iter->ctx);
15905 iter->off = 0;
15906 iter->ctx = NULL;
15907}
15908
15909void js_check_unhandled_rejections(ant_t *js) {
15910 size_t keep = 0;
15911
15912 for (size_t i = 0; i < js->pending_rejections.len; i++) {
15913 ant_value_t p = js->pending_rejections.items[i];
15914 ant_promise_state_t *pd = get_promise_data(js, p, false);
15915 if (!pd || pd->has_rejection_handler || pd->unhandled_reported) continue;
15916
15917 if (vtype(pd->trigger_parent) == T_PROMISE) {
15918 ant_promise_state_t *parent = get_promise_data(js, pd->trigger_parent, false);
15919 if (parent && parent->has_rejection_handler) continue;
15920 }
15921
15922 if (js->fatal_error) {
15923 js->thrown_exists = true;
15924 js->thrown_value = pd->value;
15925 print_uncaught_throw(js);
15926 js_destroy(js); exit(1);
15927 }
15928
15929 GC_ROOT_SAVE(root_mark, js);
15930 ant_value_t reason = pd->value;
15931 GC_ROOT_PIN(js, p); GC_ROOT_PIN(js, reason);
15932
15933 if (!js_fire_unhandled_rejection(js, p, reason))
15934 print_unhandled_promise_rejection(js, reason);
15935
15936 GC_ROOT_RESTORE(js, root_mark);
15937 pd->unhandled_reported = true;
15938 }
15939
15940 js->pending_rejections.len = keep;
15941}
15942
15943void js_set_getter(ant_value_t obj, js_getter_fn getter) {
15944 if (!is_object_type(obj)) return;
15945 if (vtype(obj) != T_OBJ) obj = js_as_obj(obj);
15946 ant_object_t *ptr = js_obj_ptr(obj);
15947 if (!ptr) return;
15948 ptr->is_exotic = 1;
15949 ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr);
15950 if (!ops) return;
15951 ops->getter = getter;
15952}
15953
15954void js_set_setter(ant_value_t obj, js_setter_fn setter) {
15955 if (!is_object_type(obj)) return;
15956 if (vtype(obj) != T_OBJ) obj = js_as_obj(obj);
15957 ant_object_t *ptr = js_obj_ptr(obj);
15958 if (!ptr) return;
15959 ptr->is_exotic = 1;
15960 ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr);
15961 if (!ops) return;
15962 ops->setter = setter;
15963}
15964
15965void js_set_deleter(ant_value_t obj, js_deleter_fn deleter) {
15966 if (!is_object_type(obj)) return;
15967 if (vtype(obj) != T_OBJ) obj = js_as_obj(obj);
15968 ant_object_t *ptr = js_obj_ptr(obj);
15969 if (!ptr) return;
15970 ptr->is_exotic = 1;
15971 ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr);
15972 if (!ops) return;
15973 ops->deleter = deleter;
15974}
15975
15976void js_set_finalizer(ant_value_t obj, js_finalizer_fn fn) {
15977 if (!is_object_type(obj)) return;
15978 if (vtype(obj) != T_OBJ) obj = js_as_obj(obj);
15979 ant_object_t *ptr = js_obj_ptr(obj);
15980 if (!ptr) return;
15981 ptr->finalizer = fn;
15982}
15983
15984void js_set_keys(ant_value_t obj, js_keys_fn keys) {
15985 if (!is_object_type(obj)) return;
15986 if (vtype(obj) != T_OBJ) obj = js_as_obj(obj);
15987 ant_object_t *ptr = js_obj_ptr(obj);
15988 if (!ptr) return;
15989 ptr->is_exotic = 1;
15990 ptr->exotic_keys = keys;
15991}