MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 15991 lines 546 kB view raw
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], &param_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}