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.

add iteration fast path

+306 -87
+6
include/ant.h
··· 17 17 #define STR_PROTO "__proto__" 18 18 #define STR_PROTO_LEN 9 19 19 20 + #define ANT_ASSERT(cond, msg) ({ \ 21 + if (!(cond)) { \ 22 + fprintf(stderr, "ANT FATAL: %s\n %s:%d\n", msg, __FILE__, __LINE__); \ 23 + abort(); \ 24 + }}) 25 + 20 26 #define ANT_STRING(s) js_mkstr(js, s, sizeof(s) - 1) 21 27 #define ANT_PTR(ptr) js_mknum((double)(uintptr_t)(ptr)) 22 28 #define ANT_COPY(buf, len, s) cpy(buf, len, s, sizeof(s) - 1)
+17
include/internal.h
··· 159 159 ant_value_t new_target; 160 160 ant_value_t current_func; 161 161 ant_value_t length_str; 162 + 163 + struct { 164 + const char *length; 165 + const char *buffer; 166 + const char *prototype; 167 + const char *constructor; 168 + const char *name; 169 + const char *message; 170 + const char *done; 171 + const char *value; 172 + const char *get; 173 + const char *set; 174 + const char *arguments; 175 + const char *callee; 176 + const char *idx[10]; 177 + } intern; 178 + 162 179 ant_value_t thrown_value; 163 180 ant_value_t thrown_stack; 164 181
+69 -81
src/ant.c
··· 79 79 #error "NaN-boxing requires IEEE 754 binary64 doubles" 80 80 #endif 81 81 82 - static const char *INTERN_LENGTH = NULL; 83 - static const char *INTERN_BUFFER = NULL; 84 - static const char *INTERN_PROTOTYPE = NULL; 85 - static const char *INTERN_CONSTRUCTOR = NULL; 86 - static const char *INTERN_NAME = NULL; 87 - static const char *INTERN_MESSAGE = NULL; 88 - static const char *INTERN_VALUE = NULL; 89 - static const char *INTERN_GET = NULL; 90 - static const char *INTERN_SET = NULL; 91 - 92 - static const char *INTERN_ARGUMENTS = NULL; 93 - static const char *INTERN_CALLEE = NULL; 94 - static const char *INTERN_IDX[10] = {NULL}; 95 - 96 82 typedef struct interned_string { 97 83 uint64_t hash; 98 84 char *str; ··· 490 476 } 491 477 492 478 ant_value_t js_obj_to_func_ex(ant_value_t obj, uint8_t flags) { 493 - sv_closure_t *closure = js_closure_alloc(rt->js); 479 + ant_t *js = rt->js; 480 + 481 + sv_closure_t *closure = js_closure_alloc(js); 494 482 if (!closure) return mkval(T_ERR, 0); 483 + 495 484 closure->func_obj = (vtype(obj) == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 496 485 closure->bound_this = js_mkundef(); 497 486 closure->bound_args = js_mkundef(); 498 487 closure->super_val = js_mkundef(); 499 488 closure->call_flags = flags; 489 + 500 490 ant_object_t *func_obj = js_obj_ptr(closure->func_obj); 501 491 if (func_obj) { 502 - if (flags & SV_CALL_IS_DEFAULT_CTOR) { 503 - func_obj->is_constructor = 1; 504 - } else if ( 492 + if (flags & SV_CALL_IS_DEFAULT_CTOR) func_obj->is_constructor = 1; 493 + // mark native function objects as constructors when they are 494 + // created with an explicit .prototype own property. 495 + else if ( 505 496 !func_obj->is_constructor && 506 497 func_obj->shape && 507 - INTERN_PROTOTYPE && 498 + js->intern.prototype && 508 499 vtype(obj_extra_get(func_obj, SLOT_CFUNC)) == T_CFUNC 509 - ) { 510 - // mark native function objects as constructors when they are 511 - // created with an explicit .prototype own property. 512 - if (ant_shape_lookup_interned(func_obj->shape, INTERN_PROTOTYPE) >= 0) 513 - func_obj->is_constructor = 1; 514 - } 500 + ) if (ant_shape_lookup_interned(func_obj->shape, js->intern.prototype) >= 0 501 + ) func_obj->is_constructor = 1; 515 502 } 503 + 516 504 return mkval(T_FUNC, (uintptr_t)closure); 517 505 } 518 506 ··· 938 926 ant_object_t *arr_ptr = array_obj_ptr(arr); 939 927 if (arr_ptr) return (ant_offset_t)arr_ptr->u.array.len; 940 928 941 - ant_value_t val = lkp_interned_val(js, arr, INTERN_LENGTH); 929 + ant_value_t val = lkp_interned_val(js, arr, js->intern.length); 942 930 if (vtype(val) == T_NUM) return (ant_offset_t) tod(val); 943 931 944 932 return 0; ··· 949 937 if (vtype(ctor) == T_FUNC) return ctor; 950 938 ant_value_t proto = get_slot(obj, SLOT_PROTO); 951 939 if (vtype(proto) != T_OBJ) return js_mkundef(); 952 - return lkp_interned_val(js, proto, INTERN_CONSTRUCTOR); 940 + return lkp_interned_val(js, proto, js->intern.constructor); 953 941 } 954 942 955 943 static const char *get_func_name(ant_t *js, ant_value_t func, ant_offset_t *out_len) { ··· 2424 2412 return (attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0; 2425 2413 } 2426 2414 2427 - static void intern_init(void) { 2428 - if (INTERN_LENGTH) return; 2429 - INTERN_LENGTH = intern_string("length", 6); 2430 - INTERN_BUFFER = intern_string("buffer", 6); 2431 - INTERN_PROTOTYPE = intern_string("prototype", 9); 2432 - INTERN_CONSTRUCTOR = intern_string("constructor", 11); 2433 - INTERN_NAME = intern_string("name", 4); 2434 - INTERN_MESSAGE = intern_string("message", 7); 2435 - INTERN_VALUE = intern_string("value", 5); 2436 - INTERN_GET = intern_string("get", 3); 2437 - INTERN_SET = intern_string("set", 3); 2438 - INTERN_ARGUMENTS = intern_string("arguments", 9); 2439 - INTERN_CALLEE = intern_string("callee", 6); 2440 - INTERN_IDX[0] = intern_string("0", 1); 2441 - INTERN_IDX[1] = intern_string("1", 1); 2442 - INTERN_IDX[2] = intern_string("2", 1); 2443 - INTERN_IDX[3] = intern_string("3", 1); 2444 - INTERN_IDX[4] = intern_string("4", 1); 2445 - INTERN_IDX[5] = intern_string("5", 1); 2446 - INTERN_IDX[6] = intern_string("6", 1); 2447 - INTERN_IDX[7] = intern_string("7", 1); 2448 - INTERN_IDX[8] = intern_string("8", 1); 2449 - INTERN_IDX[9] = intern_string("9", 1); 2415 + static void js_init_intern_cache(ant_t *js) { 2416 + js->intern.length = intern_string("length", 6); 2417 + js->intern.buffer = intern_string("buffer", 6); 2418 + js->intern.prototype = intern_string("prototype", 9); 2419 + js->intern.constructor = intern_string("constructor", 11); 2420 + js->intern.name = intern_string("name", 4); 2421 + js->intern.message = intern_string("message", 7); 2422 + js->intern.done = intern_string("done", 4); 2423 + js->intern.value = intern_string("value", 5); 2424 + js->intern.get = intern_string("get", 3); 2425 + js->intern.set = intern_string("set", 3); 2426 + js->intern.arguments = intern_string("arguments", 9); 2427 + js->intern.callee = intern_string("callee", 6); 2428 + js->intern.idx[0] = intern_string("0", 1); 2429 + js->intern.idx[1] = intern_string("1", 1); 2430 + js->intern.idx[2] = intern_string("2", 1); 2431 + js->intern.idx[3] = intern_string("3", 1); 2432 + js->intern.idx[4] = intern_string("4", 1); 2433 + js->intern.idx[5] = intern_string("5", 1); 2434 + js->intern.idx[6] = intern_string("6", 1); 2435 + js->intern.idx[7] = intern_string("7", 1); 2436 + js->intern.idx[8] = intern_string("8", 1); 2437 + js->intern.idx[9] = intern_string("9", 1); 2450 2438 } 2451 2439 2452 2440 ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs) { ··· 2827 2815 2828 2816 if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 2829 2817 ant_value_t nt_obj = js_as_obj(js->new_target); 2830 - ant_value_t nt_proto = lkp_interned_val(js, nt_obj, INTERN_PROTOTYPE); 2818 + ant_value_t nt_proto = lkp_interned_val(js, nt_obj, js->intern.prototype); 2831 2819 if (is_object_type(nt_proto)) instance_proto = nt_proto; 2832 2820 } 2833 2821 ··· 2996 2984 } 2997 2985 2998 2986 ant_value_t new_len_val = tov((double)new_len); 2999 - ant_offset_t len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); 3000 - if (len_off != 0) { 3001 - js_saveval(js, len_off, new_len_val); 3002 - } else { 3003 - js_mkprop_fast(js, obj, "length", 6, new_len_val); 3004 - } 2987 + ant_offset_t len_off = lkp_interned(js, obj, js->intern.length, 6); 2988 + 2989 + if (len_off != 0) js_saveval(js, len_off, new_len_val); 2990 + else js_mkprop_fast(js, obj, "length", 6, new_len_val); 3005 2991 } 3006 2992 3007 2993 static ant_value_t js_setprop_array_fast(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t klen, const char *key) { ··· 3843 3829 ant_value_t ctor = lkp_interned_val(js, js->global, interned); 3844 3830 if (vtype(ctor) != T_FUNC) return js_mknull(); 3845 3831 ant_value_t ctor_obj = js_as_obj(ctor); 3846 - ant_value_t proto = lkp_interned_val(js, ctor_obj, INTERN_PROTOTYPE); 3832 + ant_value_t proto = lkp_interned_val(js, ctor_obj, js->intern.prototype); 3847 3833 return vtype(proto) == T_UNDEF ? js_mknull() : proto; 3848 3834 } 3849 3835 ··· 4905 4891 } 4906 4892 4907 4893 ant_value_t this_arg = (nargs > 0) ? args[0] : js_mkundef(); 4908 - 4909 4894 int bound_argc = (nargs > 1) ? nargs - 1 : 0; 4910 4895 ant_value_t *bound_args = (bound_argc > 0) ? &args[1] : NULL; 4911 4896 4912 4897 int orig_length = 0; 4913 4898 ant_value_t target_func_obj; 4914 - if (vtype(func) == T_CFUNC) { 4915 - orig_length = 0; 4916 - } else { 4899 + 4900 + if (vtype(func) == T_CFUNC) orig_length = 0; 4901 + else { 4917 4902 target_func_obj = js_func_obj(func); 4918 - ant_value_t len_val = lkp_interned_val(js, target_func_obj, INTERN_LENGTH); 4919 - if (vtype(len_val) == T_NUM) { 4920 - orig_length = (int) tod(len_val); 4921 - } 4903 + ant_value_t len_val = lkp_interned_val(js, target_func_obj, js->intern.length); 4904 + if (vtype(len_val) == T_NUM) orig_length = (int) tod(len_val); 4922 4905 } 4923 4906 4924 4907 int bound_length = orig_length - bound_argc; ··· 5105 5088 5106 5089 if (!is_new) { 5107 5090 this_val = js_mkobj(js); 5108 - ant_value_t proto = lkp_interned_val(js, js_func_obj(js->current_func), INTERN_PROTOTYPE); 5091 + ant_value_t proto = lkp_interned_val(js, js_func_obj(js->current_func), js->intern.prototype); 5109 5092 if (vtype(proto) != T_UNDEF) js_set_proto_init(this_val, proto); 5110 5093 else js_set_proto_init(this_val, get_ctor_proto(js, "Error", 5)); 5111 5094 } ··· 5179 5162 5180 5163 if (!is_new) { 5181 5164 this_val = js_mkobj(js); 5182 - ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), INTERN_PROTOTYPE, 9); 5165 + ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), js->intern.prototype, 9); 5183 5166 if (proto_off) js_set_proto_init(this_val, propref_load(js, proto_off)); 5184 5167 else js_set_proto_init(this_val, get_ctor_proto(js, "AggregateError", 14)); 5185 5168 } ··· 5827 5810 value = propref_load(js, value_off); 5828 5811 } 5829 5812 5830 - ant_offset_t get_off = lkp_interned(js, descriptor, INTERN_GET, 3); 5813 + ant_offset_t get_off = lkp_interned(js, descriptor, js->intern.get, 3); 5831 5814 if (get_off != 0) { 5832 5815 has_get = true; 5833 5816 ant_value_t getter = propref_load(js, get_off); ··· 5836 5819 } 5837 5820 } 5838 5821 5839 - ant_offset_t set_off = lkp_interned(js, descriptor, INTERN_SET, 3); 5822 + ant_offset_t set_off = lkp_interned(js, descriptor, js->intern.set, 3); 5840 5823 if (set_off != 0) { 5841 5824 has_set = true; 5842 5825 ant_value_t setter = propref_load(js, set_off); ··· 6725 6708 } 6726 6709 6727 6710 if (is_proxy(arr)) { 6728 - ant_offset_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 6711 + ant_offset_t off = lkp_interned(js, arr, js->intern.length, 6); 6729 6712 ant_offset_t len = 0; 6730 6713 if (off != 0) { 6731 6714 ant_value_t len_val = propref_load(js, off); ··· 11508 11491 } 11509 11492 } 11510 11493 11511 - ant_offset_t proto_off = lkp_interned(js, func_obj, INTERN_PROTOTYPE, 9); 11494 + ant_offset_t proto_off = lkp_interned(js, func_obj, js->intern.prototype, 9); 11512 11495 if (proto_off == 0) return mkval(T_BOOL, 0); 11513 11496 11514 11497 ant_value_t ctor_proto = propref_load(js, proto_off); ··· 11953 11936 static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj) { 11954 11937 ant_value_t src = is_proxy(obj) ? proxy_read_target(js, obj) : obj; 11955 11938 if (vtype(src) == T_ARR) return get_array_length(js, src); 11956 - ant_offset_t off = lkp_interned(js, src, INTERN_LENGTH, 6); 11939 + ant_offset_t off = lkp_interned(js, src, js->intern.length, 6); 11957 11940 if (off == 0) return 0; 11958 11941 ant_value_t len_val = propref_load(js, off); 11959 11942 return vtype(len_val) == T_NUM ? (ant_offset_t)tod(len_val) : 0; ··· 11998 11981 ant_value_t handler = data->handler; 11999 11982 12000 11983 ant_offset_t get_trap_off = vtype(handler) == T_OBJ 12001 - ? lkp_interned(js, handler, INTERN_GET, 3) 11984 + ? lkp_interned(js, handler, js->intern.get, 3) 12002 11985 : 0; 12003 11986 12004 11987 if (get_trap_off != 0) { ··· 12056 12039 ant_value_t target = data->target; 12057 12040 ant_value_t handler = data->handler; 12058 12041 12059 - ant_offset_t set_trap_off = vtype(handler) == T_OBJ ? lkp_interned(js, handler, INTERN_SET, 3) : 0; 12042 + ant_offset_t set_trap_off = vtype(handler) == T_OBJ ? lkp_interned(js, handler, js->intern.set, 3) : 0; 12060 12043 if (set_trap_off != 0) { 12061 12044 ant_value_t set_trap = propref_load(js, set_trap_off); 12062 12045 if (vtype(set_trap) == T_FUNC || vtype(set_trap) == T_CFUNC) { ··· 12179 12162 ant_value_t handler = data->handler; 12180 12163 12181 12164 ant_offset_t get_trap_off = vtype(handler) == T_OBJ 12182 - ? lkp_interned(js, handler, INTERN_GET, 3) : 0; 12165 + ? lkp_interned(js, handler, js->intern.get, 3) : 0; 12183 12166 if (get_trap_off != 0) { 12184 12167 ant_value_t get_trap = propref_load(js, get_trap_off); 12185 12168 if (vtype(get_trap) == T_FUNC || vtype(get_trap) == T_CFUNC) { ··· 12427 12410 } 12428 12411 12429 12412 ant_t *js_create(void *buf, size_t len) { 12430 - assert( 12431 - (uintptr_t)buf <= ((1ULL << 53) - 1) && 12432 - "ANT_PTR: pointer exceeds 53-bit double-precision integer limit" 12413 + ANT_ASSERT( 12414 + (uintptr_t)buf <= ((1ULL << 53) - 1), 12415 + "pointer exceeds 53-bit double-precision integer limit" 12433 12416 ); 12434 12417 12435 - intern_init(); 12436 12418 ant_t *js = NULL; 12437 12419 12438 12420 if (len < sizeof(*js)) return js; 12439 12421 memset(buf, 0, len); 12440 12422 12441 - js = (ant_t *) buf; 12423 + js = (ant_t *)buf; 12442 12424 rt->js = js; 12425 + js_init_intern_cache(js); 12426 + 12443 12427 if (!fixed_arena_init(&js->obj_arena, sizeof(ant_object_t), offsetof(ant_object_t, mark_epoch), ANT_ARENA_MAX)) return NULL; 12444 12428 if (!fixed_arena_init(&js->closure_arena, sizeof(sv_closure_t), offsetof(sv_closure_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) { 12445 12429 fixed_arena_destroy(&js->obj_arena); 12446 12430 return NULL; 12447 12431 } 12432 + 12448 12433 if (!fixed_arena_init(&js->upvalue_arena, sizeof(sv_upvalue_t), offsetof(sv_upvalue_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) { 12449 12434 fixed_arena_destroy(&js->closure_arena); 12450 12435 fixed_arena_destroy(&js->obj_arena); 12451 12436 return NULL; 12452 12437 } 12438 + 12453 12439 js->c_root_cap = 64; 12454 12440 js->c_roots = calloc(js->c_root_cap, sizeof(*js->c_roots)); 12441 + 12455 12442 if (!js->c_roots) { 12456 12443 fixed_arena_destroy(&js->upvalue_arena); 12457 12444 fixed_arena_destroy(&js->closure_arena); 12458 12445 fixed_arena_destroy(&js->obj_arena); 12459 12446 return NULL; 12460 12447 } 12448 + 12461 12449 js->global = mkobj(js, 0); 12462 12450 js->this_val = js->global; 12463 12451 js->new_target = js_mkundef();
+39 -6
src/silver/ops/iteration.h
··· 4 4 #include "ant.h" 5 5 #include "async.h" 6 6 #include "utf8.h" 7 + #include "property.h" 7 8 #include "silver/engine.h" 8 9 #include "modules/symbol.h" 9 10 #include "modules/collections.h" ··· 134 135 return tov(0); 135 136 } 136 137 138 + static inline ant_value_t sv_iter_result_get_named( 139 + ant_t *js, 140 + ant_value_t result, 141 + const char *interned, 142 + const char *key, 143 + ant_offset_t key_len 144 + ) { 145 + ant_object_t *ptr = is_object_type(result) 146 + ? js_obj_ptr(js_as_obj(result)) 147 + : NULL; 148 + 149 + ant_value_t out = js_mkundef(); 150 + bool should_fallback = false; 151 + 152 + if (interned && sv_try_get_shape_data_prop(js, ptr, interned, &out, &should_fallback)) return out; 153 + return sv_getprop_fallback_len(js, result, key, key_len); 154 + } 155 + 156 + static inline void sv_iter_result_unpack( 157 + ant_t *js, ant_value_t result, 158 + ant_value_t *out_done, ant_value_t *out_value 159 + ) { 160 + *out_done = sv_iter_result_get_named(js, result, js->intern.done, "done", 4); 161 + *out_value = sv_iter_result_get_named(js, result, js->intern.value, "value", 5); 162 + } 163 + 137 164 static inline ant_value_t sv_iter_advance( 138 165 sv_vm_t *vm, ant_t *js, int hint, ant_value_t *out_value, bool *out_done 139 166 ) { ··· 243 270 if (is_err(result)) return result; 244 271 if (!is_object_type(result)) 245 272 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator result is not an object"); 246 - ant_value_t done = js_getprop_fallback(js, result, "done"); 247 - *out_value = js_getprop_fallback(js, result, "value"); 273 + ant_value_t done = js_mkundef(); 274 + sv_iter_result_unpack(js, result, &done, out_value); 275 + if (is_err(done)) return done; 276 + if (is_err(*out_value)) return *out_value; 248 277 *out_done = js_truthy(js, done); 249 278 return tov(0); 250 279 }} ··· 265 294 266 295 static inline void sv_op_iter_get_value(sv_vm_t *vm, ant_t *js) { 267 296 ant_value_t obj = vm->stack[--vm->sp]; 268 - ant_value_t done = js_getprop_fallback(js, obj, "done"); 269 - ant_value_t value = js_getprop_fallback(js, obj, "value"); 297 + ant_value_t done = js_mkundef(); 298 + ant_value_t value = js_mkundef(); 299 + sv_iter_result_unpack(js, obj, &done, &value); 270 300 vm->stack[vm->sp++] = value; 271 301 vm->stack[vm->sp++] = mkval(T_BOOL, js_truthy(js, done)); 272 302 } ··· 341 371 if (is_err(awaited)) return awaited; 342 372 result = awaited; 343 373 } 344 - ant_value_t done = js_getprop_fallback(js, result, "done"); 345 - ant_value_t value = js_getprop_fallback(js, result, "value"); 374 + ant_value_t done = js_mkundef(); 375 + ant_value_t value = js_mkundef(); 376 + sv_iter_result_unpack(js, result, &done, &value); 377 + if (is_err(done)) return done; 378 + if (is_err(value)) return value; 346 379 if (vtype(value) == T_PROMISE) { 347 380 ant_value_t awaited_val = sv_await_value(js, value); 348 381 if (is_err(awaited_val)) return awaited_val;
+175
tests/test_iterator_result_fast_path.cjs
··· 1 + function assert(cond, msg) { 2 + if (!cond) throw new Error(msg); 3 + } 4 + 5 + function assertEq(actual, expected, msg) { 6 + if (actual !== expected) { 7 + throw new Error(msg + " (expected " + expected + ", got " + actual + ")"); 8 + } 9 + } 10 + 11 + function makeOwnDataIterable(limit) { 12 + return { 13 + [Symbol.iterator]() { 14 + let i = 0; 15 + const result = { done: false, value: 0 }; 16 + return { 17 + next() { 18 + if (i < limit) { 19 + result.done = false; 20 + result.value = i++; 21 + } else { 22 + result.done = true; 23 + result.value = undefined; 24 + } 25 + return result; 26 + } 27 + }; 28 + } 29 + }; 30 + } 31 + 32 + function makeInheritedIterable(limit) { 33 + return { 34 + [Symbol.iterator]() { 35 + let i = 0; 36 + const proto = { done: false, value: 0 }; 37 + const result = Object.create(proto); 38 + return { 39 + next() { 40 + if (i < limit) { 41 + proto.done = false; 42 + proto.value = i++; 43 + } else { 44 + proto.done = true; 45 + proto.value = undefined; 46 + } 47 + return result; 48 + } 49 + }; 50 + } 51 + }; 52 + } 53 + 54 + function makeAccessorIterable(limit, counts) { 55 + return { 56 + [Symbol.iterator]() { 57 + let i = 0; 58 + let done = false; 59 + let value = undefined; 60 + const result = {}; 61 + 62 + Object.defineProperty(result, "done", { 63 + get() { 64 + counts.done++; 65 + return done; 66 + }, 67 + enumerable: true, 68 + configurable: true, 69 + }); 70 + 71 + Object.defineProperty(result, "value", { 72 + get() { 73 + counts.value++; 74 + return value; 75 + }, 76 + enumerable: true, 77 + configurable: true, 78 + }); 79 + 80 + return { 81 + next() { 82 + if (i < limit) { 83 + done = false; 84 + value = i++; 85 + } else { 86 + done = true; 87 + value = undefined; 88 + } 89 + return result; 90 + } 91 + }; 92 + } 93 + }; 94 + } 95 + 96 + async function collectAsync(iterable) { 97 + let sum = 0; 98 + for await (const v of iterable) sum += v; 99 + return sum; 100 + } 101 + 102 + function makeAsyncOwnDataIterable(limit) { 103 + return { 104 + [Symbol.asyncIterator]() { 105 + let i = 0; 106 + const result = { done: false, value: 0 }; 107 + return { 108 + async next() { 109 + if (i < limit) { 110 + result.done = false; 111 + result.value = i++; 112 + } else { 113 + result.done = true; 114 + result.value = undefined; 115 + } 116 + return result; 117 + } 118 + }; 119 + } 120 + }; 121 + } 122 + 123 + async function main() { 124 + console.log("iterator result fast-path regression"); 125 + 126 + console.log("\nTest 1: own data properties stay fast-path compatible"); 127 + { 128 + let sum = 0; 129 + for (const v of makeOwnDataIterable(8)) sum += v; 130 + assertEq(sum, 28, "own data result object should iterate correctly"); 131 + } 132 + console.log("PASS"); 133 + 134 + console.log("\nTest 2: inherited done/value still fall back correctly"); 135 + { 136 + let sum = 0; 137 + for (const v of makeInheritedIterable(8)) sum += v; 138 + assertEq(sum, 28, "inherited iterator result properties should still work"); 139 + } 140 + console.log("PASS"); 141 + 142 + console.log("\nTest 3: accessor done/value still invoke getters"); 143 + { 144 + const counts = { done: 0, value: 0 }; 145 + let sum = 0; 146 + for (const v of makeAccessorIterable(5, counts)) sum += v; 147 + assertEq(sum, 10, "accessor-backed iterator result should still iterate correctly"); 148 + assert(counts.done >= 6, "done getter should be observed for each step"); 149 + assert(counts.value >= 5, "value getter should be observed for yielded steps"); 150 + } 151 + console.log("PASS"); 152 + 153 + console.log("\nTest 4: array destructuring still consumes reusable results"); 154 + { 155 + const [a, b, c] = makeOwnDataIterable(3); 156 + assertEq(a, 0, "first destructured value should match"); 157 + assertEq(b, 1, "second destructured value should match"); 158 + assertEq(c, 2, "third destructured value should match"); 159 + } 160 + console.log("PASS"); 161 + 162 + console.log("\nTest 5: async iterator reusable result still works"); 163 + { 164 + const sum = await collectAsync(makeAsyncOwnDataIterable(6)); 165 + assertEq(sum, 15, "async iterator result object should still work"); 166 + } 167 + console.log("PASS"); 168 + 169 + console.log("\nAll iterator result fast-path tests passed"); 170 + } 171 + 172 + main().catch((err) => { 173 + console.error(err && err.stack ? err.stack : String(err)); 174 + throw err; 175 + });