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.

rework Silver string building and add node:net client shims

- replace deep rope-heavy string appends with a builder-backed concat path
- teach the VM, compiler, JIT glue, and GC to read, flush, and preserve builder-backed strings
- keep arguments/object slot syncing and related frame semantics aligned with the new slot behavior
- finish fetch-backed node:http/node:https client shims and expose node:url.parse
- add regression tests for string append loops, HTTP(S) shims, and node:url exports

+2043 -138
+49 -2
include/internal.h
··· 70 70 #define PROTO_WALK_F_OBJECT_ONLY (1u << 0) 71 71 #define PROTO_WALK_F_LOOKUP (1u << 1) 72 72 73 - #define ROPE_MAX_DEPTH 255 73 + #define ROPE_MAX_DEPTH 4096 74 74 #define ROPE_FLATTEN_THRESHOLD (512 * 1024) 75 + 76 + #define STR_BUILDER_TAIL_CAP 256u 77 + #define STR_HEAP_TAG_MASK 0x3ULL 78 + #define STR_HEAP_TAG_FLAT 0x0ULL 79 + #define STR_HEAP_TAG_ROPE 0x1ULL 80 + #define STR_HEAP_TAG_BUILDER 0x2ULL 75 81 76 82 #define T_EMPTY (NANBOX_PREFIX | ((ant_value_t)T_SENTINEL << NANBOX_TYPE_SHIFT) | 0xDEADULL) 77 83 #define T_SPECIAL_OBJECT_MASK (JS_TPFLG(T_OBJ) | JS_TPFLG(T_ARR)) ··· 265 271 char bytes[]; 266 272 } ant_flat_string_t; 267 273 274 + typedef struct ant_builder_chunk { 275 + struct ant_builder_chunk *next; 276 + ant_value_t value; 277 + } ant_builder_chunk_t; 278 + 279 + typedef struct { 280 + ant_offset_t len; 281 + ant_builder_chunk_t *head; 282 + ant_builder_chunk_t *chunk_tail; 283 + ant_value_t cached; 284 + uint16_t tail_len; 285 + uint8_t ascii_state; 286 + char tail[STR_BUILDER_TAIL_CAP]; 287 + } ant_string_builder_t; 288 + 268 289 typedef struct { 269 290 const char *ptr; 270 291 size_t len; ··· 344 365 ant_value_t js_builtin_import(ant_t *js, ant_value_t *args, int nargs); 345 366 ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main); 346 367 ant_value_t js_create_module_context(ant_t *js, const char *filename, bool is_main); 368 + ant_value_t js_create_arguments_object(ant_t *js, ant_value_t callee, sv_frame_t *frame, int argc, int mapped_count, bool is_strict); 369 + 370 + void js_arguments_detach(ant_t *js, ant_value_t obj); 371 + void js_arguments_sync_slot(ant_t *js, ant_value_t obj, uint32_t idx, ant_value_t value); 347 372 348 373 ant_value_t coerce_to_str(ant_t *js, ant_value_t v); 349 374 ant_value_t coerce_to_str_concat(ant_t *js, ant_value_t v); ··· 373 398 ant_value_t mkobj(ant_t *js, ant_offset_t parent); 374 399 ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit); 375 400 ant_value_t rope_flatten(ant_t *js, ant_value_t rope); 401 + ant_value_t str_materialize(ant_t *js, ant_value_t value); 376 402 377 403 ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj); 378 404 ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t len); ··· 441 467 return len == 6 && !memcmp(key, "length", 6); 442 468 } 443 469 470 + // TODO: move strings helpers to strings.h 444 471 static inline bool str_is_heap_rope(ant_value_t value) { 445 - return vtype(value) == T_STR && ((vdata(value) & 1ULL) != 0); 472 + return vtype(value) == T_STR && ((vdata(value) & STR_HEAP_TAG_MASK) == STR_HEAP_TAG_ROPE); 473 + } 474 + 475 + static inline bool str_is_heap_builder(ant_value_t value) { 476 + return vtype(value) == T_STR && ((vdata(value) & STR_HEAP_TAG_MASK) == STR_HEAP_TAG_BUILDER); 477 + } 478 + 479 + static inline ant_rope_heap_t *ant_str_rope_ptr(ant_value_t value) { 480 + return (ant_rope_heap_t *)(uintptr_t)(vdata(value) & ~STR_HEAP_TAG_MASK); 481 + } 482 + 483 + static inline ant_string_builder_t *ant_str_builder_ptr(ant_value_t value) { 484 + return (ant_string_builder_t *)(uintptr_t)(vdata(value) & ~STR_HEAP_TAG_MASK); 485 + } 486 + 487 + static inline ant_value_t ant_mkrope_value(ant_rope_heap_t *rope) { 488 + return mkval(T_STR, ((uintptr_t)rope) | STR_HEAP_TAG_ROPE); 489 + } 490 + 491 + static inline ant_value_t ant_mkbuilder_value(ant_string_builder_t *builder) { 492 + return mkval(T_STR, ((uintptr_t)builder) | STR_HEAP_TAG_BUILDER); 446 493 } 447 494 448 495 static inline int js_brand_id(ant_value_t obj) {
+1 -1
include/pool.h
··· 58 58 59 59 typedef struct { 60 60 ant_offset_t len; 61 - uint8_t depth; 61 + uint16_t depth; 62 62 ant_value_t left; 63 63 ant_value_t right; 64 64 ant_value_t cached;
+33 -2
include/silver/engine.h
··· 211 211 typedef struct 212 212 sv_upvalue sv_upvalue_t; 213 213 214 - typedef struct { 214 + typedef struct sv_frame { 215 215 uint8_t *ip; 216 216 ant_value_t *bp; 217 217 ant_value_t *lp; ··· 230 230 sv_completion_t completion; 231 231 sv_upvalue_t **upvalues; 232 232 int upvalue_count; 233 + 233 234 ant_value_t with_obj; 235 + ant_value_t arguments_obj; 234 236 } sv_frame_t; 235 237 236 238 typedef enum { ··· 254 256 static inline sv_upvalue_t *js_upvalue_alloc(void) { 255 257 return (sv_upvalue_t *)fixed_arena_alloc(&rt->js->upvalue_arena); 256 258 } 259 + 260 + bool sv_slot_has_open_upvalue(sv_vm_t *vm, ant_value_t *slot); 257 261 258 262 #define SV_CALL_HAS_BOUND_ARGS (1u << 0) 259 263 #define SV_CALL_HAS_SUPER (1u << 1) ··· 458 462 return frame->bp[idx]; 459 463 } 460 464 461 - static inline void sv_frame_set_arg_value(sv_frame_t *frame, uint16_t idx, ant_value_t val) { 465 + static inline void sv_frame_set_arg_value(ant_t *js, sv_frame_t *frame, uint16_t idx, ant_value_t val) { 462 466 int arg_slots = sv_frame_arg_slots(frame); 463 467 if (!frame || !frame->bp || (int)idx >= arg_slots) return; 464 468 frame->bp[idx] = val; 469 + if (vtype(frame->arguments_obj) != T_UNDEF) 470 + js_arguments_sync_slot(js, frame->arguments_obj, idx, val); 465 471 } 466 472 467 473 static inline ant_value_t *sv_frame_slot_ptr(sv_frame_t *frame, uint16_t slot_idx) { ··· 476 482 return &frame->lp[slot_idx - param_count]; 477 483 } 478 484 485 + static inline uint16_t sv_frame_total_slots(const sv_frame_t *frame) { 486 + if (!frame || !frame->func) return 0; 487 + int total = frame->func->param_count + frame->func->max_locals; 488 + return total > 0 ? (uint16_t)total : 0; 489 + } 490 + 479 491 static inline void sv_vm_maybe_checkpoint_microtasks(ant_t *js) { 480 492 if (!js || js->microtasks_draining || js->vm_exec_depth != 0) return; 481 493 js_maybe_drain_microtasks(js); 482 494 } 495 + 496 + ant_value_t sv_string_builder_read_value( 497 + ant_t *js, ant_value_t value 498 + ); 499 + 500 + ant_value_t sv_string_builder_flush_slot( 501 + sv_vm_t *vm, ant_t *js, 502 + sv_frame_t *frame, uint16_t slot_idx 503 + ); 504 + 505 + ant_value_t sv_string_builder_append_slot( 506 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, 507 + sv_func_t *func, uint16_t slot_idx, ant_value_t rhs 508 + ); 509 + 510 + ant_value_t sv_string_builder_append_snapshot_slot( 511 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, 512 + sv_func_t *func, uint16_t slot_idx, ant_value_t lhs, ant_value_t rhs 513 + ); 483 514 484 515 typedef struct { 485 516 ant_value_t this_val;
+11
include/silver/glue.h
··· 159 159 ant_value_t *args, int argc 160 160 ); 161 161 162 + ant_value_t jit_helper_str_append_local( 163 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 164 + ant_value_t *locals, uint16_t local_idx, ant_value_t rhs 165 + ); 166 + 167 + ant_value_t jit_helper_str_append_local_snapshot( 168 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 169 + ant_value_t *locals, uint16_t local_idx, 170 + ant_value_t lhs, ant_value_t rhs 171 + ); 172 + 162 173 #endif 163 174 #endif
+4
include/silver/opcode.h
··· 63 63 OP_DEF( SET_LOCAL_UNDEF, 3, 0, 0, loc) /* mark TDZ uninitialized */ 64 64 OP_DEF( GET_LOCAL_CHK, 7, 0, 1, loc_atom) /* get + TDZ check (u16 slot, u32 atom) */ 65 65 OP_DEF( PUT_LOCAL_CHK, 7, 1, 0, loc_atom) /* put + TDZ check (u16 slot, u32 atom) */ 66 + OP_DEF( GET_SLOT_RAW, 3, 0, 1, loc) /* push raw frame slot value (no builder read) */ 66 67 67 68 OP_DEF( GET_ARG, 3, 0, 1, arg) 68 69 OP_DEF( PUT_ARG, 3, 1, 0, arg) ··· 117 118 OP_DEF( INC_LOCAL, 2, 0, 0, loc8) /* locals[i]++ in-place */ 118 119 OP_DEF( DEC_LOCAL, 2, 0, 0, loc8) /* locals[i]-- in-place */ 119 120 OP_DEF( ADD_LOCAL, 2, 1, 0, loc8) /* locals[i] += TOS */ 121 + OP_DEF( STR_APPEND_LOCAL, 3, 1, 0, loc) /* logical slot += TOS via builder */ 122 + OP_DEF( STR_ALC_SNAPSHOT, 3, 2, 0, loc) /* logical slot = snapshot + TOS */ 123 + OP_DEF( STR_FLUSH_LOCAL, 3, 0, 0, loc) /* materialize logical slot builder */ 120 124 121 125 OP_DEF( EQ, 1, 2, 1, none) /* == (abstract) */ 122 126 OP_DEF( NE, 1, 2, 1, none) /* != */
+1
include/types.h
··· 20 20 typedef struct sv_vm sv_vm_t; 21 21 typedef struct sv_func sv_func_t; 22 22 typedef struct sv_closure sv_closure_t; 23 + typedef struct sv_frame sv_frame_t; 23 24 24 25 typedef size_t ant_handle_t; 25 26 typedef uint64_t ant_offset_t;
+291 -20
src/ant.c
··· 6 6 7 7 #include "ant.h" 8 8 #include "utf8.h" 9 + #include "ptr.h" 9 10 #include "debug.h" 10 11 #include "tokens.h" 11 12 #include "common.h" ··· 238 239 } 239 240 240 241 static inline ant_flat_string_t *str_flat_ptr(ant_value_t value) { 241 - if (vtype(value) != T_STR || str_is_heap_rope(value)) return NULL; 242 + if (vtype(value) != T_STR) return NULL; 243 + if ((vdata(value) & STR_HEAP_TAG_MASK) != STR_HEAP_TAG_FLAT) return NULL; 242 244 return (ant_flat_string_t *)(uintptr_t)vdata(value); 243 - } 244 - 245 - static inline ant_rope_heap_t *str_rope_ptr(ant_value_t value) { 246 - return (ant_rope_heap_t *)(uintptr_t)(vdata(value) & ~1ULL); 247 - } 248 - 249 - static inline ant_value_t mkrope_value(ant_rope_heap_t *rope) { 250 - return mkval(T_STR, ((uintptr_t)rope) | 1ULL); 251 245 } 252 246 253 247 static inline ant_extra_slot_t *obj_extra_slots(ant_object_t *obj) { ··· 562 556 563 557 ant_offset_t vstrlen(ant_t *js, ant_value_t v) { 564 558 if (str_is_heap_rope(v)) { 565 - ant_rope_heap_t *rope = str_rope_ptr(v); 559 + ant_rope_heap_t *rope = ant_str_rope_ptr(v); 566 560 return rope ? rope->len : 0; 561 + } 562 + if (str_is_heap_builder(v)) { 563 + ant_string_builder_t *builder = ant_str_builder_ptr(v); 564 + return builder ? builder->len : 0; 567 565 } 568 566 ant_flat_string_t *flat = str_flat_ptr(v); 569 567 return flat ? flat->len : 0; ··· 1636 1634 static inline ant_rope_heap_t *assert_rope_ptr(ant_value_t value) { 1637 1635 assert(vtype(value) == T_STR); 1638 1636 assert(str_is_heap_rope(value)); 1639 - ant_rope_heap_t *ptr = str_rope_ptr(value); 1637 + ant_rope_heap_t *ptr = ant_str_rope_ptr(value); 1640 1638 assert(ptr != NULL); 1641 1639 return ptr; 1642 1640 } ··· 1646 1644 return ptr->len; 1647 1645 } 1648 1646 1649 - static inline uint8_t rope_depth(ant_value_t value) { 1647 + static inline uint16_t rope_depth(ant_value_t value) { 1650 1648 ant_rope_heap_t *ptr = assert_rope_ptr(value); 1651 1649 return ptr->depth; 1652 1650 } ··· 1668 1666 1669 1667 static inline void rope_set_cached_flat(ant_value_t rope, ant_value_t flat) { 1670 1668 ant_rope_heap_t *ptr = assert_rope_ptr(rope); 1669 + ptr->cached = flat; 1670 + } 1671 + 1672 + static inline ant_string_builder_t *assert_builder_ptr(ant_value_t value) { 1673 + assert(vtype(value) == T_STR); 1674 + assert(str_is_heap_builder(value)); 1675 + 1676 + ant_string_builder_t *ptr = ant_str_builder_ptr(value); 1677 + assert(ptr != NULL); 1678 + 1679 + return ptr; 1680 + } 1681 + 1682 + static inline ant_offset_t builder_len(ant_value_t value) { 1683 + ant_string_builder_t *ptr = assert_builder_ptr(value); 1684 + return ptr->len; 1685 + } 1686 + 1687 + static inline ant_value_t builder_cached_flat(ant_value_t value) { 1688 + ant_string_builder_t *ptr = assert_builder_ptr(value); 1689 + return ptr->cached; 1690 + } 1691 + 1692 + static inline void builder_set_cached_flat(ant_value_t builder, ant_value_t flat) { 1693 + ant_string_builder_t *ptr = assert_builder_ptr(builder); 1671 1694 ptr->cached = flat; 1672 1695 } 1673 1696 ··· 1754 1777 return flat; 1755 1778 } 1756 1779 1780 + static ant_value_t builder_flatten(ant_t *js, ant_value_t builder) { 1781 + assert(vtype(builder) == T_STR); 1782 + if (!str_is_heap_builder(builder)) return builder; 1783 + 1784 + GC_ROOT_SAVE(root_mark, js); 1785 + GC_ROOT_PIN(js, builder); 1786 + 1787 + ant_value_t cached = builder_cached_flat(builder); 1788 + GC_ROOT_PIN(js, cached); 1789 + 1790 + if (vtype(cached) == T_STR && !str_is_heap_rope(cached) && !str_is_heap_builder(cached)) { 1791 + GC_ROOT_RESTORE(js, root_mark); 1792 + return cached; 1793 + } 1794 + 1795 + ant_string_builder_t *ptr = assert_builder_ptr(builder); 1796 + ant_value_t flat = js_mkstr(js, NULL, (size_t)ptr->len); 1797 + 1798 + GC_ROOT_PIN(js, flat); 1799 + if (is_err(flat)) { 1800 + GC_ROOT_RESTORE(js, root_mark); 1801 + return flat; 1802 + } 1803 + 1804 + ant_flat_string_t *flat_ptr = (ant_flat_string_t *)(uintptr_t)vdata(flat); 1805 + size_t cursor = 0; 1806 + 1807 + for (ant_builder_chunk_t *chunk = ptr->head; chunk; chunk = chunk->next) { 1808 + ant_value_t chunk_value = chunk->value; 1809 + if (str_is_heap_rope(chunk_value) || str_is_heap_builder(chunk_value)) { 1810 + chunk_value = str_materialize(js, chunk_value); 1811 + if (is_err(chunk_value)) { 1812 + GC_ROOT_RESTORE(js, root_mark); 1813 + return chunk_value; 1814 + }} 1815 + 1816 + ant_flat_string_t *chunk_flat = str_flat_ptr(chunk_value); 1817 + if (!chunk_flat || chunk_flat->len == 0) continue; 1818 + memcpy(flat_ptr->bytes + cursor, chunk_flat->bytes, (size_t)chunk_flat->len); 1819 + cursor += (size_t)chunk_flat->len; 1820 + } 1821 + 1822 + if (ptr->tail_len > 0) { 1823 + memcpy(flat_ptr->bytes + cursor, ptr->tail, ptr->tail_len); 1824 + cursor += ptr->tail_len; 1825 + } 1826 + 1827 + flat_ptr->bytes[cursor] = '\0'; 1828 + flat_ptr->is_ascii = ptr->ascii_state; 1829 + builder_set_cached_flat(builder, flat); 1830 + GC_ROOT_RESTORE(js, root_mark); 1831 + 1832 + return flat; 1833 + } 1834 + 1835 + ant_value_t str_materialize(ant_t *js, ant_value_t value) { 1836 + if (vtype(value) != T_STR) return value; 1837 + if (str_is_heap_rope(value)) return rope_flatten(js, value); 1838 + if (str_is_heap_builder(value)) return builder_flatten(js, value); 1839 + return value; 1840 + } 1841 + 1757 1842 ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len) { 1758 - if (str_is_heap_rope(value)) { 1759 - ant_value_t flat = rope_flatten(js, value); 1843 + if (str_is_heap_rope(value) || str_is_heap_builder(value)) { 1844 + ant_value_t flat = str_materialize(js, value); 1760 1845 assert(!is_err(flat)); 1761 1846 value = flat; 1762 1847 } ··· 2268 2353 return lkp(js, arr, idxstr, idxlen) != 0; 2269 2354 } 2270 2355 2356 + enum { ANT_ARGUMENTS_NATIVE_TAG = 0x41524753u }; // ARGS 2357 + 2358 + typedef struct { 2359 + sv_vm_t *vm; 2360 + int frame_index; 2361 + uint32_t mapped_count; 2362 + uint8_t in_setter; 2363 + uint8_t deleted[]; 2364 + } ant_arguments_state_t; 2365 + 2366 + static inline ant_arguments_state_t *js_arguments_state(ant_value_t obj) { 2367 + if (!js_check_native_tag(obj, ANT_ARGUMENTS_NATIVE_TAG)) return NULL; 2368 + return (ant_arguments_state_t *)js_get_native_ptr(obj); 2369 + } 2370 + 2371 + static ant_value_t js_arguments_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { 2372 + ant_offset_t arr_len = get_array_length(js, obj); 2373 + unsigned long idx = 0; 2374 + 2375 + if (!parse_array_index(key, key_len, arr_len, &idx)) return js_mkundef(); 2376 + if ((ant_offset_t)idx >= arr_len) return js_mkundef(); 2377 + 2378 + ant_arguments_state_t *state = js_arguments_state(obj); 2379 + if ( 2380 + state && state->frame_index >= 0 && 2381 + (uint32_t)idx < state->mapped_count && 2382 + !state->deleted[idx] 2383 + ) { 2384 + sv_frame_t *frame = &state->vm->frames[state->frame_index]; 2385 + return frame->bp[idx]; 2386 + } 2387 + 2388 + return arr_get(js, obj, (ant_offset_t)idx); 2389 + } 2390 + 2391 + static bool js_arguments_setter( 2392 + ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value 2393 + ) { 2394 + unsigned long idx = 0; 2395 + if (!parse_array_index(key, key_len, (ant_offset_t)-1, &idx)) return false; 2396 + 2397 + ant_arguments_state_t *state = js_arguments_state(obj); 2398 + if (state) state->in_setter = 1; 2399 + arr_set(js, obj, (ant_offset_t)idx, value); 2400 + 2401 + if (state) state->in_setter = 0; 2402 + if ( 2403 + state && state->frame_index >= 0 && 2404 + (uint32_t)idx < state->mapped_count && 2405 + !state->deleted[idx] 2406 + ) { 2407 + sv_frame_t *frame = &state->vm->frames[state->frame_index]; 2408 + frame->bp[idx] = value; 2409 + } 2410 + 2411 + return true; 2412 + } 2413 + 2414 + static bool js_arguments_deleter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { 2415 + unsigned long idx = 0; 2416 + if (!parse_array_index(key, key_len, (ant_offset_t)-1, &idx)) return false; 2417 + 2418 + ant_arguments_state_t *state = js_arguments_state(obj); 2419 + if (state && (uint32_t)idx < state->mapped_count) state->deleted[idx] = 1; 2420 + return true; 2421 + } 2422 + 2423 + static void js_arguments_finalizer(ant_t *js, ant_object_t *obj) { 2424 + if (!obj || obj->native.tag != ANT_ARGUMENTS_NATIVE_TAG) return; 2425 + free(obj->native.ptr); 2426 + obj->native.ptr = NULL; 2427 + obj->native.tag = 0; 2428 + } 2429 + 2430 + ant_value_t js_create_arguments_object( 2431 + ant_t *js, 2432 + ant_value_t callee, 2433 + sv_frame_t *frame, 2434 + int argc, 2435 + int mapped_count, 2436 + bool is_strict 2437 + ) { 2438 + GC_ROOT_SAVE(root_mark, js); 2439 + 2440 + ant_value_t arr = js_mkarr(js); 2441 + if (is_err(arr)) { 2442 + GC_ROOT_RESTORE(js, root_mark); 2443 + return arr; 2444 + } GC_ROOT_PIN(js, arr); 2445 + 2446 + if (frame && frame->bp && argc > 0) { 2447 + for (int i = 0; i < argc; i++) js_arr_push(js, arr, frame->bp[i]); 2448 + } 2449 + 2450 + if (is_strict) js_set_slot(arr, SLOT_STRICT_ARGS, js_true); 2451 + else if (vtype(callee) == T_FUNC) setprop_cstr(js, arr, "callee", 6, callee); 2452 + js_set_sym(js, arr, get_toStringTag_sym(), js_mkstr(js, "Arguments", 9)); 2453 + 2454 + if (is_object_type(js->sym.array_proto)) { 2455 + ant_value_t iter_fn = js_get_sym(js, js->sym.array_proto, get_iterator_sym()); 2456 + if (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC) 2457 + js_set_sym(js, arr, get_iterator_sym(), iter_fn); 2458 + } 2459 + 2460 + if (!is_strict && mapped_count > 0 && frame && js->vm) { 2461 + ant_arguments_state_t *state = calloc( 2462 + 1, sizeof(*state) + (size_t)mapped_count * sizeof(state->deleted[0])); 2463 + if (!state) { 2464 + GC_ROOT_RESTORE(js, root_mark); 2465 + return js_mkerr(js, "oom"); 2466 + } 2467 + 2468 + state->vm = js->vm; 2469 + state->frame_index = (int)(frame - js->vm->frames); 2470 + state->mapped_count = (uint32_t)mapped_count; 2471 + 2472 + js_set_native_ptr(arr, state); 2473 + js_set_native_tag(arr, ANT_ARGUMENTS_NATIVE_TAG); 2474 + js_set_finalizer(arr, js_arguments_finalizer); 2475 + js_set_getter(arr, js_arguments_getter); 2476 + js_set_setter(arr, js_arguments_setter); 2477 + js_set_deleter(arr, js_arguments_deleter); 2478 + } 2479 + 2480 + GC_ROOT_RESTORE(js, root_mark); 2481 + return arr; 2482 + } 2483 + 2484 + void js_arguments_detach(ant_t *js, ant_value_t obj) { 2485 + ant_arguments_state_t *state = js_arguments_state(obj); 2486 + if (!state || state->frame_index < 0) return; 2487 + 2488 + GC_ROOT_SAVE(root_mark, js); 2489 + GC_ROOT_PIN(js, obj); 2490 + 2491 + sv_frame_t *frame = &state->vm->frames[state->frame_index]; 2492 + ant_offset_t arr_len = get_array_length(js, obj); 2493 + ant_offset_t limit = (ant_offset_t)state->mapped_count; 2494 + if (arr_len < limit) limit = arr_len; 2495 + 2496 + for (ant_offset_t i = 0; i < limit; i++) { 2497 + if (state->deleted[i]) continue; 2498 + state->in_setter = 1; 2499 + arr_set(js, obj, i, frame->bp[i]); 2500 + state->in_setter = 0; 2501 + } 2502 + 2503 + state->frame_index = -1; 2504 + GC_ROOT_RESTORE(js, root_mark); 2505 + } 2506 + 2507 + void js_arguments_sync_slot(ant_t *js, ant_value_t obj, uint32_t idx, ant_value_t value) { 2508 + ant_arguments_state_t *state = js_arguments_state(obj); 2509 + if (!state || state->frame_index < 0 || 2510 + idx >= state->mapped_count || state->deleted[idx]) { 2511 + return; 2512 + } 2513 + 2514 + state->in_setter = 1; 2515 + arr_set(js, obj, (ant_offset_t)idx, value); 2516 + state->in_setter = 0; 2517 + } 2518 + 2271 2519 static inline void arr_del(ant_t *js, ant_value_t arr, ant_offset_t idx) { 2272 2520 ant_offset_t semantic_len = get_array_length(js, arr); 2273 2521 if (idx >= semantic_len) return; ··· 2322 2570 return mkval(T_STR, (uintptr_t)flat); 2323 2571 } 2324 2572 2325 - static ant_value_t js_mkrope(ant_t *js, ant_value_t left, ant_value_t right, ant_offset_t total_len, uint8_t depth) { 2573 + static ant_value_t js_mkrope(ant_t *js, ant_value_t left, ant_value_t right, ant_offset_t total_len, uint16_t depth) { 2326 2574 ant_rope_heap_t *rope = (ant_rope_heap_t *)js_type_alloc( 2327 2575 js, ANT_ALLOC_ROPE, sizeof(*rope), _Alignof(ant_rope_heap_t) 2328 2576 ); ··· 2332 2580 rope->left = left; 2333 2581 rope->right = right; 2334 2582 rope->cached = js_mkundef(); 2335 - return mkrope_value(rope); 2583 + return ant_mkrope_value(rope); 2336 2584 } 2337 2585 2338 2586 ··· 3230 3478 ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); 3231 3479 const char *key = (char *)(uintptr_t)(koff); 3232 3480 3481 + if (array_obj_ptr(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { 3482 + ant_arguments_state_t *args_state = js_arguments_state(obj); 3483 + if (args_state && !args_state->in_setter && js_arguments_setter(js, obj, key, (size_t)klen, v)) return v; 3484 + } 3485 + 3233 3486 if (array_obj_ptr(obj) && !is_proxy(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { 3234 3487 ant_value_t result = js_setprop_array_fast(js, obj, k, v, klen, key); 3235 3488 if (vtype(result) != T_UNDEF) return result; ··· 4057 4310 ant_offset_t str_len_fast(ant_t *js, ant_value_t str) { 4058 4311 if (vtype(str) != T_STR) return 0; 4059 4312 if (str_is_heap_rope(str)) return rope_len(str); 4313 + if (str_is_heap_builder(str)) return builder_len(str); 4060 4314 return assert_flat_string_len(js, str, NULL); 4061 4315 } 4062 4316 4063 4317 ant_value_t do_string_op(ant_t *js, uint8_t op, ant_value_t l, ant_value_t r) { 4064 4318 if (op == TOK_PLUS) { 4319 + if (str_is_heap_builder(l)) { 4320 + l = builder_flatten(js, l); 4321 + if (is_err(l)) return l; 4322 + } 4323 + 4324 + if (str_is_heap_builder(r)) { 4325 + r = builder_flatten(js, r); 4326 + if (is_err(r)) return r; 4327 + } 4328 + 4065 4329 ant_offset_t n1 = str_len_fast(js, l); 4066 4330 ant_offset_t n2 = str_len_fast(js, r); 4067 4331 ant_offset_t total_len = n1 + n2; ··· 4069 4333 if (n2 == 0) return l; 4070 4334 if (n1 == 0) return r; 4071 4335 4072 - uint8_t left_depth = (vtype(l) == T_STR && str_is_heap_rope(l)) ? rope_depth(l) : 0; 4073 - uint8_t right_depth = (vtype(r) == T_STR && str_is_heap_rope(r)) ? rope_depth(r) : 0; 4336 + uint16_t left_depth = (vtype(l) == T_STR && str_is_heap_rope(l)) ? rope_depth(l) : 0; 4337 + uint16_t right_depth = (vtype(r) == T_STR && str_is_heap_rope(r)) ? rope_depth(r) : 0; 4074 4338 unsigned int new_depth = (unsigned int)(left_depth > right_depth ? left_depth : right_depth) + 1u; 4075 4339 4076 4340 if (new_depth >= ROPE_MAX_DEPTH || total_len >= ROPE_FLATTEN_THRESHOLD) { ··· 4096 4360 return string_builder_finalize(js, &sb); 4097 4361 } 4098 4362 4099 - return js_mkrope(js, l, r, total_len, (uint8_t)new_depth); 4363 + return js_mkrope(js, l, r, total_len, (uint16_t)new_depth); 4100 4364 } 4101 4365 4102 4366 ant_offset_t n1, off1 = vstr(js, l, &n1); ··· 8586 8850 ant_value_t iter_sym = get_iterator_sym(); 8587 8851 8588 8852 if (vtype(src) == T_STR) { 8589 - if (str_is_heap_rope(src)) { src = rope_flatten(js, src); if (is_err(src)) return src; } 8853 + if (str_is_heap_rope(src) || str_is_heap_builder(src)) { 8854 + src = str_materialize(js, src); 8855 + if (is_err(src)) return src; 8856 + } 8590 8857 ant_offset_t slen = str_len_fast(js, src); 8591 8858 array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; 8592 8859 for (ant_offset_t i = 0; i < slen; ) { ··· 13627 13894 ant_offset_t arr_len = get_array_length(js, obj); 13628 13895 13629 13896 if (parse_array_index(key, key_len, arr_len, &idx)) { 13897 + if (js_arguments_state(obj)) { 13898 + *out = js_arguments_getter(js, obj, key, key_len); 13899 + return true; 13900 + } 13630 13901 if (arr_has(js, obj, (ant_offset_t)idx)) { 13631 13902 *out = arr_get(js, obj, (ant_offset_t)idx); 13632 13903 return true;
+425 -6
src/builtins/node/http.mjs
··· 110 110 throw new Error('node:http client transport is not implemented yet'); 111 111 } 112 112 113 + function isURLLike(value) { 114 + return !!value && typeof value === 'object' && typeof value.href === 'string'; 115 + } 116 + 117 + function cloneOptionObject(value) { 118 + return value && typeof value === 'object' ? { ...value } : {}; 119 + } 120 + 121 + function normalizeRequestArgs(input, options, callback) { 122 + let resolvedInput = input; 123 + let resolvedOptions = options; 124 + let resolvedCallback = callback; 125 + 126 + if (typeof resolvedOptions === 'function') { 127 + resolvedCallback = resolvedOptions; 128 + resolvedOptions = undefined; 129 + } 130 + 131 + if (typeof resolvedInput === 'function' || resolvedInput === undefined || resolvedInput === null) { 132 + resolvedCallback = typeof resolvedInput === 'function' ? resolvedInput : resolvedCallback; 133 + resolvedInput = undefined; 134 + } 135 + 136 + return { 137 + input: resolvedInput, 138 + options: cloneOptionObject(resolvedOptions), 139 + callback: resolvedCallback 140 + }; 141 + } 142 + 143 + function defaultPortForProtocol(protocol) { 144 + return protocol === 'https:' ? 443 : 80; 145 + } 146 + 147 + function hostIncludesExplicitPort(host) { 148 + if (host === undefined || host === null) return false; 149 + const value = String(host); 150 + const bracketEnd = value.lastIndexOf(']'); 151 + const colonIndex = value.lastIndexOf(':'); 152 + return colonIndex > bracketEnd; 153 + } 154 + 155 + function buildRequestOptions(input, options) { 156 + const requestOptions = cloneOptionObject(options); 157 + 158 + if (typeof input === 'string' || isURLLike(input)) { 159 + const url = new URL(String(input)); 160 + if (requestOptions.protocol === undefined) requestOptions.protocol = url.protocol; 161 + if (requestOptions.hostname === undefined && requestOptions.host === undefined) { 162 + requestOptions.hostname = url.hostname; 163 + } 164 + if (requestOptions.port === undefined && url.port) requestOptions.port = url.port; 165 + if (requestOptions.path === undefined) requestOptions.path = `${url.pathname}${url.search}`; 166 + if (requestOptions.auth === undefined && url.username) { 167 + requestOptions.auth = url.password ? `${url.username}:${url.password}` : url.username; 168 + } 169 + } else if (input && typeof input === 'object') { 170 + Object.assign(requestOptions, input); 171 + } 172 + 173 + if (!requestOptions.protocol) requestOptions.protocol = 'http:'; 174 + if (!requestOptions.method) requestOptions.method = 'GET'; 175 + if (!requestOptions.path) requestOptions.path = '/'; 176 + if ( 177 + (requestOptions.port === undefined || requestOptions.port === null || requestOptions.port === '') && 178 + !hostIncludesExplicitPort(requestOptions.host) 179 + ) { 180 + requestOptions.port = defaultPortForProtocol(requestOptions.protocol); 181 + } 182 + 183 + return requestOptions; 184 + } 185 + 186 + function buildRequestUrl(options) { 187 + const protocol = options.protocol || 'http:'; 188 + const base = new URL(`${protocol}//localhost/`); 189 + 190 + if (options.hostname !== undefined && options.hostname !== null) base.hostname = String(options.hostname); 191 + else if (options.host !== undefined && options.host !== null) base.host = String(options.host); 192 + 193 + if (options.port !== undefined && options.port !== null && options.port !== '') { 194 + base.port = String(options.port); 195 + } 196 + 197 + if (options.auth) { 198 + const auth = String(options.auth); 199 + const sep = auth.indexOf(':'); 200 + if (sep === -1) base.username = auth; 201 + else { 202 + base.username = auth.slice(0, sep); 203 + base.password = auth.slice(sep + 1); 204 + } 205 + } 206 + 207 + return new URL(String(options.path || '/'), base).toString(); 208 + } 209 + 210 + function createAbortError(message) { 211 + const error = new Error(message || 'The operation was aborted'); 212 + error.name = 'AbortError'; 213 + return error; 214 + } 215 + 216 + function buildRequestHeadersObject(headers) { 217 + const normalized = createHeadersObject(); 218 + 219 + if (!headers) return normalized; 220 + Object.keys(headers).forEach(name => { 221 + const value = headers[name]; 222 + normalized[name] = Array.isArray(value) ? value.join(', ') : String(value); 223 + }); 224 + return normalized; 225 + } 226 + 227 + function buildHeadersFromFetch(response) { 228 + const headers = createHeadersObject(); 229 + const rawHeaders = []; 230 + 231 + for (const [name, value] of response.headers.entries()) { 232 + rawHeaders.push(name, value); 233 + appendHeaderValue(headers, normalizeHeaderName(name), value); 234 + } 235 + 236 + return { headers, rawHeaders }; 237 + } 238 + 239 + function getFetchBody(chunks) { 240 + if (!chunks || chunks.length === 0) return undefined; 241 + if (chunks.length === 1) return chunks[0]; 242 + return Buffer.concat(chunks); 243 + } 244 + 113 245 // compatibility stub only 114 246 function createAgentState() { 115 247 return Object.create(null); ··· 117 249 118 250 export class OutgoingMessage extends EventEmitter {} 119 251 252 + class FetchIncomingMessage extends EventEmitter { 253 + constructor(response) { 254 + super(); 255 + 256 + const { headers, rawHeaders } = buildHeadersFromFetch(response); 257 + 258 + this.socket = null; 259 + this.connection = null; 260 + this.statusCode = response.status; 261 + this.statusMessage = response.statusText || STATUS_CODES[response.status] || ''; 262 + this.headers = headers; 263 + this.rawHeaders = rawHeaders; 264 + this.httpVersion = '1.1'; 265 + this.httpVersionMajor = 1; 266 + this.httpVersionMinor = 1; 267 + this.complete = false; 268 + this.aborted = false; 269 + this.destroyed = false; 270 + this.readableEnded = false; 271 + this.url = response.url; 272 + this._reader = response.body && typeof response.body.getReader === 'function' ? response.body.getReader() : null; 273 + this._encoding = null; 274 + this._decoder = null; 275 + this._pumpStarted = false; 276 + this._closeEmitted = false; 277 + } 278 + 279 + _emitClose() { 280 + if (this._closeEmitted) return; 281 + this._closeEmitted = true; 282 + this.emit('close'); 283 + } 284 + 285 + async _pumpBody() { 286 + if (this._pumpStarted) return; 287 + this._pumpStarted = true; 288 + 289 + if (!this._reader) { 290 + this.complete = true; 291 + this.readableEnded = true; 292 + this.emit('end'); 293 + this._emitClose(); 294 + return; 295 + } 296 + 297 + try { 298 + for (;;) { 299 + const { done, value } = await this._reader.read(); 300 + if (done) break; 301 + if (!value || value.byteLength === 0) continue; 302 + 303 + const chunk = Buffer.from(value); 304 + if (this._decoder) { 305 + const text = this._decoder.decode(chunk, { stream: true }); 306 + if (text.length > 0) this.emit('data', text); 307 + } else if (this._encoding) { 308 + this.emit('data', chunk.toString(this._encoding)); 309 + } else { 310 + this.emit('data', chunk); 311 + } 312 + } 313 + 314 + if (this._decoder) { 315 + const finalChunk = this._decoder.decode(); 316 + if (finalChunk.length > 0) this.emit('data', finalChunk); 317 + } 318 + 319 + this.complete = true; 320 + this.readableEnded = true; 321 + this.emit('end'); 322 + this._emitClose(); 323 + } catch (error) { 324 + if (this.destroyed) return; 325 + this.destroyed = true; 326 + this.aborted = true; 327 + this.emit('error', error); 328 + this._emitClose(); 329 + } 330 + } 331 + 332 + setEncoding(encoding) { 333 + this._encoding = encoding || 'utf8'; 334 + if (typeof TextDecoder === 'function') { 335 + try { 336 + this._decoder = new TextDecoder(this._encoding === 'utf8' ? 'utf-8' : this._encoding); 337 + } catch { 338 + this._decoder = null; 339 + } 340 + } 341 + return this; 342 + } 343 + 344 + resume() { 345 + queueMicrotask(() => { 346 + this._pumpBody(); 347 + }); 348 + return this; 349 + } 350 + 351 + destroy(error) { 352 + if (this.destroyed) return this; 353 + this.destroyed = true; 354 + this.aborted = true; 355 + if (this._reader && typeof this._reader.cancel === 'function') { 356 + Promise.resolve(this._reader.cancel(error)).catch(() => {}); 357 + } 358 + if (error) this.emit('error', error); 359 + this._emitClose(); 360 + return this; 361 + } 362 + } 363 + 120 364 export class ClientRequest extends OutgoingMessage { 121 - constructor() { 365 + constructor(options = {}, callback) { 122 366 super(); 123 - clientNotImplemented(); 367 + 368 + this.agent = options.agent ?? globalAgent; 369 + this.method = String(options.method || 'GET').toUpperCase(); 370 + this.protocol = options.protocol || 'http:'; 371 + this.host = options.host ?? options.hostname ?? 'localhost'; 372 + this.hostname = options.hostname ?? options.host ?? 'localhost'; 373 + this.port = options.port ?? defaultPortForProtocol(this.protocol); 374 + this.path = options.path || '/'; 375 + this.socket = null; 376 + this.connection = null; 377 + this.destroyed = false; 378 + this.aborted = false; 379 + this.finished = false; 380 + this.reusedSocket = false; 381 + this._headers = createHeadersObject(); 382 + this._bodyChunks = []; 383 + this._controller = typeof AbortController === 'function' ? new AbortController() : null; 384 + this._timeout = 0; 385 + this._timeoutHandle = null; 386 + this._dispatchStarted = false; 387 + this._requestUrl = buildRequestUrl(options); 388 + this._closeEmitted = false; 389 + this._timedOut = false; 390 + 391 + if (options.headers) { 392 + Object.keys(options.headers).forEach(name => { 393 + this.setHeader(name, options.headers[name]); 394 + }); 395 + } 396 + 397 + if (typeof callback === 'function') this.once('response', callback); 398 + if (options.timeout !== undefined) this.setTimeout(options.timeout); 399 + } 400 + 401 + _emitClose() { 402 + if (this._closeEmitted) return; 403 + this._closeEmitted = true; 404 + this.emit('close'); 405 + } 406 + 407 + _clearTimeoutTimer() { 408 + if (!this._timeoutHandle) return; 409 + clearTimeout(this._timeoutHandle); 410 + this._timeoutHandle = null; 411 + } 412 + 413 + _armTimeoutTimer() { 414 + this._clearTimeoutTimer(); 415 + if (!(this._timeout > 0)) return; 416 + 417 + this._timeoutHandle = setTimeout(() => { 418 + if (this.destroyed) return; 419 + this._timedOut = true; 420 + this.emit('timeout'); 421 + if (this._controller) this._controller.abort(createAbortError('Request timed out')); 422 + }, this._timeout); 423 + } 424 + 425 + _dispatch() { 426 + if (this._dispatchStarted || this.destroyed) return; 427 + this._dispatchStarted = true; 428 + this._armTimeoutTimer(); 429 + 430 + Promise.resolve() 431 + .then(async () => { 432 + const response = await fetch(this._requestUrl, { 433 + method: this.method, 434 + headers: buildRequestHeadersObject(this._headers), 435 + body: getFetchBody(this._bodyChunks), 436 + signal: this._controller ? this._controller.signal : undefined 437 + }); 438 + 439 + if (this.destroyed) return; 440 + this._clearTimeoutTimer(); 441 + 442 + const incoming = new FetchIncomingMessage(response); 443 + incoming.on('close', () => { 444 + this._emitClose(); 445 + }); 446 + 447 + this.emit('response', incoming); 448 + queueMicrotask(() => { 449 + incoming._pumpBody(); 450 + }); 451 + }) 452 + .catch(error => { 453 + this._clearTimeoutTimer(); 454 + if (this.destroyed || this._timedOut) { 455 + this._emitClose(); 456 + return; 457 + } 458 + this.emit('error', error); 459 + this._emitClose(); 460 + }); 461 + } 462 + 463 + setHeader(name, value) { 464 + this._headers[normalizeHeaderName(name)] = value; 465 + return this; 466 + } 467 + 468 + getHeader(name) { 469 + return this._headers[normalizeHeaderName(name)]; 470 + } 471 + 472 + getHeaders() { 473 + return { ...this._headers }; 474 + } 475 + 476 + removeHeader(name) { 477 + delete this._headers[normalizeHeaderName(name)]; 478 + return this; 479 + } 480 + 481 + setTimeout(msecs, callback) { 482 + this._timeout = Number(msecs) || 0; 483 + if (typeof callback === 'function') this.on('timeout', callback); 484 + if (this._dispatchStarted && !this.destroyed) this._armTimeoutTimer(); 485 + return this; 486 + } 487 + 488 + setNoDelay() { 489 + return this; 490 + } 491 + 492 + setSocketKeepAlive() { 493 + return this; 494 + } 495 + 496 + write(chunk, encoding, callback) { 497 + if (this.finished) throw new Error('write after end'); 498 + this._bodyChunks.push(bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined)); 499 + if (typeof encoding === 'function') callback = encoding; 500 + if (typeof callback === 'function') callback(); 501 + return true; 502 + } 503 + 504 + end(chunk, encoding, callback) { 505 + if (this.finished) return this; 506 + if (typeof chunk === 'function') { 507 + callback = chunk; 508 + chunk = undefined; 509 + encoding = undefined; 510 + } else if (typeof encoding === 'function') { 511 + callback = encoding; 512 + encoding = undefined; 513 + } 514 + 515 + if (chunk !== undefined && chunk !== null) { 516 + this.write(chunk, encoding); 517 + } 518 + 519 + this.finished = true; 520 + this.emit('finish'); 521 + if (typeof callback === 'function') callback(); 522 + this._dispatch(); 523 + return this; 524 + } 525 + 526 + abort() { 527 + return this.destroy(createAbortError('Request aborted')); 528 + } 529 + 530 + destroy(error) { 531 + if (this.destroyed) return this; 532 + this.destroyed = true; 533 + this.aborted = true; 534 + this._clearTimeoutTimer(); 535 + if (this._controller) this._controller.abort(error || createAbortError('Request destroyed')); 536 + if (error) this.emit('error', error); 537 + this._emitClose(); 538 + return this; 124 539 } 125 540 } 126 541 ··· 525 940 return new Server(options, requestListener); 526 941 } 527 942 528 - export function request() { 529 - clientNotImplemented(); 943 + export function request(input, options, callback) { 944 + const normalized = normalizeRequestArgs(input, options, callback); 945 + const requestOptions = buildRequestOptions(normalized.input, normalized.options); 946 + return new ClientRequest(requestOptions, normalized.callback); 530 947 } 531 948 532 - export function get() { 533 - clientNotImplemented(); 949 + export function get(input, options, callback) { 950 + const req = request(input, options, callback); 951 + req.end(); 952 + return req; 534 953 } 535 954 536 955 export { METHODS, STATUS_CODES } from 'ant:internal/http_metadata';
+42 -4
src/builtins/node/https.mjs
··· 94 94 throw new Error('node:https client transport is not implemented yet'); 95 95 } 96 96 97 + function createInvalidProtocolError(protocol) { 98 + const error = new TypeError(`Protocol "${protocol}" not supported. Expected "https:"`); 99 + error.code = 'ERR_INVALID_PROTOCOL'; 100 + return error; 101 + } 102 + 103 + function getProtocolFromInput(input) { 104 + if (typeof input === 'string') return new URL(input).protocol; 105 + if (!input || typeof input !== 'object') return undefined; 106 + if (typeof input.href === 'string') return new URL(String(input)).protocol; 107 + if (input.protocol === undefined || input.protocol === null) return undefined; 108 + return String(input.protocol); 109 + } 110 + 111 + function assertHttpsProtocol(protocol) { 112 + if (protocol === undefined || protocol === null || protocol === '') return; 113 + if (String(protocol) !== 'https:') throw createInvalidProtocolError(String(protocol)); 114 + } 115 + 116 + function applyDefaultHttpsProtocol(input, options) { 117 + const optionProtocol = options && typeof options === 'object' ? options.protocol : undefined; 118 + assertHttpsProtocol(optionProtocol); 119 + 120 + if (typeof input === 'string' || (input && typeof input === 'object' && typeof input.href === 'string')) { 121 + if (optionProtocol === undefined) assertHttpsProtocol(getProtocolFromInput(input)); 122 + return [input, options]; 123 + } 124 + 125 + if (typeof input === 'function' || input === undefined || input === null) { 126 + return [{ protocol: 'https:' }, options]; 127 + } 128 + 129 + assertHttpsProtocol(getProtocolFromInput(input)); 130 + return [{ ...input, protocol: input.protocol ?? 'https:' }, options]; 131 + } 132 + 97 133 // compatibility stub only 98 134 export class Server extends http.Server { 99 135 constructor(options, requestListener) { ··· 133 169 return new Server(options, requestListener); 134 170 } 135 171 136 - export function request() { 137 - clientNotImplemented(); 172 + export function request(input, options, callback) { 173 + const [resolvedInput, resolvedOptions] = applyDefaultHttpsProtocol(input, options); 174 + return http.request(resolvedInput, resolvedOptions, callback); 138 175 } 139 176 140 - export function get() { 141 - clientNotImplemented(); 177 + export function get(input, options, callback) { 178 + const [resolvedInput, resolvedOptions] = applyDefaultHttpsProtocol(input, options); 179 + return http.get(resolvedInput, resolvedOptions, callback); 142 180 } 143 181 144 182 export { METHODS, STATUS_CODES } from 'ant:internal/http_metadata';
+32 -10
src/gc/gc.c
··· 53 53 } 54 54 55 55 static void gc_mark_str(ant_t *js, ant_value_t v) { 56 + static const void *dispatch[] = { 57 + [STR_HEAP_TAG_FLAT] = &&l_flat, 58 + [STR_HEAP_TAG_ROPE] = &&l_rope, 59 + [STR_HEAP_TAG_BUILDER] = &&l_builder, 60 + }; 61 + 56 62 if (v <= NANBOX_PREFIX) return; 57 63 uint8_t t = (v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; 58 - 59 64 if (t != T_STR) return; 65 + 60 66 uintptr_t data = (uintptr_t)(v & NANBOX_DATA_MASK); 61 - 62 - if (data & 1ULL) { 63 - ant_rope_heap_t *rope = (ant_rope_heap_t *)(data & ~1ULL); 64 - 65 - if (!rope) return; 66 - if (!gc_ropes_mark(rope)) return; 67 - 67 + uintptr_t tag = data & STR_HEAP_TAG_MASK; 68 + 69 + if (tag < sizeof(dispatch) / sizeof(*dispatch) && dispatch[tag]) 70 + goto *dispatch[tag]; 71 + goto l_flat; 72 + 73 + l_rope: { 74 + ant_rope_heap_t *rope = (ant_rope_heap_t *)(data & ~STR_HEAP_TAG_MASK); 75 + if (!rope || !gc_ropes_mark(rope)) return; 68 76 gc_mark_str(js, rope->left); 69 77 gc_mark_str(js, rope->right); 70 78 gc_mark_str(js, rope->cached); 71 - 72 - } else if (data) gc_strings_mark(js, (const void *)data); 79 + return; 80 + } 81 + 82 + l_builder: { 83 + ant_string_builder_t *builder = (ant_string_builder_t *)(data & ~STR_HEAP_TAG_MASK); 84 + if (!builder || !gc_ropes_mark(builder)) return; 85 + gc_mark_value(js, builder->cached); 86 + for (ant_builder_chunk_t *chunk = builder->head; chunk; chunk = chunk->next) { 87 + if (gc_ropes_mark(chunk)) gc_mark_value(js, chunk->value); 88 + } 89 + return; 90 + } 91 + 92 + l_flat: 93 + if (data) gc_strings_mark(js, (const void *)data); 94 + return; 73 95 } 74 96 75 97 void gc_run(ant_t *js) {
+1
src/modules/url.c
··· 1602 1602 js_set(js, lib, "URLSearchParams",js_get(js, glob, "URLSearchParams")); 1603 1603 js_set(js, lib, "fileURLToPath", js_mkfun(builtin_fileURLToPath)); 1604 1604 js_set(js, lib, "pathToFileURL", js_mkfun(builtin_pathToFileURL)); 1605 + js_set(js, lib, "parse", js_mkfun(url_parse)); 1605 1606 js_set(js, lib, "format", js_mkfun(builtin_url_format)); 1606 1607 js_set(js, lib, "default", lib); 1607 1608
+152 -1
src/silver/compiler.c
··· 883 883 set_local_inferred_type(c, local_idx, type); 884 884 } 885 885 886 + static inline void emit_slot_op(sv_compiler_t *c, sv_op_t op, uint16_t slot) { 887 + emit_op(c, op); 888 + emit_u16(c, slot); 889 + } 890 + 886 891 static void emit_put_local(sv_compiler_t *c, int local_idx) { 887 892 emit_put_local_typed(c, local_idx, SV_TI_UNKNOWN); 888 893 } 894 + 895 + static uint8_t infer_expr_type(sv_compiler_t *c, sv_ast_t *node); 889 896 890 897 static void emit_get_local(sv_compiler_t *c, int local_idx) { 891 898 int slot = local_idx - c->param_locals; 892 899 if (slot <= 255) { emit_op(c, OP_GET_LOCAL8); emit(c, (uint8_t)slot); } 893 900 else { emit_op(c, OP_GET_LOCAL); emit_u16(c, (uint16_t)slot); } 901 + } 902 + 903 + static bool match_self_append_local( 904 + sv_compiler_t *c, sv_ast_t *node, 905 + int *out_local_idx, uint16_t *out_slot, sv_ast_t **out_rhs 906 + ) { 907 + if (!c || !node || node->type != N_ASSIGN || !node->left || node->left->type != N_IDENT) 908 + return false; 909 + if (c->with_depth > 0) return false; 910 + 911 + int local = resolve_local(c, node->left->str, node->left->len); 912 + if (local < 0 || c->locals[local].is_const) return false; 913 + if (c->locals[local].is_tdz) return false; 914 + if (c->locals[local].depth == -1 && has_implicit_arguments_obj(c)) return false; 915 + 916 + sv_ast_t *rhs = NULL; 917 + if (node->op == TOK_PLUS_ASSIGN) rhs = node->right; 918 + else if ( 919 + node->op == TOK_ASSIGN && 920 + node->right && node->right->type == N_BINARY && node->right->op == TOK_PLUS && 921 + node->right->left && node->right->left->type == N_IDENT && 922 + node->right->left->len == node->left->len && 923 + memcmp(node->right->left->str, node->left->str, node->left->len) == 0 924 + ) rhs = node->right->right; 925 + 926 + if (!rhs) return false; 927 + uint8_t local_type = get_local_inferred_type(c, local); 928 + uint8_t rhs_type = infer_expr_type(c, rhs); 929 + if (local_type != SV_TI_STR && rhs_type != SV_TI_STR) 930 + return false; 931 + 932 + if (out_local_idx) *out_local_idx = local; 933 + if (out_slot) *out_slot = (uint16_t)local_to_frame_slot(c, local); 934 + if (out_rhs) *out_rhs = rhs; 935 + 936 + return true; 937 + } 938 + 939 + static bool is_self_append_inplace_safe_ident(sv_compiler_t *c, sv_ast_t *node) { 940 + if (!c || !node || node->type != N_IDENT) return false; 941 + 942 + if (resolve_local(c, node->str, node->len) != -1) return true; 943 + if (resolve_upvalue(c, node->str, node->len) != -1) return true; 944 + 945 + if (is_ident_str(node->str, node->len, "arguments", 9)) { 946 + if (has_implicit_arguments_obj(c)) return true; 947 + if (c->is_arrow && resolve_arguments_upvalue(c) != -1) return true; 948 + } 949 + 950 + if (c->is_arrow && is_ident_str(node->str, node->len, "super", 5)) 951 + return resolve_super_upvalue(c) != -1; 952 + 953 + if (has_module_import_binding(c) && is_ident_str(node->str, node->len, "import", 6)) 954 + return true; 955 + 956 + return false; 957 + } 958 + 959 + static bool is_self_append_inplace_safe_expr(sv_compiler_t *c, sv_ast_t *node) { 960 + if (!node) return false; 961 + 962 + switch (node->type) { 963 + case N_NUMBER: 964 + case N_STRING: 965 + case N_BIGINT: 966 + case N_BOOL: 967 + case N_NULL: 968 + case N_UNDEF: 969 + return true; 970 + 971 + case N_IDENT: 972 + return is_self_append_inplace_safe_ident(c, node); 973 + 974 + case N_BINARY: return 975 + is_self_append_inplace_safe_expr(c, node->left) && 976 + is_self_append_inplace_safe_expr(c, node->right); 977 + 978 + case N_UNARY: 979 + case N_TYPEOF: 980 + case N_VOID: 981 + return is_self_append_inplace_safe_expr(c, node->left); 982 + 983 + default: 984 + return false; 985 + } 986 + } 987 + 988 + static bool compile_self_append_stmt(sv_compiler_t *c, sv_ast_t *node) { 989 + int local = -1; 990 + uint16_t slot = 0; 991 + sv_ast_t *rhs = NULL; 992 + if (!match_self_append_local(c, node, &local, &slot, &rhs)) return false; 993 + if (is_self_append_inplace_safe_expr(c, rhs)) { 994 + compile_expr(c, rhs); 995 + emit_slot_op(c, OP_STR_APPEND_LOCAL, slot); 996 + } else { 997 + emit_slot_op(c, OP_GET_SLOT_RAW, slot); 998 + compile_expr(c, rhs); 999 + emit_slot_op(c, OP_STR_ALC_SNAPSHOT, slot); 1000 + } 1001 + set_local_inferred_type(c, local, SV_TI_UNKNOWN); 1002 + return true; 894 1003 } 895 1004 896 1005 ··· 1612 1721 void compile_assign(sv_compiler_t *c, sv_ast_t *node) { 1613 1722 sv_ast_t *target = node->left; 1614 1723 uint8_t op = node->op; 1724 + int append_local = -1; 1725 + uint16_t append_slot = 0; 1726 + sv_ast_t *append_rhs = NULL; 1727 + 1728 + bool can_append_builder = match_self_append_local( 1729 + c, node, &append_local, 1730 + &append_slot, &append_rhs 1731 + ); 1615 1732 1616 1733 if (op == TOK_ASSIGN) { 1617 1734 if (target->type == N_MEMBER && !(target->flags & 1)) { ··· 1622 1739 emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); 1623 1740 return; 1624 1741 } 1625 - 1742 + 1626 1743 if (target->type == N_MEMBER && (target->flags & 1)) { 1627 1744 compile_expr(c, target->left); 1628 1745 compile_expr(c, target->right); ··· 1631 1748 emit_op(c, OP_PUT_ELEM); 1632 1749 return; 1633 1750 } 1751 + 1752 + if (can_append_builder) { 1753 + if (is_self_append_inplace_safe_expr(c, append_rhs)) { 1754 + compile_expr(c, append_rhs); 1755 + emit_slot_op(c, OP_STR_APPEND_LOCAL, append_slot); 1756 + } else { 1757 + emit_slot_op(c, OP_GET_SLOT_RAW, append_slot); 1758 + compile_expr(c, append_rhs); 1759 + emit_slot_op(c, OP_STR_ALC_SNAPSHOT, append_slot); 1760 + } 1761 + emit_get_var(c, target->str, target->len); 1762 + set_local_inferred_type(c, append_local, SV_TI_UNKNOWN); 1763 + return; 1764 + } 1634 1765 1635 1766 compile_expr(c, node->right); 1636 1767 compile_lhs_set(c, target, true); ··· 1641 1772 int lhs_local = resolve_local(c, target->str, target->len); 1642 1773 uint8_t lhs_type = (lhs_local >= 0) ? get_local_inferred_type(c, lhs_local) : SV_TI_UNKNOWN; 1643 1774 uint8_t rhs_type = infer_expr_type(c, node->right); 1775 + 1776 + if (can_append_builder) { 1777 + if (is_self_append_inplace_safe_expr(c, append_rhs)) { 1778 + compile_expr(c, append_rhs); 1779 + emit_slot_op(c, OP_STR_APPEND_LOCAL, append_slot); 1780 + } else { 1781 + emit_slot_op(c, OP_GET_SLOT_RAW, append_slot); 1782 + compile_expr(c, append_rhs); 1783 + emit_slot_op(c, OP_STR_ALC_SNAPSHOT, append_slot); 1784 + } 1785 + emit_get_var(c, target->str, target->len); 1786 + set_local_inferred_type(c, append_local, SV_TI_UNKNOWN); 1787 + return; 1788 + } 1644 1789 1645 1790 if (op == TOK_PLUS_ASSIGN) { 1646 1791 int slot = resolve_local_slot(c, target->str, target->len); ··· 2725 2870 2726 2871 case N_LABEL: 2727 2872 compile_label(c, node); 2873 + break; 2874 + 2875 + case N_ASSIGN: 2876 + if (compile_self_append_stmt(c, node)) break; 2877 + compile_expr(c, node); 2878 + emit_op(c, OP_POP); 2728 2879 break; 2729 2880 2730 2881 case N_FUNC:
+308 -21
src/silver/engine.c
··· 176 176 js_set_error_site_from_bc(js, func, bc_off, func->filename); 177 177 } 178 178 179 + // TODO: move to strings.c 180 + static inline bool sv_builder_has_cached_value(const ant_string_builder_t *builder) { 181 + return builder && builder->cached != js_mkundef(); 182 + } 179 183 184 + static inline ant_flat_string_t *sv_string_builder_flat_ptr(ant_value_t value) { 185 + if (vtype(value) != T_STR || str_is_heap_rope(value) || str_is_heap_builder(value)) return NULL; 186 + return (ant_flat_string_t *)(uintptr_t)vdata(value); 187 + } 188 + 189 + static inline ant_string_builder_t *sv_string_builder_heap_ptr(ant_value_t value) { 190 + if (vtype(value) != T_STR || !str_is_heap_builder(value)) return NULL; 191 + return ant_str_builder_ptr(value); 192 + } 193 + 194 + static inline uint8_t sv_builder_chunk_ascii_state(ant_flat_string_t *flat) { 195 + if (!flat) return STR_ASCII_UNKNOWN; 196 + if (flat->is_ascii == STR_ASCII_UNKNOWN) 197 + flat->is_ascii = str_detect_ascii_bytes(flat->bytes, (size_t)flat->len); 198 + return flat->is_ascii; 199 + } 200 + 201 + static inline void sv_builder_note_ascii(ant_string_builder_t *builder, uint8_t state) { 202 + if (!builder) return; 203 + if (state == STR_ASCII_NO) builder->ascii_state = STR_ASCII_NO; 204 + else if (builder->ascii_state != STR_ASCII_NO && state == STR_ASCII_YES) 205 + builder->ascii_state = STR_ASCII_YES; 206 + } 207 + 208 + static inline void sv_builder_record_flat(ant_string_builder_t *builder, ant_flat_string_t *flat) { 209 + if (!builder || !flat) return; 210 + builder->len += flat->len; 211 + sv_builder_note_ascii(builder, sv_builder_chunk_ascii_state(flat)); 212 + } 213 + 214 + static ant_value_t sv_builder_normalize_chunk(ant_t *js, ant_value_t value) { 215 + if (vtype(value) != T_STR) return js_mkerr(js, "string builder expects string chunk"); 216 + return str_materialize(js, value); 217 + } 218 + 219 + static ant_value_t sv_builder_push_chunk_value( 220 + ant_t *js, ant_string_builder_t *builder, ant_value_t chunk 221 + ) { 222 + if (!builder) return js_mkerr(js, "string builder chunk allocation failed"); 223 + ant_builder_chunk_t *node = (ant_builder_chunk_t *)js_type_alloc( 224 + js, ANT_ALLOC_ROPE, sizeof(*node), _Alignof(ant_builder_chunk_t) 225 + ); 226 + if (!node) return js_mkerr(js, "string builder chunk allocation failed"); 227 + node->next = NULL; 228 + node->value = chunk; 229 + if (builder->chunk_tail) builder->chunk_tail->next = node; 230 + else builder->head = node; 231 + builder->chunk_tail = node; 232 + return js_mkundef(); 233 + } 234 + 235 + static ant_value_t sv_builder_flush_tail( 236 + ant_t *js, ant_string_builder_t *builder 237 + ) { 238 + if (!builder || builder->tail_len == 0) return js_mkundef(); 239 + ant_value_t tail = js_mkstr(js, builder->tail, builder->tail_len); 240 + if (is_err(tail)) return tail; 241 + ant_value_t push = sv_builder_push_chunk_value(js, builder, tail); 242 + if (is_err(push)) return push; 243 + builder->tail_len = 0; 244 + return js_mkundef(); 245 + } 246 + 247 + static ant_value_t sv_builder_append_flat( 248 + ant_t *js, ant_string_builder_t *builder, ant_value_t chunk 249 + ) { 250 + ant_flat_string_t *flat = sv_string_builder_flat_ptr(chunk); 251 + if (!flat) return js_mkerr(js, "string builder received non-flat string"); 252 + if (flat->len == 0) return js_mkundef(); 253 + 254 + if ( 255 + flat->len <= STR_BUILDER_TAIL_CAP && 256 + builder->tail_len + flat->len <= STR_BUILDER_TAIL_CAP 257 + ) { 258 + memcpy(builder->tail + builder->tail_len, flat->bytes, (size_t)flat->len); 259 + builder->tail_len = (uint16_t)(builder->tail_len + flat->len); 260 + sv_builder_record_flat(builder, flat); 261 + return js_mkundef(); 262 + } 263 + 264 + ant_value_t flush = sv_builder_flush_tail(js, builder); 265 + if (is_err(flush)) return flush; 266 + ant_value_t push = sv_builder_push_chunk_value(js, builder, chunk); 267 + 268 + if (is_err(push)) return push; 269 + sv_builder_record_flat(builder, flat); 270 + 271 + return js_mkundef(); 272 + } 273 + 274 + static ant_value_t sv_string_builder_new(ant_t *js) { 275 + ant_string_builder_t *builder = (ant_string_builder_t *)js_type_alloc( 276 + js, ANT_ALLOC_ROPE, sizeof(*builder), _Alignof(ant_string_builder_t) 277 + ); 278 + if (!builder) return js_mkerr(js, "string builder allocation failed"); 279 + memset(builder, 0, sizeof(*builder)); 280 + builder->cached = js_mkundef(); 281 + builder->ascii_state = STR_ASCII_YES; 282 + return ant_mkbuilder_value(builder); 283 + } 284 + 285 + static inline void sv_record_slot_feedback( 286 + sv_frame_t *frame, sv_func_t *func, uint16_t slot_idx, ant_value_t value 287 + ) { 288 + if (!frame || !func) return; 289 + if ((int)slot_idx < func->param_count) return; 290 + sv_tfb_record_local(func, (int)(slot_idx - func->param_count), value); 291 + } 292 + 293 + bool sv_slot_has_open_upvalue(sv_vm_t *vm, ant_value_t *slot) { 294 + if (!vm || !slot) return false; 295 + for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) 296 + if (uv->location == slot) return true; 297 + return false; 298 + } 299 + 300 + ant_value_t sv_string_builder_read_value(ant_t *js, ant_value_t value) { 301 + if (vtype(value) == T_STR && str_is_heap_builder(value)) 302 + return str_materialize(js, value); 303 + return value; 304 + } 305 + 306 + static ant_value_t sv_slot_generic_add_store( 307 + sv_vm_t *vm, ant_t *js, ant_value_t *slot, ant_value_t lhs, ant_value_t rhs 308 + ) { 309 + vm->stack[vm->sp++] = lhs; 310 + vm->stack[vm->sp++] = rhs; 311 + ant_value_t err = sv_op_add(vm, js); 312 + if (is_err(err)) return err; 313 + *slot = vm->stack[--vm->sp]; 314 + return js_mkundef(); 315 + } 316 + 317 + ant_value_t sv_string_builder_flush_slot( 318 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint16_t slot_idx 319 + ) { 320 + ant_value_t *slot = sv_frame_slot_ptr(frame, slot_idx); 321 + if (!slot || vtype(*slot) != T_STR || !str_is_heap_builder(*slot)) return js_mkundef(); 322 + 323 + ant_value_t out = str_materialize(js, *slot); 324 + if (is_err(out)) return out; 325 + *slot = out; 326 + sv_record_slot_feedback(frame, frame->func, slot_idx, out); 327 + return out; 328 + } 329 + 330 + ant_value_t sv_string_builder_append_slot( 331 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, 332 + sv_func_t *func, uint16_t slot_idx, ant_value_t rhs 333 + ) { 334 + ant_value_t *slot = sv_frame_slot_ptr(frame, slot_idx); 335 + if (!slot) return js_mkerr(js, "invalid string builder slot"); 336 + 337 + ant_value_t lhs = *slot; 338 + ant_string_builder_t *builder = sv_string_builder_heap_ptr(lhs); 339 + 340 + if (builder) { 341 + ant_value_t rhs_str = coerce_to_str_concat(js, rhs); 342 + if (is_err(rhs_str)) return rhs_str; 343 + rhs_str = sv_builder_normalize_chunk(js, rhs_str); 344 + if (is_err(rhs_str)) return rhs_str; 345 + builder->cached = js_mkundef(); 346 + ant_value_t append_err = sv_builder_append_flat(js, builder, rhs_str); 347 + if (is_err(append_err)) return append_err; 348 + sv_record_slot_feedback(frame, func, slot_idx, lhs); 349 + return js_mkundef(); 350 + } 351 + 352 + if (vtype(lhs) == T_NUM && vtype(rhs) == T_NUM) { 353 + *slot = tov(tod(lhs) + tod(rhs)); 354 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 355 + return js_mkundef(); 356 + } 357 + 358 + ant_value_t lu = unwrap_primitive(js, lhs); 359 + ant_value_t ru = unwrap_primitive(js, rhs); 360 + bool string_concat = is_non_numeric(lu) || is_non_numeric(ru); 361 + if (!string_concat) { 362 + ant_value_t add_err = sv_slot_generic_add_store(vm, js, slot, lhs, rhs); 363 + if (is_err(add_err)) return add_err; 364 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 365 + return js_mkundef(); 366 + } 367 + 368 + ant_value_t lhs_str = coerce_to_str_concat(js, lhs); 369 + if (is_err(lhs_str)) return lhs_str; 370 + ant_value_t rhs_str = coerce_to_str_concat(js, rhs); 371 + if (is_err(rhs_str)) return rhs_str; 372 + lhs_str = sv_builder_normalize_chunk(js, lhs_str); 373 + if (is_err(lhs_str)) return lhs_str; 374 + rhs_str = sv_builder_normalize_chunk(js, rhs_str); 375 + if (is_err(rhs_str)) return rhs_str; 376 + 377 + ant_value_t builder_value = sv_string_builder_new(js); 378 + if (is_err(builder_value)) return builder_value; 379 + builder = sv_string_builder_heap_ptr(builder_value); 380 + 381 + ant_value_t append_lhs = sv_builder_append_flat(js, builder, lhs_str); 382 + if (is_err(append_lhs)) return append_lhs; 383 + ant_value_t append_rhs = sv_builder_append_flat(js, builder, rhs_str); 384 + if (is_err(append_rhs)) return append_rhs; 385 + *slot = builder_value; 386 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 387 + 388 + return js_mkundef(); 389 + } 390 + 391 + ant_value_t sv_string_builder_append_snapshot_slot( 392 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, 393 + sv_func_t *func, uint16_t slot_idx, ant_value_t lhs, ant_value_t rhs 394 + ) { 395 + ant_value_t *slot = sv_frame_slot_ptr(frame, slot_idx); 396 + if (!slot) return js_mkerr(js, "invalid string builder slot"); 397 + 398 + // the snapshot path is only semantically required if the slot changed while 399 + // evaluating the RHS. when it did not, delegate to the normal append path so 400 + // active builders can keep appending in place instead of rebuilding. 401 + if (*slot == lhs) 402 + return sv_string_builder_append_slot(vm, js, frame, func, slot_idx, rhs); 403 + 404 + if (vtype(lhs) == T_NUM && vtype(rhs) == T_NUM) { 405 + *slot = tov(tod(lhs) + tod(rhs)); 406 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 407 + return js_mkundef(); 408 + } 409 + 410 + ant_value_t lu = unwrap_primitive(js, lhs); 411 + ant_value_t ru = unwrap_primitive(js, rhs); 412 + 413 + bool string_concat = is_non_numeric(lu) || is_non_numeric(ru); 414 + if (!string_concat) { 415 + ant_value_t add_err = sv_slot_generic_add_store(vm, js, slot, lhs, rhs); 416 + if (is_err(add_err)) return add_err; 417 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 418 + return js_mkundef(); 419 + } 420 + 421 + ant_value_t lhs_str = coerce_to_str_concat(js, lhs); 422 + if (is_err(lhs_str)) return lhs_str; 423 + ant_value_t rhs_str = coerce_to_str_concat(js, rhs); 424 + if (is_err(rhs_str)) return rhs_str; 425 + lhs_str = sv_builder_normalize_chunk(js, lhs_str); 426 + if (is_err(lhs_str)) return lhs_str; 427 + rhs_str = sv_builder_normalize_chunk(js, rhs_str); 428 + if (is_err(rhs_str)) return rhs_str; 429 + 430 + ant_value_t builder_value = sv_string_builder_new(js); 431 + if (is_err(builder_value)) return builder_value; 432 + ant_string_builder_t *builder = sv_string_builder_heap_ptr(builder_value); 433 + 434 + ant_value_t append_lhs = sv_builder_append_flat(js, builder, lhs_str); 435 + if (is_err(append_lhs)) return append_lhs; 436 + ant_value_t append_rhs = sv_builder_append_flat(js, builder, rhs_str); 437 + if (is_err(append_rhs)) return append_rhs; 438 + *slot = builder_value; 439 + sv_record_slot_feedback(frame, func, slot_idx, *slot); 440 + 441 + return js_mkundef(); 442 + } 180 443 181 444 void sv_vm_visit_frame_funcs(sv_vm_t *vm, void (*visitor)(void *, sv_func_t *), void *ctx) { 182 445 if (!vm) return; ··· 202 465 *frame = &vm->frames[vm->fp]; *func = (*frame)->func; 203 466 *bp = (*frame)->bp; *lp = (*frame)->lp; 204 467 } 468 + 469 + static inline void sv_drop_frame_runtime_state(ant_t *js, sv_frame_t *frame) { 470 + if (frame && vtype(frame->arguments_obj) != T_UNDEF) { 471 + js_arguments_detach(js, frame->arguments_obj); 472 + frame->arguments_obj = js_mkundef(); 473 + }} 205 474 206 475 static inline ant_value_t sv_stage_frame_args( 207 476 sv_vm_t *vm, ant_t *js, sv_func_t *func, ant_value_t *args, int argc, ··· 486 755 // TODO: shorthand? 487 756 sv_frame_t *frame = &vm->frames[vm->fp]; 488 757 if (!resuming) { 758 + sv_drop_frame_runtime_state(js, frame); 489 759 frame->ip = ip; 490 760 frame->func = func; 491 761 frame->this = sv_normalize_this_for_frame(js, func, this); ··· 498 768 frame->completion.kind = SV_COMPLETION_NONE; 499 769 frame->completion.value = js_mkundef(); 500 770 frame->with_obj = js_mkundef(); 771 + frame->arguments_obj = js_mkundef(); 501 772 } else { 502 773 func = frame->func; 503 774 ip = frame->ip; ··· 675 946 L_ARRAY: { sv_op_array(vm, js, ip); NEXT(3); } 676 947 677 948 L_REGEXP: { sv_op_regexp(vm, js); NEXT(1); } 678 - L_CLOSURE: { sv_op_closure(vm, js, frame, func, ip); NEXT(5); } 949 + L_CLOSURE: { VM_CHECK(sv_op_closure(vm, js, frame, func, ip)); NEXT(5); } 679 950 680 951 L_POP: { sv_op_pop(vm); NEXT(1); } 681 952 L_DUP: { sv_op_dup(vm); NEXT(1); } ··· 691 962 L_SWAP_UNDER: { sv_op_swap_under(vm); NEXT(1); } 692 963 L_ROT4_UNDER: { sv_op_rot4_under(vm); NEXT(1); } 693 964 694 - L_GET_LOCAL: { sv_op_get_local(vm, lp, ip); NEXT(3); } 695 - L_PUT_LOCAL: { sv_op_put_local(vm, lp, func, ip); NEXT(3); } 696 - L_SET_LOCAL: { sv_op_set_local(vm, lp, func, ip); NEXT(3); } 697 - L_GET_LOCAL8: { sv_op_get_local8(vm, lp, ip); NEXT(2); } 698 - L_PUT_LOCAL8: { sv_op_put_local8(vm, lp, func, ip); NEXT(2); } 699 - L_SET_LOCAL8: { sv_op_set_local8(vm, lp, func, ip); NEXT(2); } 700 - L_SET_LOCAL_UNDEF: { sv_op_set_local_undef(lp, ip); NEXT(3); } 965 + L_GET_LOCAL: { VM_CHECK(sv_op_get_local(vm, lp, js, frame, ip)); NEXT(3); } 966 + L_PUT_LOCAL: { sv_op_put_local(vm, lp, frame, func, ip); NEXT(3); } 967 + L_SET_LOCAL: { sv_op_set_local(vm, lp, frame, func, ip); NEXT(3); } 968 + L_GET_LOCAL8: { VM_CHECK(sv_op_get_local8(vm, lp, js, frame, ip)); NEXT(2); } 969 + L_PUT_LOCAL8: { sv_op_put_local8(vm, lp, frame, func, ip); NEXT(2); } 970 + L_SET_LOCAL8: { sv_op_set_local8(vm, lp, frame, func, ip); NEXT(2); } 971 + L_SET_LOCAL_UNDEF: { sv_op_set_local_undef(frame, lp, ip); NEXT(3); } 701 972 702 - L_GET_LOCAL_CHK: { VM_CHECK(sv_op_get_local_chk(vm, lp, js, func, ip)); NEXT(7); } 703 - L_PUT_LOCAL_CHK: { VM_CHECK(sv_op_put_local_chk(vm, lp, js, func, ip)); NEXT(7); } 973 + L_GET_LOCAL_CHK: { VM_CHECK(sv_op_get_local_chk(vm, lp, js, frame, func, ip)); NEXT(7); } 974 + L_PUT_LOCAL_CHK: { VM_CHECK(sv_op_put_local_chk(vm, lp, js, frame, func, ip)); NEXT(7); } 975 + L_GET_SLOT_RAW: { VM_CHECK(sv_op_get_slot_raw(vm, js, frame, ip)); NEXT(3); } 704 976 705 - L_GET_ARG: { sv_op_get_arg(vm, frame, ip); NEXT(3); } 706 - L_PUT_ARG: { sv_op_put_arg(vm, frame, ip); NEXT(3); } 707 - L_SET_ARG: { sv_op_set_arg(vm, frame, ip); NEXT(3); } 708 - L_REST: { sv_op_rest(vm, frame, js, ip); NEXT(3); } 977 + L_GET_ARG: { VM_CHECK(sv_op_get_arg(vm, js, frame, ip)); NEXT(3); } 978 + L_PUT_ARG: { sv_op_put_arg(vm, js, frame, ip); NEXT(3); } 979 + L_SET_ARG: { sv_op_set_arg(vm, js, frame, ip); NEXT(3); } 980 + L_REST: { sv_op_rest(vm, frame, js, ip); NEXT(3); } 709 981 710 982 L_GET_UPVAL: { VM_CHECK(sv_op_get_upval(vm, frame, js, ip)); NEXT(3); } 711 983 L_PUT_UPVAL: { sv_op_put_upval(vm, frame, ip); NEXT(3); } 712 984 L_SET_UPVAL: { sv_op_set_upval(vm, frame, ip); NEXT(3); } 713 - L_CLOSE_UPVAL: { sv_op_close_upval(vm, frame, ip); NEXT(3); } 985 + L_CLOSE_UPVAL: { VM_CHECK(sv_op_close_upval(vm, frame, ip)); NEXT(3); } 714 986 715 987 L_GET_GLOBAL: { VM_CHECK(sv_op_get_global(vm, js, func, ip)); NEXT(7); } 716 988 L_GET_GLOBAL_UNDEF: { sv_op_get_global_undef(vm, js, func, ip); NEXT(7); } ··· 782 1054 NEXT(1); 783 1055 } 784 1056 785 - L_MUL: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mul(vm, js)); NEXT(1); } 786 - L_DIV: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_div(vm, js)); NEXT(1); } 787 - L_MOD: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mod(vm, js)); NEXT(1); } 788 - L_NEG: { sv_tfb_record1(func, ip, vm->stack[vm->sp-1]); VM_CHECK(sv_op_neg(vm, js)); NEXT(1); } 1057 + L_MUL: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mul(vm, js)); NEXT(1); } 1058 + L_DIV: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_div(vm, js)); NEXT(1); } 1059 + L_MOD: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mod(vm, js)); NEXT(1); } 1060 + L_NEG: { sv_tfb_record1(func, ip, vm->stack[vm->sp-1]); VM_CHECK(sv_op_neg(vm, js)); NEXT(1); } 789 1061 L_ADD_LOCAL: { sv_tfb_record2(func, ip, lp[sv_get_u8(ip+1)], vm->stack[vm->sp-1]); VM_CHECK(sv_op_add_local(vm, lp, js, func, ip)); NEXT(2); } 1062 + 1063 + L_STR_APPEND_LOCAL: { VM_CHECK(sv_op_str_append_local(vm, js, frame, func, ip)); NEXT(3); } 1064 + L_STR_ALC_SNAPSHOT: { VM_CHECK(sv_op_str_append_local_snapshot(vm, js, frame, func, ip)); NEXT(3); } 1065 + L_STR_FLUSH_LOCAL: { VM_CHECK(sv_op_str_flush_local(vm, js, frame, ip)); NEXT(3); } 790 1066 791 1067 L_EXP: { VM_CHECK(sv_op_exp(vm, js)); NEXT(1); } 792 1068 L_UPLUS: { VM_CHECK(sv_op_uplus(vm, js)); NEXT(1); } ··· 794 1070 L_DEC: { sv_op_dec(vm); NEXT(1); } 795 1071 L_POST_INC: { sv_op_post_inc(vm); NEXT(1); } 796 1072 L_POST_DEC: { sv_op_post_dec(vm); NEXT(1); } 797 - L_INC_LOCAL: { sv_op_inc_local(vm, lp, func, ip); NEXT(2); } 798 - L_DEC_LOCAL: { sv_op_dec_local(vm, lp, func, ip); NEXT(2); } 1073 + 1074 + L_INC_LOCAL: { VM_CHECK(sv_op_inc_local(lp, js, func, ip)); NEXT(2); } 1075 + L_DEC_LOCAL: { VM_CHECK(sv_op_dec_local(lp, js, func, ip)); NEXT(2); } 799 1076 800 1077 L_EQ: { sv_op_eq(vm, js); NEXT(1); } 801 1078 L_NE: { sv_op_ne(vm, js); NEXT(1); } ··· 984 1261 frame->handler_base = vm->handler_depth; 985 1262 frame->handler_top = vm->handler_depth; 986 1263 frame->argc = call_argc; 1264 + frame->arguments_obj = js_mkundef(); 987 1265 ant_value_t *call_bp = NULL; 988 1266 ant_value_t *call_lp = NULL; 989 1267 ant_value_t call_stage_err = sv_stage_frame_args( ··· 1074 1352 frame->handler_base = vm->handler_depth; 1075 1353 frame->handler_top = vm->handler_depth; 1076 1354 frame->argc = call_argc; 1355 + frame->arguments_obj = js_mkundef(); 1077 1356 ant_value_t *call_bp = NULL; 1078 1357 ant_value_t *call_lp = NULL; 1079 1358 ant_value_t call_stage_err = sv_stage_frame_args( ··· 1198 1477 ant_value_t call_func = vm->stack[vm->sp - call_argc - 1]; 1199 1478 sv_closure_t *closure = js_func_closure(call_func); 1200 1479 if (frame->bp && vm->open_upvalues) sv_close_upvalues_from_slot(vm, frame->bp); 1480 + sv_drop_frame_runtime_state(js, frame); 1201 1481 vm->sp = frame->prev_sp; 1202 1482 int arg_slots = ( 1203 1483 (int)call_argc > closure->func->param_count) ··· 1218 1498 frame->argc = call_argc; 1219 1499 frame->handler_base = vm->handler_depth; 1220 1500 frame->handler_top = vm->handler_depth; 1501 + frame->arguments_obj = js_mkundef(); 1221 1502 frame->bp = base; 1222 1503 frame->lp = new_lp; 1223 1504 frame->upvalues = closure->upvalues; ··· 1255 1536 vm_result = r; 1256 1537 goto sv_leave; 1257 1538 } 1539 + sv_drop_frame_runtime_state(js, frame); 1258 1540 vm->fp--; 1259 1541 frame = &vm->frames[vm->fp]; 1260 1542 func = frame->func; ··· 1285 1567 vm_result = r; 1286 1568 goto sv_leave; 1287 1569 } 1570 + sv_drop_frame_runtime_state(js, frame); 1288 1571 vm->fp--; 1289 1572 frame = &vm->frames[vm->fp]; 1290 1573 func = frame->func; ··· 1315 1598 vm_result = r; 1316 1599 goto sv_leave; 1317 1600 } 1601 + sv_drop_frame_runtime_state(js, frame); 1318 1602 vm->fp--; 1319 1603 frame = &vm->frames[vm->fp]; 1320 1604 func = frame->func; ··· 1357 1641 vm_result = completion; 1358 1642 goto sv_leave; 1359 1643 } 1644 + sv_drop_frame_runtime_state(js, frame); 1360 1645 vm->fp--; 1361 1646 frame = &vm->frames[vm->fp]; 1362 1647 func = frame->func; ··· 1543 1828 ant_value_t *drop_bp = vm->frames[f].bp; 1544 1829 if (drop_bp) sv_close_upvalues_from_slot(vm, drop_bp); 1545 1830 }} 1831 + for (int f = vm->fp; f >= entry_fp; f--) 1832 + sv_drop_frame_runtime_state(js, &vm->frames[f]); 1546 1833 vm->fp = entry_fp; 1547 1834 vm->sp = vm->frames[entry_fp].prev_sp; 1548 1835 vm->handler_depth = vm->frames[entry_fp].handler_base;
+35
src/silver/glue.c
··· 51 51 return SV_JIT_BAILOUT; 52 52 } 53 53 54 + ant_value_t jit_helper_str_append_local( 55 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 56 + ant_value_t *locals, uint16_t local_idx, ant_value_t rhs 57 + ) { 58 + if (!func || !locals || local_idx >= (uint16_t)func->max_locals) 59 + return SV_JIT_BAILOUT; 60 + 61 + sv_frame_t frame = { 62 + .func = func, 63 + .lp = locals, 64 + .argc = func->param_count, 65 + }; 66 + 67 + uint16_t slot_idx = (uint16_t)(func->param_count + local_idx); 68 + return sv_string_builder_append_slot(vm, js, &frame, func, slot_idx, rhs); 69 + } 70 + 71 + ant_value_t jit_helper_str_append_local_snapshot( 72 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 73 + ant_value_t *locals, uint16_t local_idx, 74 + ant_value_t lhs, ant_value_t rhs 75 + ) { 76 + if (!func || !locals || local_idx >= (uint16_t)func->max_locals) 77 + return SV_JIT_BAILOUT; 78 + 79 + sv_frame_t frame = { 80 + .func = func, 81 + .lp = locals, 82 + .argc = func->param_count, 83 + }; 84 + 85 + uint16_t slot_idx = (uint16_t)(func->param_count + local_idx); 86 + return sv_string_builder_append_snapshot_slot(vm, js, &frame, func, slot_idx, lhs, rhs); 87 + } 88 + 54 89 ant_value_t jit_helper_lt(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) { 55 90 if (vtype(l) == T_NUM && vtype(r) == T_NUM) return js_bool(tod(l) < tod(r)); 56 91 return SV_JIT_BAILOUT;
+30 -3
src/silver/ops/arithmetic.h
··· 187 187 vm->stack[vm->sp++] = tov(tod(old) - 1.0); 188 188 } 189 189 190 - static inline void sv_op_inc_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 190 + static inline ant_value_t sv_op_inc_local(ant_value_t *lp, ant_t *js, sv_func_t *func, uint8_t *ip) { 191 191 uint8_t idx = sv_get_u8(ip + 1); 192 192 ant_value_t *slot = &lp[idx]; 193 + 194 + if (vtype(*slot) == T_STR && str_is_heap_builder(*slot)) { 195 + ant_value_t out = str_materialize(js, *slot); 196 + if (is_err(out)) return out; 197 + *slot = out; 198 + } 199 + 193 200 *slot = tov(tod(*slot) + 1.0); 194 201 sv_tfb_record_local(func, (int)idx, *slot); 202 + 203 + return js_mkundef(); 195 204 } 196 205 197 - static inline void sv_op_dec_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 206 + static inline ant_value_t sv_op_dec_local(ant_value_t *lp, ant_t *js, sv_func_t *func, uint8_t *ip) { 198 207 uint8_t idx = sv_get_u8(ip + 1); 199 208 ant_value_t *slot = &lp[idx]; 209 + 210 + if (vtype(*slot) == T_STR && str_is_heap_builder(*slot)) { 211 + ant_value_t out = str_materialize(js, *slot); 212 + if (is_err(out)) return out; 213 + *slot = out; 214 + } 215 + 200 216 *slot = tov(tod(*slot) - 1.0); 201 217 sv_tfb_record_local(func, (int)idx, *slot); 202 - (void)vm; 218 + 219 + return js_mkundef(); 203 220 } 204 221 205 222 static inline ant_value_t sv_op_add_local(sv_vm_t *vm, ant_value_t *lp, ant_t *js, sv_func_t *func, uint8_t *ip) { 206 223 uint8_t idx = sv_get_u8(ip + 1); 207 224 ant_value_t *slot = &lp[idx]; 225 + 226 + if (vtype(*slot) == T_STR && str_is_heap_builder(*slot)) { 227 + ant_value_t out = str_materialize(js, *slot); 228 + if (is_err(out)) return out; 229 + *slot = out; 230 + } 231 + 208 232 ant_value_t val = vm->stack[--vm->sp]; 209 233 if (vtype(*slot) == T_NUM && vtype(val) == T_NUM) { 210 234 *slot = tov(tod(*slot) + tod(val)); 211 235 sv_tfb_record_local(func, (int)idx, *slot); 212 236 return tov(0); 213 237 } 238 + 214 239 vm->stack[vm->sp++] = *slot; 215 240 vm->stack[vm->sp++] = val; 241 + 216 242 ant_value_t err = sv_op_add(vm, js); 217 243 if (is_err(err)) return err; 218 244 *slot = vm->stack[--vm->sp]; 219 245 sv_tfb_record_local(func, (int)idx, *slot); 246 + 220 247 return tov(0); 221 248 } 222 249
+9 -19
src/silver/ops/coercion.h
··· 2 2 #define SV_COERCION_H 3 3 4 4 #include "silver/engine.h" 5 - #include "modules/symbol.h" 6 - 7 5 #include "esm/loader.h" 8 6 #include <string.h> 9 7 ··· 230 228 if (frame->lp) frame->lp[idx] = val; 231 229 break; 232 230 case WITH_FB_ARG: 233 - sv_frame_set_arg_value(frame, idx, val); 231 + sv_frame_set_arg_value(js, frame, idx, val); 234 232 break; 235 233 case WITH_FB_UPVAL: 236 234 if (frame->upvalues && (int)idx < frame->upvalue_count) { ··· 360 358 return; 361 359 } 362 360 363 - ant_value_t arr = js_mkarr(js); 364 - if (frame->bp && frame->argc > 0) { 365 - for (int i = 0; i < frame->argc; i++) js_arr_push(js, arr, frame->bp[i]); 366 - } 367 - 368 - if (sv_frame_is_strict(frame)) js_set_slot(arr, SLOT_STRICT_ARGS, js_true); 369 - else if (vtype(frame->callee) == T_FUNC) setprop_cstr(js, arr, "callee", 6, frame->callee); 370 - 371 - js_set_sym(js, arr, get_toStringTag_sym(), js_mkstr(js, "Arguments", 9)); 372 - ant_value_t array_proto = js_get_ctor_proto(js, "Array", 5); 373 - 374 - if (is_object_type(array_proto)) { 375 - ant_value_t iter_fn = js_get_sym(js, array_proto, get_iterator_sym()); 376 - if (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC) 377 - js_set_sym(js, arr, get_iterator_sym(), iter_fn); 361 + if (vtype(frame->arguments_obj) == T_UNDEF) { 362 + int mapped_count = sv_frame_is_strict(frame) || !frame->func ? 0 : frame->func->param_count; 363 + if (mapped_count > frame->argc) mapped_count = frame->argc; 364 + frame->arguments_obj = js_create_arguments_object( 365 + js, frame->callee, frame, frame->argc, 366 + mapped_count, sv_frame_is_strict(frame) 367 + ); 378 368 } 379 369 380 - vm->stack[vm->sp++] = arr; 370 + vm->stack[vm->sp++] = frame->arguments_obj; 381 371 } 382 372 383 373 #endif
+2 -2
src/silver/ops/iteration.h
··· 74 74 } 75 75 76 76 if (vtype(iterable) == T_STR) { 77 - if (str_is_heap_rope(iterable)) { 78 - iterable = rope_flatten(js, iterable); 77 + if (str_is_heap_rope(iterable) || str_is_heap_builder(iterable)) { 78 + iterable = str_materialize(js, iterable); 79 79 if (is_err(iterable)) return iterable; 80 80 } 81 81 vm->stack[vm->sp++] = iterable;
+109 -21
src/silver/ops/locals.h
··· 4 4 #include "silver/engine.h" 5 5 #include "errors.h" 6 6 7 - static inline void sv_op_get_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { 7 + static inline ant_value_t sv_op_get_local( 8 + sv_vm_t *vm, ant_value_t *lp, 9 + ant_t *js, sv_frame_t *frame, uint8_t *ip 10 + ) { 8 11 uint16_t idx = sv_get_u16(ip + 1); 9 - vm->stack[vm->sp++] = lp[idx]; 12 + ant_value_t value = lp[idx]; 13 + if (vtype(value) == T_STR && str_is_heap_builder(value)) { 14 + value = str_materialize(js, value); 15 + if (is_err(value)) return value; 16 + } 17 + vm->stack[vm->sp++] = value; 18 + return js_mkundef(); 10 19 } 11 20 12 - static inline void sv_op_put_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 21 + static inline void sv_op_put_local( 22 + sv_vm_t *vm, ant_value_t *lp, 23 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 24 + ) { 13 25 uint16_t idx = sv_get_u16(ip + 1); 14 26 lp[idx] = vm->stack[--vm->sp]; 15 27 sv_tfb_record_local(func, (int)idx, lp[idx]); 16 28 } 17 29 18 - static inline void sv_op_set_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 30 + static inline void sv_op_set_local( 31 + sv_vm_t *vm, ant_value_t *lp, 32 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 33 + ) { 19 34 uint16_t idx = sv_get_u16(ip + 1); 20 35 lp[idx] = vm->stack[vm->sp - 1]; 21 36 sv_tfb_record_local(func, (int)idx, lp[idx]); 22 37 } 23 38 24 - static inline void sv_op_get_local8(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { 39 + static inline ant_value_t sv_op_get_local8( 40 + sv_vm_t *vm, ant_value_t *lp, 41 + ant_t *js, sv_frame_t *frame, uint8_t *ip 42 + ) { 25 43 uint8_t idx = sv_get_u8(ip + 1); 26 - vm->stack[vm->sp++] = lp[idx]; 44 + ant_value_t value = lp[idx]; 45 + if (vtype(value) == T_STR && str_is_heap_builder(value)) { 46 + value = str_materialize(js, value); 47 + if (is_err(value)) return value; 48 + } 49 + vm->stack[vm->sp++] = value; 50 + return js_mkundef(); 27 51 } 28 52 29 - static inline void sv_op_put_local8(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 53 + static inline void sv_op_put_local8( 54 + sv_vm_t *vm, ant_value_t *lp, 55 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 56 + ) { 30 57 uint8_t idx = sv_get_u8(ip + 1); 31 58 lp[idx] = vm->stack[--vm->sp]; 32 59 sv_tfb_record_local(func, (int)idx, lp[idx]); 33 60 } 34 61 35 - static inline void sv_op_set_local8(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { 62 + static inline void sv_op_set_local8( 63 + sv_vm_t *vm, ant_value_t *lp, 64 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 65 + ) { 36 66 uint8_t idx = sv_get_u8(ip + 1); 37 67 lp[idx] = vm->stack[vm->sp - 1]; 38 68 sv_tfb_record_local(func, (int)idx, lp[idx]); 39 69 } 40 70 41 - static inline void sv_op_set_local_undef(ant_value_t *lp, uint8_t *ip) { 71 + static inline void sv_op_set_local_undef(sv_frame_t *frame, ant_value_t *lp, uint8_t *ip) { 42 72 uint16_t idx = sv_get_u16(ip + 1); 43 73 lp[idx] = SV_TDZ; 44 74 } 45 75 46 76 static inline ant_value_t sv_op_get_local_chk( 47 77 sv_vm_t *vm, ant_value_t *lp, 48 - ant_t *js, sv_func_t *func, uint8_t *ip 78 + ant_t *js, sv_frame_t *frame, sv_func_t *func, uint8_t *ip 49 79 ) { 50 80 uint16_t idx = sv_get_u16(ip + 1); 51 81 ant_value_t val = lp[idx]; ··· 55 85 sv_atom_t *a = &func->atoms[ai]; 56 86 return js_mkerr_typed( 57 87 js, JS_ERR_REFERENCE, 58 - "Cannot access '%.*s' before initialization", (int)a->len, a->str); 88 + "Cannot access '%.*s' before initialization", 89 + (int)a->len, a->str 90 + ); 59 91 } 60 92 return js_mkerr_typed( 61 93 js, JS_ERR_REFERENCE, 62 - "Cannot access variable before initialization"); 94 + "Cannot access variable before initialization" 95 + ); 96 + } 97 + if (vtype(val) == T_STR && str_is_heap_builder(val)) { 98 + val = str_materialize(js, val); 99 + if (is_err(val)) return val; 63 100 } 64 101 vm->stack[vm->sp++] = val; 65 102 return val; 66 103 } 67 104 105 + static inline ant_value_t sv_op_get_slot_raw( 106 + sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint8_t *ip 107 + ) { 108 + uint16_t slot_idx = sv_get_u16(ip + 1); 109 + ant_value_t *slot = sv_frame_slot_ptr(frame, slot_idx); 110 + if (!slot) return js_mkerr(js, "invalid frame slot"); 111 + if (*slot == SV_TDZ) return js_mkerr_typed(js, JS_ERR_REFERENCE, 112 + "Cannot access variable before initialization" 113 + ); 114 + vm->stack[vm->sp++] = *slot; 115 + return js_mkundef(); 116 + } 117 + 68 118 static inline ant_value_t sv_op_put_local_chk( 69 119 sv_vm_t *vm, ant_value_t *lp, 70 - ant_t *js, sv_func_t *func, uint8_t *ip 120 + ant_t *js, sv_frame_t *frame, sv_func_t *func, uint8_t *ip 71 121 ) { 72 122 uint16_t idx = sv_get_u16(ip + 1); 73 123 ant_value_t *slot = &lp[idx]; ··· 77 127 sv_atom_t *a = &func->atoms[ai]; 78 128 return js_mkerr_typed( 79 129 js, JS_ERR_REFERENCE, 80 - "Cannot access '%.*s' before initialization", (int)a->len, a->str); 130 + "Cannot access '%.*s' before initialization", 131 + (int)a->len, a->str 132 + ); 81 133 } 82 134 return js_mkerr_typed( 83 135 js, JS_ERR_REFERENCE, 84 - "Cannot access variable before initialization"); 136 + "Cannot access variable before initialization" 137 + ); 85 138 } 86 139 *slot = vm->stack[--vm->sp]; 87 140 return *slot; 88 141 } 89 142 90 - static inline void sv_op_get_arg(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip) { 143 + static inline ant_value_t sv_op_get_arg(sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint8_t *ip) { 91 144 uint16_t idx = sv_get_u16(ip + 1); 92 - vm->stack[vm->sp++] = sv_frame_get_arg_value(frame, idx); 145 + ant_value_t value = sv_frame_get_arg_value(frame, idx); 146 + if (vtype(value) == T_STR && str_is_heap_builder(value)) { 147 + value = str_materialize(js, value); 148 + if (is_err(value)) return value; 149 + } 150 + vm->stack[vm->sp++] = value; 151 + return js_mkundef(); 93 152 } 94 153 95 - static inline void sv_op_put_arg(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip) { 154 + static inline void sv_op_put_arg(sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint8_t *ip) { 96 155 uint16_t idx = sv_get_u16(ip + 1); 97 - sv_frame_set_arg_value(frame, idx, vm->stack[--vm->sp]); 156 + sv_frame_set_arg_value(js, frame, idx, vm->stack[--vm->sp]); 98 157 } 99 158 100 - static inline void sv_op_set_arg(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip) { 159 + static inline void sv_op_set_arg(sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint8_t *ip) { 101 160 uint16_t idx = sv_get_u16(ip + 1); 102 - sv_frame_set_arg_value(frame, idx, vm->stack[vm->sp - 1]); 161 + sv_frame_set_arg_value(js, frame, idx, vm->stack[vm->sp - 1]); 103 162 } 104 163 105 164 static inline void sv_op_rest( ··· 113 172 js_arr_push(js, arr, frame->bp[i]); 114 173 } 115 174 vm->stack[vm->sp++] = arr; 175 + } 176 + 177 + static inline ant_value_t sv_op_str_append_local( 178 + sv_vm_t *vm, ant_t *js, 179 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 180 + ) { 181 + uint16_t idx = sv_get_u16(ip + 1); 182 + ant_value_t rhs = vm->stack[--vm->sp]; 183 + return sv_string_builder_append_slot(vm, js, frame, func, idx, rhs); 184 + } 185 + 186 + static inline ant_value_t sv_op_str_append_local_snapshot( 187 + sv_vm_t *vm, ant_t *js, 188 + sv_frame_t *frame, sv_func_t *func, uint8_t *ip 189 + ) { 190 + uint16_t idx = sv_get_u16(ip + 1); 191 + ant_value_t rhs = vm->stack[--vm->sp]; 192 + ant_value_t lhs = vm->stack[--vm->sp]; 193 + return sv_string_builder_append_snapshot_slot(vm, js, frame, func, idx, lhs, rhs); 194 + } 195 + 196 + static inline ant_value_t sv_op_str_flush_local( 197 + sv_vm_t *vm, ant_t *js, 198 + sv_frame_t *frame, uint8_t *ip 199 + ) { 200 + uint16_t idx = sv_get_u16(ip + 1); 201 + ant_value_t flush = sv_string_builder_flush_slot(vm, js, frame, idx); 202 + if (is_err(flush)) return flush; 203 + return js_mkundef(); 116 204 } 117 205 118 206 #endif
+4 -2
src/silver/ops/objects.h
··· 150 150 } 151 151 152 152 if (vtype(iterable) == T_STR) { 153 - if (str_is_heap_rope(iterable)) { 154 - iterable = rope_flatten(js, iterable); 153 + if (str_is_heap_rope(iterable) || str_is_heap_builder(iterable)) { 154 + iterable = str_materialize(js, iterable); 155 155 if (is_err(iterable)) return iterable; 156 156 } 157 + 157 158 ant_offset_t slen = str_len_fast(js, iterable); 158 159 for (ant_offset_t i = 0; i < slen; ) { 159 160 ant_offset_t off = vstr(js, iterable, NULL); ··· 165 166 js_arr_push(js, arr, js_mkstr(js, (const void *)(uintptr_t)(off + i), cb_len)); 166 167 i += cb_len; 167 168 } 169 + 168 170 return tov(0); 169 171 } 170 172
+33 -24
src/silver/ops/upvalues.h
··· 36 36 uint16_t idx = sv_get_u16(ip + 1); 37 37 sv_upvalue_t *uv = frame->upvalues[idx]; 38 38 ant_value_t val = *uv->location; 39 - if (val == SV_TDZ) 40 - return js_mkerr_typed(js, JS_ERR_REFERENCE, 41 - "Cannot access variable before initialization"); 39 + if (val == SV_TDZ) return js_mkerr_typed(js, 40 + JS_ERR_REFERENCE, 41 + "Cannot access variable before initialization" 42 + ); 43 + if (vtype(val) == T_STR && str_is_heap_builder(val)) { 44 + val = str_materialize(js, val); 45 + if (is_err(val)) return val; 46 + } 42 47 vm->stack[vm->sp++] = val; 43 48 return js_mkundef(); 44 49 } ··· 55 60 *uv->location = vm->stack[vm->sp - 1]; 56 61 } 57 62 58 - static inline void sv_op_close_upval(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip) { 63 + static inline ant_value_t sv_op_close_upval(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip) { 59 64 uint16_t idx = sv_get_u16(ip + 1); 60 65 ant_value_t *slot = sv_frame_slot_ptr(frame, idx); 61 - if (!slot) return; 66 + if (!slot) return js_mkundef(); 62 67 63 68 sv_upvalue_t **pp = &vm->open_upvalues; 64 69 while (*pp) { 65 - sv_upvalue_t *uv = *pp; 66 - ant_value_t *loc = uv->location; 67 - if (sv_slot_in_vm_stack(vm, loc) && loc >= slot) { 68 - uv->closed = *loc; 69 - uv->location = &uv->closed; 70 - *pp = uv->next; 70 + sv_upvalue_t *uv = *pp; 71 + ant_value_t *loc = uv->location; 72 + if (sv_slot_in_vm_stack(vm, loc) && loc >= slot) { 73 + uv->closed = *loc; 74 + uv->location = &uv->closed; 75 + *pp = uv->next; 76 + } 77 + else pp = &uv->next; 71 78 } 72 - else pp = &uv->next; 73 - }} 79 + 80 + return js_mkundef(); 81 + } 74 82 75 83 static inline sv_upvalue_t *sv_capture_upvalue(sv_vm_t *vm, ant_value_t *slot) { 76 84 sv_upvalue_t **pp = &vm->open_upvalues; ··· 84 92 return uv; 85 93 } 86 94 87 - static inline void sv_op_closure( 95 + static inline ant_value_t sv_op_closure( 88 96 sv_vm_t *vm, ant_t *js, sv_frame_t *frame, 89 97 sv_func_t *func, uint8_t *ip 90 98 ) { ··· 99 107 closure->call_flags = child->is_arrow ? SV_CALL_IS_ARROW : 0; 100 108 101 109 if (child->upvalue_count > 0) { 102 - closure->upvalues = calloc((size_t)child->upvalue_count, sizeof(sv_upvalue_t *)); 103 - for (int i = 0; i < child->upvalue_count; i++) { 104 - sv_upval_desc_t *desc = &child->upval_descs[i]; 105 - if (desc->is_local) { 106 - ant_value_t *slot = sv_frame_slot_ptr(frame, desc->index); 107 - if (!slot) slot = frame->bp; 108 - closure->upvalues[i] = sv_capture_upvalue(vm, slot); 109 - } else closure->upvalues[i] = frame->upvalues[desc->index]; 110 - } 111 - } 110 + closure->upvalues = calloc((size_t)child->upvalue_count, sizeof(sv_upvalue_t *)); 111 + for (int i = 0; i < child->upvalue_count; i++) { 112 + sv_upval_desc_t *desc = &child->upval_descs[i]; 113 + if (desc->is_local) { 114 + ant_value_t *slot = sv_frame_slot_ptr(frame, desc->index); 115 + if (!slot) slot = frame->bp; 116 + closure->upvalues[i] = sv_capture_upvalue(vm, slot); 117 + } else closure->upvalues[i] = frame->upvalues[desc->index]; 118 + }} 112 119 113 120 ant_value_t func_val = mkval(T_FUNC, (uintptr_t)closure); 114 121 vm->stack[vm->sp++] = func_val; ··· 138 145 ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); 139 146 if (vtype(func_proto) == T_FUNC) js_set_proto_init(func_obj, func_proto); 140 147 } 148 + 149 + return js_mkundef(); 141 150 } 142 151 143 152 #endif
+260
src/silver/swarm.c
··· 51 51 LOAD_EXT(jit_helper_mul); 52 52 LOAD_EXT(jit_helper_div); 53 53 LOAD_EXT(jit_helper_mod); 54 + LOAD_EXT(jit_helper_str_append_local); 55 + LOAD_EXT(jit_helper_str_append_local_snapshot); 54 56 LOAD_EXT(jit_helper_lt); 55 57 LOAD_EXT(jit_helper_le); 56 58 LOAD_EXT(jit_helper_gt); ··· 1819 1821 case OP_SHL: case OP_SHR: case OP_USHR: 1820 1822 case OP_TYPEOF: 1821 1823 case OP_ADD_LOCAL: 1824 + case OP_STR_APPEND_LOCAL: 1825 + case OP_STR_ALC_SNAPSHOT: 1826 + case OP_STR_FLUSH_LOCAL: 1822 1827 f.needs_bailout = true; 1823 1828 break; 1824 1829 case OP_INC_LOCAL: case OP_DEC_LOCAL: ··· 1869 1874 case OP_GET_LOCAL: case OP_PUT_LOCAL: case OP_SET_LOCAL: 1870 1875 case OP_GET_LOCAL8: case OP_PUT_LOCAL8: case OP_SET_LOCAL8: 1871 1876 case OP_SET_LOCAL_UNDEF: 1877 + case OP_GET_SLOT_RAW: 1872 1878 case OP_GET_UPVAL: case OP_PUT_UPVAL: case OP_SET_UPVAL: 1873 1879 case OP_CLOSE_UPVAL: 1874 1880 case OP_REST: ··· 1903 1909 case OP_IN: case OP_GET_LENGTH: 1904 1910 case OP_DEFINE_FIELD: case OP_SEQ: case OP_EQ: 1905 1911 case OP_INC_LOCAL: case OP_DEC_LOCAL: case OP_ADD_LOCAL: 1912 + case OP_STR_APPEND_LOCAL: 1913 + case OP_STR_ALC_SNAPSHOT: 1906 1914 case OP_TO_PROPKEY: 1907 1915 case OP_RETURN: case OP_RETURN_UNDEF: 1908 1916 case OP_SET_NAME: ··· 2060 2068 MIR_T_I64, "js", 2061 2069 MIR_JSVAL, "v"); 2062 2070 2071 + MIR_type_t sal_ret = MIR_JSVAL; 2072 + MIR_item_t str_append_local_proto = MIR_new_proto(ctx, "sal_proto", 2073 + 1, &sal_ret, 6, 2074 + MIR_T_I64, "vm", 2075 + MIR_T_I64, "js", 2076 + MIR_T_P, "func", 2077 + MIR_T_P, "locals", 2078 + MIR_T_I32, "local_idx", 2079 + MIR_JSVAL, "rhs"); 2080 + 2081 + MIR_type_t sals_ret = MIR_JSVAL; 2082 + MIR_item_t str_append_local_snapshot_proto = MIR_new_proto(ctx, "sals_proto", 2083 + 1, &sals_ret, 7, 2084 + MIR_T_I64, "vm", 2085 + MIR_T_I64, "js", 2086 + MIR_T_P, "func", 2087 + MIR_T_P, "locals", 2088 + MIR_T_I32, "local_idx", 2089 + MIR_JSVAL, "lhs", 2090 + MIR_JSVAL, "rhs"); 2091 + 2063 2092 MIR_type_t truthy_ret = MIR_T_I64; 2064 2093 MIR_item_t truthy_proto = MIR_new_proto(ctx, "truthy_proto", 2065 2094 1, &truthy_ret, 2, ··· 2200 2229 MIR_item_t imp_mul = MIR_new_import(ctx, "jit_helper_mul"); 2201 2230 MIR_item_t imp_div = MIR_new_import(ctx, "jit_helper_div"); 2202 2231 MIR_item_t imp_mod = MIR_new_import(ctx, "jit_helper_mod"); 2232 + MIR_item_t imp_str_append_local = 2233 + MIR_new_import(ctx, "jit_helper_str_append_local"); 2234 + MIR_item_t imp_str_append_local_snapshot = 2235 + MIR_new_import(ctx, "jit_helper_str_append_local_snapshot"); 2203 2236 MIR_item_t imp_lt = MIR_new_import(ctx, "jit_helper_lt"); 2204 2237 MIR_item_t imp_le = MIR_new_import(ctx, "jit_helper_le"); 2205 2238 MIR_item_t imp_gt = MIR_new_import(ctx, "jit_helper_gt"); ··· 2834 2867 break; 2835 2868 } 2836 2869 2870 + case OP_GET_SLOT_RAW: { 2871 + uint16_t slot_idx = sv_get_u16(ip + 1); 2872 + if ((int)slot_idx < param_count) { 2873 + uint16_t idx = slot_idx; 2874 + MIR_reg_t dst = vstack_push(&vs); 2875 + if (has_captured_params && captured_params && idx < (uint16_t)param_count && 2876 + captured_params[idx]) { 2877 + MIR_append_insn(ctx, jit_func, 2878 + MIR_new_insn(ctx, MIR_MOV, 2879 + MIR_new_reg_op(ctx, dst), 2880 + MIR_new_mem_op(ctx, MIR_JSVAL, 2881 + (MIR_disp_t)(idx * (int)sizeof(ant_value_t)), 2882 + r_slotbuf, 0, 1))); 2883 + } else { 2884 + MIR_label_t arg_in_range = MIR_new_label(ctx); 2885 + MIR_label_t arg_done = MIR_new_label(ctx); 2886 + MIR_append_insn(ctx, jit_func, 2887 + MIR_new_insn(ctx, MIR_UBGT, 2888 + MIR_new_label_op(ctx, arg_in_range), 2889 + MIR_new_reg_op(ctx, r_argc), 2890 + MIR_new_int_op(ctx, (int64_t)idx))); 2891 + mir_load_imm(ctx, jit_func, dst, mkval(T_UNDEF, 0)); 2892 + MIR_append_insn(ctx, jit_func, 2893 + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, arg_done))); 2894 + MIR_append_insn(ctx, jit_func, arg_in_range); 2895 + MIR_append_insn(ctx, jit_func, 2896 + MIR_new_insn(ctx, MIR_MOV, 2897 + MIR_new_reg_op(ctx, dst), 2898 + MIR_new_mem_op(ctx, MIR_JSVAL, 2899 + (MIR_disp_t)(idx * (int)sizeof(ant_value_t)), 2900 + r_args, 0, 1))); 2901 + MIR_append_insn(ctx, jit_func, arg_done); 2902 + } 2903 + } else { 2904 + uint16_t idx = (uint16_t)(slot_idx - (uint16_t)param_count); 2905 + if (idx >= (uint16_t)n_locals) { ok = false; break; } 2906 + if (has_captures && captured_locals && captured_locals[idx]) 2907 + MIR_append_insn(ctx, jit_func, 2908 + MIR_new_insn(ctx, MIR_MOV, 2909 + MIR_new_reg_op(ctx, local_regs[idx]), 2910 + MIR_new_mem_op(ctx, MIR_T_I64, 2911 + (MIR_disp_t)((int)idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 2912 + MIR_reg_t dst = vstack_push(&vs); 2913 + if (known_func_locals) vs.known_func[vs.sp - 1] = known_func_locals[idx]; 2914 + MIR_append_insn(ctx, jit_func, 2915 + MIR_new_insn(ctx, MIR_MOV, 2916 + MIR_new_reg_op(ctx, dst), 2917 + MIR_new_reg_op(ctx, local_regs[idx]))); 2918 + if (known_type_locals && known_type_locals[idx] == SV_TI_NUM) { 2919 + MIR_append_insn(ctx, jit_func, 2920 + MIR_new_insn(ctx, MIR_DMOV, 2921 + MIR_new_reg_op(ctx, vs.d_regs[vs.sp - 1]), 2922 + MIR_new_reg_op(ctx, local_d_regs[idx]))); 2923 + if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_NUM; 2924 + } 2925 + } 2926 + break; 2927 + } 2928 + 2837 2929 case OP_PUT_LOCAL: { 2838 2930 uint16_t idx = sv_get_u16(ip + 1); 2839 2931 if (idx >= (uint16_t)n_locals) { ok = false; break; } ··· 5156 5248 break; 5157 5249 } 5158 5250 5251 + case OP_STR_APPEND_LOCAL: { 5252 + uint16_t slot_idx = sv_get_u16(ip + 1); 5253 + int pre_op_sp = vs.sp; 5254 + if ((int)slot_idx < param_count) { 5255 + mir_load_imm(ctx, jit_func, r_bailout_val, (uint64_t)SV_JIT_BAILOUT); 5256 + mir_emit_bailout_check(ctx, jit_func, r_bailout_val, 5257 + 0, r_bailout_off, bc_off, 5258 + r_bailout_sp, pre_op_sp, bailout_tramp, 5259 + r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5260 + break; 5261 + } 5262 + 5263 + uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5264 + if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5265 + 5266 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5267 + MIR_reg_t rhs = vstack_pop(&vs); 5268 + 5269 + MIR_append_insn(ctx, jit_func, 5270 + MIR_new_call_insn(ctx, 9, 5271 + MIR_new_ref_op(ctx, str_append_local_proto), 5272 + MIR_new_ref_op(ctx, imp_str_append_local), 5273 + MIR_new_reg_op(ctx, r_err_tmp), 5274 + MIR_new_reg_op(ctx, r_vm), 5275 + MIR_new_reg_op(ctx, r_js), 5276 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5277 + MIR_new_reg_op(ctx, r_lbuf), 5278 + MIR_new_int_op(ctx, (int64_t)local_idx), 5279 + MIR_new_reg_op(ctx, rhs))); 5280 + 5281 + mir_emit_bailout_check(ctx, jit_func, r_err_tmp, 5282 + 0, r_bailout_off, bc_off, 5283 + r_bailout_sp, pre_op_sp, bailout_tramp, 5284 + r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5285 + 5286 + if (has_captures) { 5287 + for (int i = 0; i < n_locals; i++) 5288 + if (captured_locals[i]) 5289 + MIR_append_insn(ctx, jit_func, 5290 + MIR_new_insn(ctx, MIR_MOV, 5291 + MIR_new_reg_op(ctx, local_regs[i]), 5292 + MIR_new_mem_op(ctx, MIR_T_I64, 5293 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5294 + } 5295 + 5296 + MIR_append_insn(ctx, jit_func, 5297 + MIR_new_insn(ctx, MIR_MOV, 5298 + MIR_new_reg_op(ctx, local_regs[local_idx]), 5299 + MIR_new_mem_op(ctx, MIR_T_I64, 5300 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5301 + if (known_func_locals) known_func_locals[local_idx] = NULL; 5302 + if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5303 + 5304 + MIR_label_t no_err = MIR_new_label(ctx); 5305 + MIR_append_insn(ctx, jit_func, 5306 + MIR_new_insn(ctx, MIR_URSH, 5307 + MIR_new_reg_op(ctx, r_bool), 5308 + MIR_new_reg_op(ctx, r_err_tmp), 5309 + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); 5310 + MIR_append_insn(ctx, jit_func, 5311 + MIR_new_insn(ctx, MIR_BNE, 5312 + MIR_new_label_op(ctx, no_err), 5313 + MIR_new_reg_op(ctx, r_bool), 5314 + MIR_new_uint_op(ctx, JIT_ERR_TAG))); 5315 + if (jit_try_depth > 0) { 5316 + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; 5317 + MIR_append_insn(ctx, jit_func, 5318 + MIR_new_insn(ctx, MIR_MOV, 5319 + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), 5320 + MIR_new_reg_op(ctx, r_err_tmp))); 5321 + MIR_append_insn(ctx, jit_func, 5322 + MIR_new_insn(ctx, MIR_JMP, 5323 + MIR_new_label_op(ctx, h->catch_label))); 5324 + } else { 5325 + MIR_append_insn(ctx, jit_func, 5326 + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 5327 + } 5328 + MIR_append_insn(ctx, jit_func, no_err); 5329 + break; 5330 + } 5331 + 5332 + case OP_STR_ALC_SNAPSHOT: { 5333 + uint16_t slot_idx = sv_get_u16(ip + 1); 5334 + int pre_op_sp = vs.sp; 5335 + if ((int)slot_idx < param_count) { 5336 + mir_load_imm(ctx, jit_func, r_bailout_val, (uint64_t)SV_JIT_BAILOUT); 5337 + mir_emit_bailout_check(ctx, jit_func, r_bailout_val, 5338 + 0, r_bailout_off, bc_off, 5339 + r_bailout_sp, pre_op_sp, bailout_tramp, 5340 + r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5341 + break; 5342 + } 5343 + 5344 + uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5345 + if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5346 + 5347 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5348 + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); 5349 + MIR_reg_t rhs = vstack_pop(&vs); 5350 + MIR_reg_t lhs = vstack_pop(&vs); 5351 + 5352 + MIR_append_insn(ctx, jit_func, 5353 + MIR_new_call_insn(ctx, 10, 5354 + MIR_new_ref_op(ctx, str_append_local_snapshot_proto), 5355 + MIR_new_ref_op(ctx, imp_str_append_local_snapshot), 5356 + MIR_new_reg_op(ctx, r_err_tmp), 5357 + MIR_new_reg_op(ctx, r_vm), 5358 + MIR_new_reg_op(ctx, r_js), 5359 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5360 + MIR_new_reg_op(ctx, r_lbuf), 5361 + MIR_new_int_op(ctx, (int64_t)local_idx), 5362 + MIR_new_reg_op(ctx, lhs), 5363 + MIR_new_reg_op(ctx, rhs))); 5364 + 5365 + mir_emit_bailout_check(ctx, jit_func, r_err_tmp, 5366 + 0, r_bailout_off, bc_off, 5367 + r_bailout_sp, pre_op_sp, bailout_tramp, 5368 + r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5369 + 5370 + if (has_captures) { 5371 + for (int i = 0; i < n_locals; i++) 5372 + if (captured_locals[i]) 5373 + MIR_append_insn(ctx, jit_func, 5374 + MIR_new_insn(ctx, MIR_MOV, 5375 + MIR_new_reg_op(ctx, local_regs[i]), 5376 + MIR_new_mem_op(ctx, MIR_T_I64, 5377 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5378 + } 5379 + 5380 + MIR_append_insn(ctx, jit_func, 5381 + MIR_new_insn(ctx, MIR_MOV, 5382 + MIR_new_reg_op(ctx, local_regs[local_idx]), 5383 + MIR_new_mem_op(ctx, MIR_T_I64, 5384 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5385 + if (known_func_locals) known_func_locals[local_idx] = NULL; 5386 + if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5387 + 5388 + MIR_label_t no_err = MIR_new_label(ctx); 5389 + MIR_append_insn(ctx, jit_func, 5390 + MIR_new_insn(ctx, MIR_URSH, 5391 + MIR_new_reg_op(ctx, r_bool), 5392 + MIR_new_reg_op(ctx, r_err_tmp), 5393 + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); 5394 + MIR_append_insn(ctx, jit_func, 5395 + MIR_new_insn(ctx, MIR_BNE, 5396 + MIR_new_label_op(ctx, no_err), 5397 + MIR_new_reg_op(ctx, r_bool), 5398 + MIR_new_uint_op(ctx, JIT_ERR_TAG))); 5399 + if (jit_try_depth > 0) { 5400 + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; 5401 + MIR_append_insn(ctx, jit_func, 5402 + MIR_new_insn(ctx, MIR_MOV, 5403 + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), 5404 + MIR_new_reg_op(ctx, r_err_tmp))); 5405 + MIR_append_insn(ctx, jit_func, 5406 + MIR_new_insn(ctx, MIR_JMP, 5407 + MIR_new_label_op(ctx, h->catch_label))); 5408 + } else { 5409 + MIR_append_insn(ctx, jit_func, 5410 + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 5411 + } 5412 + MIR_append_insn(ctx, jit_func, no_err); 5413 + break; 5414 + } 5415 + 5159 5416 case OP_TO_PROPKEY: { 5160 5417 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5161 5418 MIR_reg_t src = vstack_pop(&vs); ··· 7001 7258 case OP_LINE_NUM: 7002 7259 case OP_COL_NUM: 7003 7260 case OP_LABEL: 7261 + break; 7262 + case OP_STR_FLUSH_LOCAL: 7263 + ok = false; 7004 7264 break; 7005 7265 7006 7266 case OP_SET_NAME: {
+38
tests/test_http_request_host_port_fetch_shim.mjs
··· 1 + import { request } from 'node:http'; 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + const originalFetch = globalThis.fetch; 8 + const seenUrls = []; 9 + 10 + try { 11 + globalThis.fetch = async url => { 12 + seenUrls.push(String(url)); 13 + return new Response('ok', { status: 200 }); 14 + }; 15 + 16 + await new Promise((resolve, reject) => { 17 + const req = request({ 18 + host: 'example.com:8080', 19 + path: '/resource', 20 + timeout: 500 21 + }, response => { 22 + response.resume(); 23 + response.on('end', resolve); 24 + }); 25 + 26 + req.on('error', reject); 27 + req.on('timeout', () => reject(new Error('request timed out unexpectedly'))); 28 + req.end(); 29 + }); 30 + 31 + assert(seenUrls.length === 1, `expected one fetch call, got ${seenUrls.length}`); 32 + assert(seenUrls[0] === 'http://example.com:8080/resource', 33 + `unexpected request URL: ${seenUrls[0]}`); 34 + 35 + console.log('http.request host port fetch shim test passed'); 36 + } finally { 37 + globalThis.fetch = originalFetch; 38 + }
+77
tests/test_https_get_fetch_shim.mjs
··· 1 + import { get } from 'node:https'; 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + const originalFetch = globalThis.fetch; 8 + let seenUrl = null; 9 + 10 + try { 11 + globalThis.fetch = async (url, init) => { 12 + seenUrl = String(url); 13 + assert(seenUrl === 'https://registry.npmjs.org/serve', `unexpected request URL: ${seenUrl}`); 14 + assert(init.method === 'GET', `expected GET method, got ${init.method}`); 15 + assert(init.headers.accept.includes('application/json'), 16 + `unexpected accept header: ${init.headers.accept}`); 17 + 18 + return new Response('{"ok":true}', { 19 + status: 200, 20 + headers: { 21 + 'content-type': 'application/json' 22 + } 23 + }); 24 + }; 25 + 26 + const body = await new Promise((resolve, reject) => { 27 + const req = get({ 28 + host: 'registry.npmjs.org', 29 + path: '/serve', 30 + timeout: 500, 31 + headers: { 32 + accept: 'application/json' 33 + } 34 + }, response => { 35 + assert(response.statusCode === 200, `expected 200 status, got ${response.statusCode}`); 36 + assert(response.headers['content-type'] === 'application/json', 37 + `unexpected content-type: ${response.headers['content-type']}`); 38 + 39 + response.setEncoding('utf8'); 40 + 41 + let rawData = ''; 42 + response.on('data', chunk => { 43 + rawData += chunk; 44 + }); 45 + response.on('end', () => { 46 + resolve(rawData); 47 + }); 48 + }); 49 + 50 + req.on('error', reject); 51 + req.on('timeout', () => reject(new Error('request timed out unexpectedly'))); 52 + }); 53 + 54 + assert(body === '{"ok":true}', `unexpected body: ${body}`); 55 + 56 + for (const invalidInput of [ 57 + 'http://registry.npmjs.org/serve', 58 + { protocol: 'http:', host: 'registry.npmjs.org', path: '/serve' } 59 + ]) { 60 + let invalidError = null; 61 + try { 62 + get(invalidInput); 63 + } catch (error) { 64 + invalidError = error; 65 + } 66 + 67 + assert(invalidError, `expected invalid protocol error for ${String(invalidInput)}`); 68 + assert(invalidError.name === 'TypeError', 69 + `expected TypeError for invalid protocol, got ${invalidError && invalidError.name}`); 70 + assert(invalidError.code === 'ERR_INVALID_PROTOCOL', 71 + `expected ERR_INVALID_PROTOCOL, got ${invalidError && invalidError.code}`); 72 + } 73 + 74 + console.log('https.get fetch shim test passed'); 75 + } finally { 76 + globalThis.fetch = originalFetch; 77 + }
+19
tests/test_node_url_parse_named_export.mjs
··· 1 + import { parse } from 'node:url'; 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + const tcp = parse('tcp://localhost:4321'); 8 + assert(tcp, 'expected parse() to return a value for tcp:// URLs'); 9 + assert(tcp.protocol === 'tcp:', `expected tcp protocol, got ${tcp && tcp.protocol}`); 10 + assert(tcp.hostname === 'localhost', `expected localhost hostname, got ${tcp && tcp.hostname}`); 11 + assert(tcp.port === '4321', `expected 4321 port, got ${tcp && tcp.port}`); 12 + 13 + const unix = parse('unix:/tmp/ant-test.sock'); 14 + assert(unix, 'expected parse() to return a value for unix: URLs'); 15 + assert(unix.protocol === 'unix:', `expected unix protocol, got ${unix && unix.protocol}`); 16 + assert(unix.pathname === '/tmp/ant-test.sock', 17 + `expected unix pathname, got ${unix && unix.pathname}`); 18 + 19 + console.log('node:url parse named export test passed');
+77
tests/test_string_small_append_loop.cjs
··· 1 + function assert(cond, msg) { 2 + if (!cond) throw new Error(msg); 3 + } 4 + 5 + let parserLike = ""; 6 + const parserLikeIters = 600000; 7 + for (let i = 0; i < parserLikeIters; i++) { 8 + let ch = (i & 1) === 0 ? "a" : "\t"; 9 + if (ch === "\t") ch = " "; 10 + parserLike += ch; 11 + } 12 + 13 + assert(parserLike.length === parserLikeIters, "parser-like append length mismatch"); 14 + assert(parserLike.substring(0, 6) === "a a a ", "parser-like append prefix mismatch"); 15 + assert(parserLike.substring(parserLikeIters - 6) === "a a a ", "parser-like append suffix mismatch"); 16 + 17 + let chunked = ""; 18 + for (let i = 0; i < 200000; i++) chunked += "xyz"; 19 + 20 + assert(chunked.length === 600000, "chunked append length mismatch"); 21 + assert(chunked.substring(0, 9) === "xyzxyzxyz", "chunked append prefix mismatch"); 22 + assert(chunked.substring(chunked.length - 9) === "xyzxyzxyz", "chunked append suffix mismatch"); 23 + assert(chunked.charCodeAt(3) === 120, "chunked append charCodeAt mismatch"); 24 + 25 + for (const count of [1000, 10000, 100000]) { 26 + let s = ""; 27 + for (let i = 0; i < count; i++) s += "a"; 28 + assert(s.length === count, `single-char append mismatch @ ${count}`); 29 + assert(s.charCodeAt(0) === 97, `charCodeAt mismatch @ ${count}`); 30 + assert(s.substring(count - 4) === "aaaa", `substring mismatch @ ${count}`); 31 + } 32 + 33 + function takesString(s) { 34 + return s.length; 35 + } 36 + 37 + let escaped = ""; 38 + for (let i = 0; i < 5000; i++) escaped = escaped + "xyz"; 39 + assert(takesString(escaped) === 15000, "call escape length mismatch"); 40 + assert(escaped.substring(0, 6) === "xyzxyz", "call escape prefix mismatch"); 41 + 42 + let assigned = ""; 43 + for (let i = 0; i < 4096; i++) assigned += (i & 1) ? "b" : "a"; 44 + const box = { value: assigned }; 45 + assert(box.value.length === 4096, "object assignment length mismatch"); 46 + assert(box.value.substring(0, 4) === "abab", "object assignment prefix mismatch"); 47 + 48 + const arr = []; 49 + arr.push(assigned); 50 + assert(arr[0].length === 4096, "array assignment length mismatch"); 51 + assert(arr[0].substring(0, 4) === "abab", "array assignment prefix mismatch"); 52 + 53 + let branchy = ""; 54 + for (let i = 0; i < 20000; i++) { 55 + if ((i & 3) === 0) branchy += "x"; 56 + else branchy = branchy + "yz"; 57 + } 58 + assert(branchy.length === 35000, "branchy append length mismatch"); 59 + assert(branchy.substring(0, 5) === "xyzyz", "branchy append prefix mismatch"); 60 + assert(branchy.charCodeAt(0) === 120, "branchy append charCodeAt mismatch"); 61 + 62 + let mixed = ""; 63 + for (let i = 0; i < 30000; i++) mixed += (i & 1) === 0 ? "m" : "tiny"; 64 + assert(mixed.length === 75000, "mixed append length mismatch"); 65 + assert(mixed.substring(0, 9) === "mtinymtin", "mixed append prefix mismatch"); 66 + assert(mixed.charCodeAt(1) === 116, "mixed append charCodeAt mismatch"); 67 + 68 + let numeric = 0; 69 + for (let i = 0; i < 10000; i++) numeric += 3; 70 + assert(numeric === 30000, "numeric += changed semantics"); 71 + 72 + const obj = { value: "" }; 73 + for (let i = 0; i < 2048; i++) obj.value += "q"; 74 + assert(obj.value.length === 2048, "non-local append length mismatch"); 75 + assert(obj.value.substring(0, 4) === "qqqq", "non-local append prefix mismatch"); 76 + 77 + console.log("small append loop test passed");