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.

rewrite iterator system

+764 -199
+13 -13
examples/results.txt
··· 464 464 compat-table/es6/Map.constructor-invokes-set.js: failed 465 465 compat-table/es6/Map.constructor-requires-new.js: OK 466 466 compat-table/es6/Map.iterator-closing.js: failed 467 - compat-table/es6/Map.iterator-prototype-chain.js: TypeError: Cannot read properties of null (reading 'hasOwnProperty') 467 + compat-table/es6/Map.iterator-prototype-chain.js: OK 468 468 compat-table/es6/Map.js: OK 469 469 compat-table/es6/Map.prototype-not-instance.js: failed 470 470 compat-table/es6/Map.prototype.Symbol.iterator.js: OK ··· 581 581 compat-table/es6/Set.constructor-invokes-add.js: failed 582 582 compat-table/es6/Set.constructor-requires-new.js: OK 583 583 compat-table/es6/Set.iterator-closing.js: failed 584 - compat-table/es6/Set.iterator-prototype-chain.js: TypeError: Cannot read properties of null (reading 'hasOwnProperty') 584 + compat-table/es6/Set.iterator-prototype-chain.js: OK 585 585 compat-table/es6/Set.js: OK 586 586 compat-table/es6/Set.prototype-not-instance.js: failed 587 587 compat-table/es6/Set.prototype.Symbol.iterator.js: OK ··· 1065 1065 compat-table/es6/typed-arrays.of.js: failed 1066 1066 compat-table/es6/typed-arrays.prototype.Symbol.iterator.js: OK 1067 1067 compat-table/es6/typed-arrays.prototype.copyWithin.js: OK 1068 - compat-table/es6/typed-arrays.prototype.entries.js: failed 1068 + compat-table/es6/typed-arrays.prototype.entries.js: OK 1069 1069 compat-table/es6/typed-arrays.prototype.every.js: failed 1070 1070 compat-table/es6/typed-arrays.prototype.fill.js: OK 1071 1071 compat-table/es6/typed-arrays.prototype.filter.js: failed ··· 1074 1074 compat-table/es6/typed-arrays.prototype.forEach.js: failed 1075 1075 compat-table/es6/typed-arrays.prototype.indexOf.js: failed 1076 1076 compat-table/es6/typed-arrays.prototype.join.js: OK 1077 - compat-table/es6/typed-arrays.prototype.keys.js: failed 1077 + compat-table/es6/typed-arrays.prototype.keys.js: OK 1078 1078 compat-table/es6/typed-arrays.prototype.lastIndexOf.js: failed 1079 1079 compat-table/es6/typed-arrays.prototype.map.js: failed 1080 1080 compat-table/es6/typed-arrays.prototype.reduceRight.js: failed ··· 1337 1337 compat-table/es2024/regex.flags.v.unicode-15.1.js: OK 1338 1338 compat-table/es2024/regex.flags.v.unicode-16.0.js: OK 1339 1339 compat-table/es2024/regex.flags.v.unicode-17.0.js: OK 1340 - compat-table/es2025/Iterator.extends.js: ReferenceError: 'Iterator' is not defined 1341 - compat-table/es2025/Iterator.from.iterable.js: ReferenceError: 'Iterator' is not defined 1342 - compat-table/es2025/Iterator.from.iterator.js: ReferenceError: 'Iterator' is not defined 1343 - compat-table/es2025/Iterator.instanceof.js: ReferenceError: 'Iterator' is not defined 1344 - compat-table/es2025/Iterator.prototype.Symbol.toStringTag.js: ReferenceError: 'Iterator' is not defined 1345 - compat-table/es2025/Iterator.prototype.drop.js: TypeError: undefined is not a function 1340 + compat-table/es2025/Iterator.extends.js: OK 1341 + compat-table/es2025/Iterator.from.iterable.js: OK 1342 + compat-table/es2025/Iterator.from.iterator.js: OK 1343 + compat-table/es2025/Iterator.instanceof.js: OK 1344 + compat-table/es2025/Iterator.prototype.Symbol.toStringTag.js: OK 1345 + compat-table/es2025/Iterator.prototype.drop.js: OK 1346 1346 compat-table/es2025/Iterator.prototype.every.js: OK 1347 1347 compat-table/es2025/Iterator.prototype.filter.js: OK 1348 1348 compat-table/es2025/Iterator.prototype.find.js: OK ··· 1351 1351 compat-table/es2025/Iterator.prototype.map.js: OK 1352 1352 compat-table/es2025/Iterator.prototype.reduce.js: OK 1353 1353 compat-table/es2025/Iterator.prototype.some.js: OK 1354 - compat-table/es2025/Iterator.prototype.take.js: TypeError: undefined is not a function 1355 - compat-table/es2025/Iterator.prototype.toArray.js: TypeError: undefined is not a function 1354 + compat-table/es2025/Iterator.prototype.take.js: OK 1355 + compat-table/es2025/Iterator.prototype.toArray.js: OK 1356 1356 compat-table/es2025/Promise.try.js: OK 1357 1357 compat-table/es2025/RegExp.escape.js: OK 1358 1358 compat-table/es2025/Set.prototype.difference.js: TypeError: undefined is not a function ··· 1365 1365 compat-table/es2025/regex.duplicate-named-groups.js: OK 1366 1366 compat-table/es2025/regex.pattern-modifiers.i.js: OK 1367 1367 compat-table/es2025/regex.pattern-modifiers.m.js: OK 1368 - compat-table/es2025/regex.pattern-modifiers.s.js: OK 1368 + compat-table/es2025/regex.pattern-modifiers.s.js: OK
+4
include/modules/collections.h
··· 3 3 4 4 #include <uthash.h> 5 5 #include "types.h" 6 + #include "modules/symbol.h" 6 7 7 8 typedef struct map_entry { 8 9 char *key; ··· 54 55 55 56 extern ant_value_t g_map_iter_proto; 56 57 extern ant_value_t g_set_iter_proto; 58 + 59 + bool advance_map(ant_t *js, struct js_iter_t *it, ant_value_t *out); 60 + bool advance_set(ant_t *js, struct js_iter_t *it, ant_value_t *out); 57 61 58 62 #endif
+6
include/modules/headers.h
··· 1 1 #ifndef HEADERS_H 2 2 #define HEADERS_H 3 3 4 + #include "types.h" 5 + #include "modules/symbol.h" 6 + 7 + extern ant_value_t g_headers_iter_proto; 8 + 4 9 void init_headers_module(void); 10 + bool advance_headers(ant_t *js, struct js_iter_t *it, ant_value_t *out); 5 11 6 12 #endif
+6
include/modules/iterator.h
··· 1 + #ifndef ITERATOR_H 2 + #define ITERATOR_H 3 + 4 + void init_iterator_module(void); 5 + 6 + #endif
+29 -3
include/modules/symbol.h
··· 7 7 void init_symbol_module(void); 8 8 void js_define_species_getter(ant_t *js, ant_value_t ctor); 9 9 10 - #define ARR_ITER_PACK(kind, idx) ((uint32_t)(kind) << 28 | (uint32_t)(idx)) 11 - #define ARR_ITER_KIND(v) ((uint32_t)(v) >> 28) 12 - #define ARR_ITER_INDEX(v) ((uint32_t)(v) & 0x0FFFFFFFU) 10 + #define ITER_STATE_PACK(kind, n) ((uint32_t)(kind) << 28 | ((uint32_t)(n) & 0x0FFFFFFFU)) 11 + #define ITER_STATE_KIND(v) ((uint32_t)(v) >> 28) 12 + #define ITER_STATE_INDEX(v) ((uint32_t)(v) & 0x0FFFFFFFU) 13 13 14 14 enum { ARR_ITER_VALUES = 0, ARR_ITER_KEYS = 1, ARR_ITER_ENTRIES = 2 }; 15 15 ant_value_t make_array_iterator(ant_t *js, ant_value_t array, int kind); 16 16 17 + typedef struct js_iter_t js_iter_t; 18 + typedef bool (*js_iter_advance_fn)(ant_t *js, js_iter_t *it, ant_value_t *out); 19 + 20 + struct js_iter_t { 21 + ant_value_t iterator; 22 + ant_value_t next_fn; 23 + js_iter_advance_fn advance; 24 + }; 25 + 26 + void js_iter_register_advance(ant_value_t proto, js_iter_advance_fn fn); 27 + bool js_iter_open(ant_t *js, ant_value_t iterable, js_iter_t *it); 28 + bool js_iter_next(ant_t *js, js_iter_t *it, ant_value_t *out); 29 + void js_iter_close(ant_t *js, js_iter_t *it); 30 + 17 31 ant_value_t maybe_call_symbol_method( 18 32 ant_t *js, ant_value_t target, ant_value_t sym, 19 33 ant_value_t this_arg, ant_value_t *args, ··· 41 55 42 56 static inline ant_value_t sym_this_cb(ant_t *js, ant_value_t *args, int nargs) { 43 57 return js->this_val; 58 + } 59 + 60 + static inline ant_value_t js_iter_result(ant_t *js, bool has_value, ant_value_t value) { 61 + ant_value_t result = js_mkobj(js); 62 + if (__builtin_expect(has_value, 1)) { 63 + js_set(js, result, "done", js_false); 64 + js_set(js, result, "value", value); 65 + } else { 66 + js_set(js, result, "done", js_true); 67 + js_set(js, result, "value", js_mkundef()); 68 + } 69 + return result; 44 70 } 45 71 46 72 #endif
+2
src/main.c
··· 63 63 #include "modules/readline.h" 64 64 #include "modules/observable.h" 65 65 #include "modules/collections.h" 66 + #include "modules/iterator.h" 66 67 #include "modules/module.h" 67 68 #include "modules/util.h" 68 69 #include "modules/async_hooks.h" ··· 578 579 ant_runtime_init(js, proc_argv.argc, proc_argv.argv, localstorage_file); 579 580 580 581 init_symbol_module(); 582 + init_iterator_module(); 581 583 init_timer_module(); 582 584 init_domexception_module(); 583 585 init_globals_module();
+14 -25
src/modules/blob.c
··· 138 138 return js_mkerr_typed(js, JS_ERR_TYPE, 139 139 "Failed to construct 'Blob': The provided value cannot be converted to a sequence."); 140 140 141 - ant_value_t iter_fn = js_get_sym(js, parts, get_iterator_sym()); 142 - if (vtype(iter_fn) != T_FUNC && vtype(iter_fn) != T_CFUNC) 143 - return js_mkerr_typed(js, JS_ERR_TYPE, 144 - "Failed to construct 'Blob': The provided value is not of type 'BlobPart'"); 145 - 146 - ant_value_t iter = sv_call_native(js, iter_fn, parts, NULL, 0); 147 - if (is_err(iter)) return iter; 148 - 149 - ant_value_t next_fn = js_getprop_fallback(js, iter, "next"); 141 + js_iter_t it; 142 + if (!js_iter_open(js, parts, &it)) 143 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Blob': The provided value is not of type 'BlobPart'"); 150 144 151 - for (;;) { 152 - ant_value_t result = sv_call_native(js, next_fn, iter, NULL, 0); 153 - if (is_err(result)) return result; 154 - if (js_truthy(js, js_get(js, result, "done"))) break; 155 - 156 - ant_value_t r = process_blob_part(js, buf, js_get(js, result, "value")); 157 - if (is_err(r)) return r; 145 + ant_value_t value; 146 + while (js_iter_next(js, &it, &value)) { 147 + ant_value_t r = process_blob_part(js, buf, value); 148 + if (is_err(r)) { js_iter_close(js, &it); return r; } 158 149 } 150 + 159 151 return js_mkundef(); 160 152 } 161 153 162 154 static void blob_finalize(ant_t *js, ant_object_t *obj) { 163 - (void)js; 164 155 if (!obj->extra_slots) return; 165 156 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 166 157 for (uint8_t i = 0; i < obj->extra_count; i++) { 167 - if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 168 - blob_data_t *bd = (blob_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 169 - if (bd) { free(bd->data); free(bd->type); free(bd->name); free(bd); } 170 - return; 171 - } 172 - } 158 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 159 + blob_data_t *bd = (blob_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 160 + if (bd) { free(bd->data); free(bd->type); free(bd->name); free(bd); } 161 + return; 162 + }} 173 163 } 174 164 175 165 ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type) { ··· 432 422 void init_blob_module(void) { 433 423 ant_t *js = rt->js; 434 424 ant_value_t g = js_glob(js); 435 - 436 - g_blob_proto = js_mkobj(js); 425 + g_blob_proto = js_mkobj(js); 437 426 438 427 js_set_getter_desc(js, g_blob_proto, "size", 4, js_mkfun(blob_get_size), JS_DESC_C); 439 428 js_set_getter_desc(js, g_blob_proto, "type", 4, js_mkfun(blob_get_type), JS_DESC_C);
+96 -8
src/modules/buffer.c
··· 27 27 static uint8_t *ta_arena = NULL; 28 28 static size_t ta_arena_offset = 0; 29 29 30 - static ArrayBufferData **buffer_registry = NULL; 30 + static ArrayBufferData **buffer_registry = NULL; 31 + static ant_value_t g_typedarray_iter_proto = 0; 32 + 31 33 static size_t buffer_registry_count = 0; 32 - static size_t buffer_registry_cap = 0; 34 + static size_t buffer_registry_cap = 0; 35 + 36 + static bool advance_typedarray(ant_t *js, js_iter_t *it, ant_value_t *out) { 37 + ant_value_t iter = it->iterator; 38 + ant_value_t ta_obj = js_get_slot(iter, SLOT_DATA); 39 + ant_value_t state_v = js_get_slot(iter, SLOT_ITER_STATE); 40 + uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0; 41 + 42 + uint32_t kind = ITER_STATE_KIND(state); 43 + uint32_t idx = ITER_STATE_INDEX(state); 44 + 45 + ant_value_t ta_val = js_get_slot(ta_obj, SLOT_BUFFER); 46 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_val); 47 + if (!ta || !ta->buffer || ta->buffer->is_detached || idx >= (uint32_t)ta->length) 48 + return false; 49 + 50 + uint8_t *data = ta->buffer->data + ta->byte_offset; 51 + double value; 52 + switch (ta->type) { 53 + case TYPED_ARRAY_INT8: value = (double)((int8_t *)data)[idx]; break; 54 + case TYPED_ARRAY_UINT8: 55 + case TYPED_ARRAY_UINT8_CLAMPED: value = (double)data[idx]; break; 56 + case TYPED_ARRAY_INT16: value = (double)((int16_t *)data)[idx]; break; 57 + case TYPED_ARRAY_UINT16: value = (double)((uint16_t *)data)[idx]; break; 58 + case TYPED_ARRAY_INT32: value = (double)((int32_t *)data)[idx]; break; 59 + case TYPED_ARRAY_UINT32: value = (double)((uint32_t *)data)[idx]; break; 60 + case TYPED_ARRAY_FLOAT32: value = (double)((float *)data)[idx]; break; 61 + case TYPED_ARRAY_FLOAT64: value = ((double *)data)[idx]; break; 62 + default: return false; 63 + } 64 + 65 + switch (kind) { 66 + case ARR_ITER_KEYS: 67 + *out = js_mknum((double)idx); 68 + break; 69 + case ARR_ITER_ENTRIES: { 70 + ant_value_t pair = js_mkarr(js); 71 + js_arr_push(js, pair, js_mknum((double)idx)); 72 + js_arr_push(js, pair, js_mknum(value)); 73 + *out = pair; 74 + break; 75 + } 76 + default: 77 + *out = js_mknum(value); 78 + break; 79 + } 80 + 81 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, idx + 1))); 82 + return true; 83 + } 84 + 85 + static ant_value_t ta_iter_next(ant_t *js, ant_value_t *args, int nargs) { 86 + js_iter_t it = { .iterator = js->this_val }; 87 + ant_value_t value; 88 + return js_iter_result(js, advance_typedarray(js, &it, &value), value); 89 + } 90 + 91 + static ant_value_t ta_values(ant_t *js, ant_value_t *args, int nargs) { 92 + ant_value_t iter = js_mkobj(js); 93 + js_set_slot_wb(js, iter, SLOT_DATA, js->this_val); 94 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(ARR_ITER_VALUES, 0))); 95 + js_set_proto_init(iter, g_typedarray_iter_proto); 96 + return iter; 97 + } 98 + 99 + static ant_value_t ta_keys(ant_t *js, ant_value_t *args, int nargs) { 100 + ant_value_t iter = js_mkobj(js); 101 + js_set_slot_wb(js, iter, SLOT_DATA, js->this_val); 102 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(ARR_ITER_KEYS, 0))); 103 + js_set_proto_init(iter, g_typedarray_iter_proto); 104 + return iter; 105 + } 106 + 107 + static ant_value_t ta_entries(ant_t *js, ant_value_t *args, int nargs) { 108 + ant_value_t iter = js_mkobj(js); 109 + js_set_slot_wb(js, iter, SLOT_DATA, js->this_val); 110 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(ARR_ITER_ENTRIES, 0))); 111 + js_set_proto_init(iter, g_typedarray_iter_proto); 112 + return iter; 113 + } 33 114 34 115 static void register_buffer(ArrayBufferData *data) { 35 116 if (!data) return; ··· 1991 2072 js_set(js, typedarray_proto, "join", js_mkfun(js_typedarray_join)); 1992 2073 js_set_sym(js, typedarray_proto, get_toStringTag_sym(), js_mkstr(js, "TypedArray", 10)); 1993 2074 1994 - ant_value_t array_proto = js_get(js, js_get(js, glob, "Array"), "prototype"); 1995 - ant_value_t iter_fn = js_get_sym(js, array_proto, get_iterator_sym()); 1996 - js_set_sym(js, typedarray_proto, get_iterator_sym(), iter_fn); 1997 - js_set(js, typedarray_proto, "values", iter_fn); 2075 + g_typedarray_iter_proto = js_mkobj(js); 2076 + js_set_proto_init(g_typedarray_iter_proto, js->sym.iterator_proto); 2077 + js_set(js, g_typedarray_iter_proto, "next", js_mkfun(ta_iter_next)); 2078 + js_iter_register_advance(g_typedarray_iter_proto, advance_typedarray); 2079 + 2080 + ant_value_t ta_values_fn = js_mkfun(ta_values); 2081 + js_set(js, typedarray_proto, "values", ta_values_fn); 2082 + js_set(js, typedarray_proto, "keys", js_mkfun(ta_keys)); 2083 + js_set(js, typedarray_proto, "entries", js_mkfun(ta_entries)); 2084 + js_set_sym(js, typedarray_proto, get_iterator_sym(), ta_values_fn); 1998 2085 1999 2086 #define SETUP_TYPEDARRAY(name) \ 2000 2087 do { \ ··· 2069 2156 js_set(js, buffer_proto, "toString", js_mkfun(js_buffer_toString)); 2070 2157 js_set(js, buffer_proto, "toBase64", js_mkfun(js_buffer_toBase64)); 2071 2158 js_set(js, buffer_proto, "write", js_mkfun(js_buffer_write)); 2159 + 2072 2160 js_set_sym(js, buffer_proto, get_toStringTag_sym(), js_mkstr(js, "Buffer", 6)); 2073 - js_set_sym(js, buffer_proto, get_iterator_sym(), iter_fn); 2074 - js_set(js, buffer_proto, "values", iter_fn); 2161 + js_set_sym(js, buffer_proto, get_iterator_sym(), ta_values_fn); 2162 + js_set(js, buffer_proto, "values", ta_values_fn); 2075 2163 2076 2164 js_set(js, buffer_ctor_obj, "from", js_mkfun(js_buffer_from)); 2077 2165 js_set(js, buffer_ctor_obj, "alloc", js_mkfun(js_buffer_alloc));
+37 -59
src/modules/collections.c
··· 169 169 ant_value_t callback = args[0]; 170 170 171 171 if (map_ptr && *map_ptr) { 172 - map_entry_t *entry, *tmp; 173 - HASH_ITER(hh, *map_ptr, entry, tmp) { 174 - ant_value_t k = js_mkstr(js, entry->key, strlen(entry->key)); 175 - ant_value_t call_args[3] = { entry->value, k, this_val }; 176 - ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false); 177 - if (is_err(result)) return result; 178 - } 179 - } 172 + map_entry_t *entry, *tmp; 173 + HASH_ITER(hh, *map_ptr, entry, tmp) { 174 + ant_value_t k = js_mkstr(js, entry->key, strlen(entry->key)); 175 + ant_value_t call_args[3] = { entry->value, k, this_val }; 176 + ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), call_args, 3, NULL, false); 177 + if (is_err(result)) return result; 178 + }} 180 179 181 180 return js_mkundef(); 182 181 } 183 182 184 - static ant_value_t map_iter_next(ant_t *js, ant_value_t *args, int nargs) { 185 - (void)args; (void)nargs; 186 - ant_value_t this_val = js->this_val; 187 - 188 - map_iterator_state_t *state = get_map_iter_state(js, this_val); 189 - if (!state) return js_mkerr(js, "Invalid iterator"); 190 - 191 - ant_value_t result = js_mkobj(js); 192 - 193 - if (!state->current) { 194 - js_set(js, result, "done", js_true); 195 - js_set(js, result, "value", js_mkundef()); 196 - return result; 197 - } 198 - 183 + bool advance_map(ant_t *js, js_iter_t *it, ant_value_t *out) { 184 + map_iterator_state_t *state = get_map_iter_state(js, it->iterator); 185 + if (!state || !state->current) return false; 186 + 199 187 map_entry_t *entry = state->current; 200 - ant_value_t value; 201 - 202 188 switch (state->type) { 203 189 case ITER_TYPE_MAP_VALUES: 204 - value = entry->value; 190 + *out = entry->value; 205 191 break; 206 192 case ITER_TYPE_MAP_KEYS: 207 - value = js_mkstr(js, entry->key, strlen(entry->key)); 193 + *out = js_mkstr(js, entry->key, strlen(entry->key)); 208 194 break; 209 195 case ITER_TYPE_MAP_ENTRIES: { 210 196 ant_value_t pair = js_mkarr(js); 211 197 js_arr_push(js, pair, js_mkstr(js, entry->key, strlen(entry->key))); 212 198 js_arr_push(js, pair, entry->value); 213 - value = pair; 199 + *out = pair; 214 200 break; 215 201 } 216 - default: 217 - value = js_mkundef(); 202 + default: *out = js_mkundef(); 218 203 } 219 204 220 205 state->current = entry->hh.next; 221 - 222 - js_set(js, result, "value", value); 223 - js_set(js, result, "done", js_false); 224 - return result; 206 + return true; 207 + } 208 + 209 + static ant_value_t map_iter_next(ant_t *js, ant_value_t *args, int nargs) { 210 + js_iter_t it = { .iterator = js->this_val }; 211 + ant_value_t value; 212 + return js_iter_result(js, advance_map(js, &it, &value), value); 225 213 } 226 214 227 215 static ant_value_t create_map_iterator(ant_t *js, ant_value_t map_obj, iter_type_t type) { ··· 261 249 return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_ENTRIES); 262 250 } 263 251 264 - static ant_value_t set_iter_next(ant_t *js, ant_value_t *args, int nargs) { 265 - (void)args; (void)nargs; 266 - ant_value_t this_val = js->this_val; 267 - 268 - set_iterator_state_t *state = get_set_iter_state(js, this_val); 269 - if (!state) return js_mkerr(js, "Invalid iterator"); 270 - 271 - ant_value_t result = js_mkobj(js); 272 - 273 - if (!state->current) { 274 - js_set(js, result, "done", js_true); 275 - js_set(js, result, "value", js_mkundef()); 276 - return result; 277 - } 278 - 252 + bool advance_set(ant_t *js, js_iter_t *it, ant_value_t *out) { 253 + set_iterator_state_t *state = get_set_iter_state(js, it->iterator); 254 + if (!state || !state->current) return false; 255 + 279 256 set_entry_t *entry = state->current; 280 - ant_value_t value; 281 - 282 257 if (state->type == ITER_TYPE_SET_ENTRIES) { 283 258 ant_value_t pair = js_mkarr(js); 284 259 js_arr_push(js, pair, entry->value); 285 260 js_arr_push(js, pair, entry->value); 286 - value = pair; 287 - } else { 288 - value = entry->value; 289 - } 261 + *out = pair; 262 + } else *out = entry->value; 290 263 291 264 state->current = entry->hh.next; 292 - 293 - js_set(js, result, "value", value); 294 - js_set(js, result, "done", js_false); 295 - return result; 265 + return true; 266 + } 267 + 268 + static ant_value_t set_iter_next(ant_t *js, ant_value_t *args, int nargs) { 269 + js_iter_t it = { .iterator = js->this_val }; 270 + ant_value_t value; 271 + return js_iter_result(js, advance_set(js, &it, &value), value); 296 272 } 297 273 298 274 static ant_value_t create_set_iterator(ant_t *js, ant_value_t set_obj, iter_type_t type) { ··· 945 921 g_map_iter_proto = js_mkobj(js); 946 922 js_set_proto_init(g_map_iter_proto, js->sym.iterator_proto); 947 923 js_set(js, g_map_iter_proto, "next", js_mkfun(map_iter_next)); 924 + js_iter_register_advance(g_map_iter_proto, advance_map); 948 925 949 926 g_set_iter_proto = js_mkobj(js); 950 927 js_set_proto_init(g_set_iter_proto, js->sym.iterator_proto); 951 928 js_set(js, g_set_iter_proto, "next", js_mkfun(set_iter_next)); 929 + js_iter_register_advance(g_set_iter_proto, advance_set); 952 930 953 931 ant_value_t map_proto = js_mkobj(js); 954 932 js_set_proto_init(map_proto, object_proto);
+35 -44
src/modules/headers.c
··· 42 42 ITER_VALUES = 2 43 43 }; 44 44 45 - static ant_value_t g_headers_proto = 0; 46 - static ant_value_t g_headers_iter_proto = 0; 45 + static ant_value_t g_headers_proto = 0; 46 + ant_value_t g_headers_iter_proto = 0; 47 47 48 48 static hdr_list_t *list_new(void) { 49 49 hdr_list_t *l = ant_calloc(sizeof(hdr_list_t)); ··· 241 241 } 242 242 243 243 static ant_value_t init_from_sequence(ant_t *js, hdr_list_t *l, ant_value_t seq) { 244 - ant_value_t iter_fn = js_get_sym(js, seq, get_iterator_sym()); 245 - if (vtype(iter_fn) != T_FUNC && vtype(iter_fn) != T_CFUNC) 246 - return js_mkerr_typed(js, JS_ERR_TYPE, "Headers init is not iterable"); 247 - 248 - ant_value_t iter = sv_call_native(js, iter_fn, seq, NULL, 0); 249 - if (is_err(iter)) return iter; 250 - 251 - ant_value_t next_fn = js_getprop_fallback(js, iter, "next"); 252 - 253 - for (;;) { 254 - ant_value_t result = sv_call_native(js, next_fn, iter, NULL, 0); 255 - if (is_err(result)) return result; 256 - if (js_truthy(js, js_get(js, result, "done"))) break; 244 + js_iter_t it; 245 + if (!js_iter_open(js, seq, &it)) return js_mkerr_typed(js, JS_ERR_TYPE, "Headers init is not iterable"); 257 246 258 - ant_value_t pair = js_get(js, result, "value"); 247 + ant_value_t pair; 248 + while (js_iter_next(js, &it, &pair)) { 259 249 uint8_t pt = vtype(pair); 260 - if (pt != T_ARR && pt != T_OBJ) 250 + if (pt != T_ARR && pt != T_OBJ) { 251 + js_iter_close(js, &it); 261 252 return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must be a sequence"); 262 - 263 - if (js_arr_len(js, pair) != 2) 264 - return js_mkerr_typed(js, JS_ERR_TYPE, 265 - "Each header init pair must have exactly 2 elements"); 266 - 267 - ant_value_t r = headers_append_pair(js, l, js_arr_get(js, pair, 0), 268 - js_arr_get(js, pair, 1)); 269 - if (is_err(r)) return r; 253 + } 254 + 255 + if (js_arr_len(js, pair) != 2) { 256 + js_iter_close(js, &it); 257 + return js_mkerr_typed(js, JS_ERR_TYPE, "Each header init pair must have exactly 2 elements"); 258 + } 259 + 260 + ant_value_t r = headers_append_pair(js, l, js_arr_get(js, pair, 0), js_arr_get(js, pair, 1)); 261 + if (is_err(r)) { js_iter_close(js, &it); return r; } 270 262 } 263 + 271 264 return js_mkundef(); 272 265 } 273 266 274 - // iterate own enumerable properties as a record 275 267 static ant_value_t init_from_record(ant_t *js, hdr_list_t *l, ant_value_t obj) { 276 268 ant_iter_t it = js_prop_iter_begin(js, obj); 277 269 const char *key; ··· 282 274 ant_value_t r = headers_append_pair(js, l, js_mkstr(js, key, key_len), val); 283 275 if (is_err(r)) { js_prop_iter_end(&it); return r; } 284 276 } 277 + 285 278 js_prop_iter_end(&it); 286 279 return js_mkundef(); 287 280 } 288 281 289 - static ant_value_t headers_iter_next(ant_t *js, ant_value_t *args, int nargs) { 290 - ant_value_t state_val = js_get_slot(js->this_val, SLOT_ITER_STATE); 291 - if (vtype(state_val) == T_UNDEF) return js_mkerr(js, "Invalid iterator"); 282 + bool advance_headers(ant_t *js, js_iter_t *it, ant_value_t *out) { 283 + ant_value_t state_val = js_get_slot(it->iterator, SLOT_ITER_STATE); 284 + if (vtype(state_val) == T_UNDEF) return false; 292 285 hdr_iter_t *st = (hdr_iter_t *)(uintptr_t)(size_t)js_getnum(state_val); 293 286 294 287 size_t count = 0; 295 288 sorted_pair_t *view = build_sorted_view(st->list, &count); 296 - 297 - ant_value_t result = js_mkobj(js); 298 289 299 290 if (st->index >= count) { 300 291 free_sorted_view(view, count); 301 - js_set(js, result, "done", js_true); 302 - js_set(js, result, "value", js_mkundef()); 303 - return result; 292 + return false; 304 293 } 305 294 306 295 sorted_pair_t *e = &view[st->index]; 307 - ant_value_t value; 308 - 309 296 switch (st->kind) { 310 297 case ITER_KEYS: 311 - value = js_mkstr(js, e->name, strlen(e->name)); 298 + *out = js_mkstr(js, e->name, strlen(e->name)); 312 299 break; 313 300 case ITER_VALUES: 314 - value = js_mkstr(js, e->value, strlen(e->value)); 301 + *out = js_mkstr(js, e->value, strlen(e->value)); 315 302 break; 316 303 default: { 317 - value = js_mkarr(js); 318 - js_arr_push(js, value, js_mkstr(js, e->name, strlen(e->name))); 319 - js_arr_push(js, value, js_mkstr(js, e->value, strlen(e->value))); 304 + *out = js_mkarr(js); 305 + js_arr_push(js, *out, js_mkstr(js, e->name, strlen(e->name))); 306 + js_arr_push(js, *out, js_mkstr(js, e->value, strlen(e->value))); 320 307 break; 321 308 }} 322 309 323 310 free_sorted_view(view, count); 324 311 st->index++; 312 + return true; 313 + } 325 314 326 - js_set(js, result, "done", js_false); 327 - js_set(js, result, "value", value); 328 - return result; 315 + static ant_value_t headers_iter_next(ant_t *js, ant_value_t *args, int nargs) { 316 + js_iter_t it = { .iterator = js->this_val }; 317 + ant_value_t value; 318 + return js_iter_result(js, advance_headers(js, &it, &value), value); 329 319 } 330 320 331 321 static ant_value_t make_headers_iter(ant_t *js, ant_value_t headers_obj, int kind) { ··· 579 569 js_set(js, g_headers_iter_proto, "next", js_mkfun(headers_iter_next)); 580 570 js_set_descriptor(js, g_headers_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 581 571 js_set_sym(js, g_headers_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 572 + js_iter_register_advance(g_headers_iter_proto, advance_headers); 582 573 583 574 g_headers_proto = js_mkobj(js); 584 575
+416
src/modules/iterator.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + 4 + #include "ant.h" 5 + #include "errors.h" 6 + #include "runtime.h" 7 + #include "internal.h" 8 + #include "silver/engine.h" 9 + #include "descriptors.h" 10 + 11 + #include "modules/iterator.h" 12 + #include "modules/symbol.h" 13 + 14 + enum { 15 + WRAP_MAP = 0, 16 + WRAP_FILTER = 1, 17 + WRAP_TAKE = 2, 18 + WRAP_DROP = 3, 19 + WRAP_FLATMAP = 4, 20 + }; 21 + 22 + static ant_value_t g_wrap_iter_proto = 0; 23 + 24 + static ant_value_t wrap_iter_next(ant_t *js, ant_value_t *args, int nargs) { 25 + ant_value_t self = js->this_val; 26 + ant_value_t source = js_get_slot(self, SLOT_DATA); 27 + ant_value_t state_v = js_get_slot(self, SLOT_ITER_STATE); 28 + 29 + uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0; 30 + uint32_t kind = ITER_STATE_KIND(state); 31 + uint32_t count = ITER_STATE_INDEX(state); 32 + ant_value_t cb = js_get_slot(self, SLOT_CTOR); 33 + 34 + ant_value_t result = js_mkobj(js); 35 + ant_value_t next_fn = js_getprop_fallback(js, source, "next"); 36 + 37 + for (;;) { 38 + if (kind == WRAP_FLATMAP) { 39 + ant_value_t inner = js_get_slot(self, SLOT_ENTRIES); 40 + 41 + if (vtype(inner) != T_UNDEF) { 42 + ant_value_t inner_next = js_getprop_fallback(js, inner, "next"); 43 + ant_value_t inner_step = sv_vm_call(js->vm, js, inner_next, inner, NULL, 0, NULL, false); 44 + if (!is_err(inner_step)) { 45 + ant_value_t inner_done = js_getprop_fallback(js, inner_step, "done"); 46 + if (!js_truthy(js, inner_done)) { 47 + js_set(js, result, "done", js_false); 48 + js_set(js, result, "value", js_getprop_fallback(js, inner_step, "value")); 49 + return result; 50 + }} 51 + 52 + js_set_slot(self, SLOT_ENTRIES, js_mkundef()); 53 + }} 54 + 55 + ant_value_t step; 56 + if (vtype(next_fn) == T_CFUNC) { 57 + ant_value_t old_this = js->this_val; 58 + js->this_val = source; 59 + step = js_as_cfunc(next_fn)(js, NULL, 0); 60 + js->this_val = old_this; 61 + } else step = sv_vm_call(js->vm, js, next_fn, source, NULL, 0, NULL, false); 62 + 63 + if (is_err(step)) return step; 64 + ant_value_t done = js_getprop_fallback(js, step, "done"); 65 + 66 + if (js_truthy(js, done)) { 67 + if (kind == WRAP_FLATMAP) { 68 + ant_value_t inner = js_get_slot(self, SLOT_ENTRIES); 69 + if (vtype(inner) != T_UNDEF) { 70 + js_set_slot(self, SLOT_ENTRIES, js_mkundef()); 71 + }} 72 + 73 + js_set(js, result, "done", js_true); 74 + js_set(js, result, "value", js_mkundef()); 75 + 76 + return result; 77 + } 78 + 79 + ant_value_t value = js_getprop_fallback(js, step, "value"); 80 + 81 + switch (kind) { 82 + case WRAP_MAP: { 83 + ant_value_t out_val; 84 + if (is_callable(cb)) { 85 + ant_value_t call_args[2] = { value, js_mknum((double)count) }; 86 + out_val = sv_vm_call(js->vm, js, cb, js_mkundef(), call_args, 2, NULL, false); 87 + if (is_err(out_val)) return out_val; 88 + } else out_val = value; 89 + 90 + count++; 91 + js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count))); 92 + js_set(js, result, "done", js_false); 93 + js_set(js, result, "value", out_val); 94 + 95 + return result; 96 + } 97 + 98 + case WRAP_FILTER: { 99 + ant_value_t call_args[2] = { value, js_mknum((double)count) }; 100 + ant_value_t test = sv_vm_call(js->vm, js, cb, js_mkundef(), call_args, 2, NULL, false); 101 + if (is_err(test)) return test; 102 + count++; 103 + js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count))); 104 + if (js_truthy(js, test)) { 105 + js_set(js, result, "done", js_false); 106 + js_set(js, result, "value", value); 107 + return result; 108 + } 109 + continue; 110 + } 111 + 112 + case WRAP_TAKE: { 113 + uint32_t limit = (vtype(cb) == T_NUM) ? (uint32_t)js_getnum(cb) : 0; 114 + if (count >= limit) { 115 + js_set(js, result, "done", js_true); 116 + js_set(js, result, "value", js_mkundef()); 117 + return result; 118 + } 119 + js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1))); 120 + js_set(js, result, "done", js_false); 121 + js_set(js, result, "value", value); 122 + return result; 123 + } 124 + 125 + case WRAP_DROP: { 126 + uint32_t limit = (vtype(cb) == T_NUM) ? (uint32_t)js_getnum(cb) : 0; 127 + count++; 128 + js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count))); 129 + if (count <= limit) continue; 130 + js_set(js, result, "done", js_false); 131 + js_set(js, result, "value", value); 132 + return result; 133 + } 134 + 135 + case WRAP_FLATMAP: { 136 + ant_value_t call_args[2] = { value, js_mknum((double)count) }; 137 + ant_value_t mapped = sv_vm_call(js->vm, js, cb, js_mkundef(), call_args, 2, NULL, false); 138 + if (is_err(mapped)) return mapped; 139 + count++; 140 + js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count))); 141 + 142 + ant_value_t iter_fn = js_get_sym(js, mapped, get_iterator_sym()); 143 + if (!is_callable(iter_fn)) { 144 + js_set(js, result, "done", js_false); 145 + js_set(js, result, "value", mapped); 146 + return result; 147 + } 148 + 149 + ant_value_t inner = sv_vm_call(js->vm, js, iter_fn, mapped, NULL, 0, NULL, false); 150 + if (is_err(inner)) return inner; 151 + 152 + ant_value_t inner_next = js_getprop_fallback(js, inner, "next"); 153 + ant_value_t inner_step = sv_vm_call(js->vm, js, inner_next, inner, NULL, 0, NULL, false); 154 + if (is_err(inner_step)) return inner_step; 155 + ant_value_t inner_done = js_getprop_fallback(js, inner_step, "done"); 156 + if (!js_truthy(js, inner_done)) { 157 + js_set_slot_wb(js, self, SLOT_ENTRIES, inner); 158 + js_set(js, result, "done", js_false); 159 + js_set(js, result, "value", js_getprop_fallback(js, inner_step, "value")); 160 + return result; 161 + } 162 + continue; 163 + } 164 + 165 + default: 166 + js_set(js, result, "done", js_false); 167 + js_set(js, result, "value", value); 168 + return result; 169 + } 170 + } 171 + } 172 + 173 + static ant_value_t make_wrap_iter(ant_t *js, ant_value_t source, int kind, ant_value_t cb) { 174 + ant_value_t iter = js_mkobj(js); 175 + 176 + js_set_proto_init(iter, g_wrap_iter_proto); 177 + js_set_slot_wb(js, iter, SLOT_DATA, source); 178 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0))); 179 + js_set_slot_wb(js, iter, SLOT_CTOR, cb); 180 + 181 + return iter; 182 + } 183 + 184 + static ant_value_t get_source_iter(ant_t *js) { 185 + ant_value_t self = js->this_val; 186 + ant_value_t next = js_getprop_fallback(js, self, "next"); 187 + if (is_callable(next)) return self; 188 + 189 + ant_value_t iter_fn = js_get_sym(js, self, get_iterator_sym()); 190 + if (!is_callable(iter_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 191 + 192 + return sv_vm_call(js->vm, js, iter_fn, self, NULL, 0, NULL, false); 193 + } 194 + 195 + static ant_value_t iter_map(ant_t *js, ant_value_t *args, int nargs) { 196 + if (nargs < 1 || !is_callable(args[0])) 197 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.map requires a callable"); 198 + ant_value_t source = get_source_iter(js); 199 + if (is_err(source)) return source; 200 + return make_wrap_iter(js, source, WRAP_MAP, args[0]); 201 + } 202 + 203 + static ant_value_t iter_filter(ant_t *js, ant_value_t *args, int nargs) { 204 + if (nargs < 1 || !is_callable(args[0])) 205 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.filter requires a callable"); 206 + ant_value_t source = get_source_iter(js); 207 + if (is_err(source)) return source; 208 + return make_wrap_iter(js, source, WRAP_FILTER, args[0]); 209 + } 210 + 211 + static ant_value_t iter_take(ant_t *js, ant_value_t *args, int nargs) { 212 + double limit = (nargs >= 1 && vtype(args[0]) == T_NUM) ? js_getnum(args[0]) : 0; 213 + if (limit < 0) return js_mkerr(js, "Iterator.prototype.take requires a non-negative number"); 214 + ant_value_t source = get_source_iter(js); 215 + if (is_err(source)) return source; 216 + return make_wrap_iter(js, source, WRAP_TAKE, js_mknum(limit)); 217 + } 218 + 219 + static ant_value_t iter_drop(ant_t *js, ant_value_t *args, int nargs) { 220 + double limit = (nargs >= 1 && vtype(args[0]) == T_NUM) ? js_getnum(args[0]) : 0; 221 + if (limit < 0) return js_mkerr(js, "Iterator.prototype.drop requires a non-negative number"); 222 + ant_value_t source = get_source_iter(js); 223 + if (is_err(source)) return source; 224 + return make_wrap_iter(js, source, WRAP_DROP, js_mknum(limit)); 225 + } 226 + 227 + static ant_value_t iter_flatMap(ant_t *js, ant_value_t *args, int nargs) { 228 + if (nargs < 1 || !is_callable(args[0])) 229 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.flatMap requires a callable"); 230 + ant_value_t source = get_source_iter(js); 231 + if (is_err(source)) return source; 232 + return make_wrap_iter(js, source, WRAP_FLATMAP, args[0]); 233 + } 234 + 235 + static ant_value_t iter_every(ant_t *js, ant_value_t *args, int nargs) { 236 + if (nargs < 1 || !is_callable(args[0])) 237 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.every requires a callable"); 238 + ant_value_t fn = args[0]; 239 + 240 + js_iter_t it; 241 + if (!js_iter_open(js, js->this_val, &it)) 242 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 243 + 244 + ant_value_t value; 245 + uint32_t counter = 0; 246 + while (js_iter_next(js, &it, &value)) { 247 + ant_value_t call_args[2] = { value, js_mknum((double)counter++) }; 248 + ant_value_t test = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 2, NULL, false); 249 + if (is_err(test)) { js_iter_close(js, &it); return test; } 250 + if (!js_truthy(js, test)) { js_iter_close(js, &it); return js_false; } 251 + } 252 + return js_true; 253 + } 254 + 255 + static ant_value_t iter_some(ant_t *js, ant_value_t *args, int nargs) { 256 + if (nargs < 1 || !is_callable(args[0])) 257 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.some requires a callable"); 258 + ant_value_t fn = args[0]; 259 + 260 + js_iter_t it; 261 + if (!js_iter_open(js, js->this_val, &it)) 262 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 263 + 264 + ant_value_t value; 265 + uint32_t counter = 0; 266 + while (js_iter_next(js, &it, &value)) { 267 + ant_value_t call_args[2] = { value, js_mknum((double)counter++) }; 268 + ant_value_t test = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 2, NULL, false); 269 + if (is_err(test)) { js_iter_close(js, &it); return test; } 270 + if (js_truthy(js, test)) { js_iter_close(js, &it); return js_true; } 271 + } 272 + return js_false; 273 + } 274 + 275 + static ant_value_t iter_find(ant_t *js, ant_value_t *args, int nargs) { 276 + if (nargs < 1 || !is_callable(args[0])) 277 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.find requires a callable"); 278 + ant_value_t fn = args[0]; 279 + 280 + js_iter_t it; 281 + if (!js_iter_open(js, js->this_val, &it)) 282 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 283 + 284 + ant_value_t value; 285 + uint32_t counter = 0; 286 + while (js_iter_next(js, &it, &value)) { 287 + ant_value_t call_args[2] = { value, js_mknum((double)counter++) }; 288 + ant_value_t test = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 2, NULL, false); 289 + if (is_err(test)) { js_iter_close(js, &it); return test; } 290 + if (js_truthy(js, test)) { js_iter_close(js, &it); return value; } 291 + } 292 + return js_mkundef(); 293 + } 294 + 295 + static ant_value_t iter_forEach(ant_t *js, ant_value_t *args, int nargs) { 296 + if (nargs < 1 || !is_callable(args[0])) 297 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.forEach requires a callable"); 298 + ant_value_t fn = args[0]; 299 + 300 + js_iter_t it; 301 + if (!js_iter_open(js, js->this_val, &it)) 302 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 303 + 304 + ant_value_t value; 305 + uint32_t counter = 0; 306 + while (js_iter_next(js, &it, &value)) { 307 + ant_value_t call_args[2] = { value, js_mknum((double)counter++) }; 308 + ant_value_t r = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 2, NULL, false); 309 + if (is_err(r)) { js_iter_close(js, &it); return r; } 310 + } 311 + return js_mkundef(); 312 + } 313 + 314 + static ant_value_t iter_reduce(ant_t *js, ant_value_t *args, int nargs) { 315 + if (nargs < 1 || !is_callable(args[0])) 316 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.reduce requires a callable"); 317 + ant_value_t fn = args[0]; 318 + bool has_init = (nargs >= 2); 319 + 320 + js_iter_t it; 321 + if (!js_iter_open(js, js->this_val, &it)) 322 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 323 + 324 + ant_value_t acc = has_init ? args[1] : js_mkundef(); 325 + bool first = !has_init; 326 + ant_value_t value; 327 + uint32_t counter = 0; 328 + 329 + while (js_iter_next(js, &it, &value)) { 330 + if (first) { acc = value; first = false; counter++; continue; } 331 + ant_value_t call_args[3] = { acc, value, js_mknum((double)counter++) }; 332 + acc = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 3, NULL, false); 333 + if (is_err(acc)) { js_iter_close(js, &it); return acc; } 334 + } 335 + 336 + if (first) 337 + return js_mkerr_typed(js, JS_ERR_TYPE, "reduce of empty iterator with no initial value"); 338 + return acc; 339 + } 340 + 341 + static ant_value_t iter_toArray(ant_t *js, ant_value_t *args, int nargs) { 342 + js_iter_t it; 343 + if (!js_iter_open(js, js->this_val, &it)) 344 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 345 + 346 + ant_value_t arr = js_mkarr(js); 347 + ant_value_t value; 348 + while (js_iter_next(js, &it, &value)) 349 + js_arr_push(js, arr, value); 350 + 351 + return arr; 352 + } 353 + 354 + static ant_value_t iter_from(ant_t *js, ant_value_t *args, int nargs) { 355 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.from requires an argument"); 356 + ant_value_t obj = args[0]; 357 + 358 + ant_value_t next = js_getprop_fallback(js, obj, "next"); 359 + if (is_callable(next)) { 360 + return make_wrap_iter(js, obj, WRAP_MAP, js_mkundef()); 361 + } 362 + 363 + ant_value_t iter_fn = js_get_sym(js, obj, get_iterator_sym()); 364 + if (!is_callable(iter_fn)) 365 + return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable"); 366 + 367 + ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, obj, NULL, 0, NULL, false); 368 + if (is_err(iterator)) return iterator; 369 + 370 + return iterator; 371 + } 372 + 373 + static ant_value_t iter_ctor(ant_t *js, ant_value_t *args, int nargs) { 374 + if (vtype(js->new_target) == T_UNDEF) 375 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator is not directly constructable"); 376 + 377 + ant_value_t obj = js_mkobj(js); 378 + ant_value_t proto = js_instance_proto_from_new_target(js, js->sym.iterator_proto); 379 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 380 + 381 + return obj; 382 + } 383 + 384 + void init_iterator_module(void) { 385 + ant_t *js = rt->js; 386 + ant_value_t g = js_glob(js); 387 + ant_value_t iter_proto = js->sym.iterator_proto; 388 + 389 + g_wrap_iter_proto = js_mkobj(js); 390 + js_set_proto_init(g_wrap_iter_proto, iter_proto); 391 + js_set(js, g_wrap_iter_proto, "next", js_mkfun(wrap_iter_next)); 392 + 393 + js_set(js, iter_proto, "map", js_mkfun(iter_map)); 394 + js_set(js, iter_proto, "filter", js_mkfun(iter_filter)); 395 + js_set(js, iter_proto, "take", js_mkfun(iter_take)); 396 + js_set(js, iter_proto, "drop", js_mkfun(iter_drop)); 397 + js_set(js, iter_proto, "flatMap", js_mkfun(iter_flatMap)); 398 + js_set(js, iter_proto, "every", js_mkfun(iter_every)); 399 + js_set(js, iter_proto, "some", js_mkfun(iter_some)); 400 + js_set(js, iter_proto, "find", js_mkfun(iter_find)); 401 + js_set(js, iter_proto, "forEach", js_mkfun(iter_forEach)); 402 + js_set(js, iter_proto, "reduce", js_mkfun(iter_reduce)); 403 + js_set(js, iter_proto, "toArray", js_mkfun(iter_toArray)); 404 + js_set_sym(js, iter_proto, get_toStringTag_sym(), js_mkstr(js, "Iterator", 8)); 405 + 406 + ant_value_t ctor_obj = js_mkobj(js); 407 + js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(iter_ctor)); 408 + js_mkprop_fast(js, ctor_obj, "prototype", 9, iter_proto); 409 + js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "Iterator", 8)); 410 + js_set_descriptor(js, ctor_obj, "name", 4, 0); 411 + js_set(js, ctor_obj, "from", js_mkfun(iter_from)); 412 + 413 + ant_value_t ctor = js_obj_to_func(ctor_obj); 414 + js_set(js, iter_proto, "constructor", ctor); 415 + js_set(js, g, "Iterator", ctor); 416 + }
+106 -47
src/modules/symbol.c
··· 74 74 return js->sym.iterator_proto; 75 75 } 76 76 77 - static ant_value_t arr_iter_next(ant_t *js, ant_value_t *args, int nargs) { 78 - ant_value_t iter = js->this_val; 77 + static bool advance_array(ant_t *js, js_iter_t *it, ant_value_t *out) { 78 + ant_value_t iter = it->iterator; 79 79 ant_value_t array = js_get_slot(iter, SLOT_DATA); 80 80 ant_value_t state_v = js_get_slot(iter, SLOT_ITER_STATE); 81 81 82 82 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0; 83 - uint32_t kind = ARR_ITER_KIND(state); 84 - uint32_t idx = ARR_ITER_INDEX(state); 85 - 86 - ant_value_t result = js_mkobj(js); 83 + uint32_t kind = ITER_STATE_KIND(state); 84 + uint32_t idx = ITER_STATE_INDEX(state); 87 85 ant_offset_t len = js_arr_len(js, array); 88 - 89 - if (idx >= (uint32_t)len) { 90 - js_set(js, result, "done", js_true); 91 - js_set(js, result, "value", js_mkundef()); 92 - return result; 93 - } 86 + if (idx >= (uint32_t)len) return false; 94 87 95 - ant_value_t value; 96 88 switch (kind) { 97 89 case ARR_ITER_KEYS: 98 - value = js_mknum((double)idx); 90 + *out = js_mknum((double)idx); 99 91 break; 100 92 case ARR_ITER_ENTRIES: { 101 93 ant_value_t pair = js_mkarr(js); 102 94 js_arr_push(js, pair, js_mknum((double)idx)); 103 95 js_arr_push(js, pair, js_arr_get(js, array, (ant_offset_t)idx)); 104 - value = pair; 96 + *out = pair; 105 97 break; 106 98 } 107 99 default: 108 - value = js_arr_get(js, array, (ant_offset_t)idx); 100 + *out = js_arr_get(js, array, (ant_offset_t)idx); 109 101 break; 110 102 } 111 103 112 - js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ARR_ITER_PACK(kind, idx + 1))); 113 - js_set(js, result, "done", js_false); 114 - js_set(js, result, "value", value); 115 - 116 - return result; 104 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, idx + 1))); 105 + return true; 106 + } 107 + 108 + static bool advance_string(ant_t *js, js_iter_t *it, ant_value_t *out) { 109 + ant_value_t iter = it->iterator; 110 + ant_value_t str = js_get_slot(iter, SLOT_DATA); 111 + ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE); 112 + int idx = (vtype(idx_v) == T_NUM) ? (int)js_getnum(idx_v) : 0; 113 + 114 + size_t slen; 115 + char *s = js_getstr(js, str, &slen); 116 + if (idx >= (int)slen) return false; 117 + 118 + unsigned char c = (unsigned char)s[idx]; 119 + int char_bytes = utf8_sequence_length(c); 120 + if (char_bytes < 1) char_bytes = 1; 121 + if (idx + char_bytes > (int)slen) char_bytes = (int)slen - idx; 122 + 123 + *out = js_mkstr(js, s + idx, (ant_offset_t)char_bytes); 124 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum(idx + char_bytes)); 125 + return true; 126 + } 127 + 128 + static ant_value_t arr_iter_next(ant_t *js, ant_value_t *args, int nargs) { 129 + js_iter_t it = { .iterator = js->this_val }; 130 + ant_value_t value; 131 + return js_iter_result(js, advance_array(js, &it, &value), value); 117 132 } 118 133 119 134 static ant_value_t get_array_iterator_prototype(ant_t *js) { ··· 130 145 ant_value_t make_array_iterator(ant_t *js, ant_value_t array, int kind) { 131 146 ant_value_t iter = js_mkobj(js); 132 147 js_set_slot_wb(js, iter, SLOT_DATA, array); 133 - js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ARR_ITER_PACK(kind, 0))); 148 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0))); 134 149 js_set_proto_init(iter, get_array_iterator_prototype(js)); 135 150 return iter; 136 151 } 137 152 138 153 static ant_value_t str_iter_next(ant_t *js, ant_value_t *args, int nargs) { 139 - ant_value_t iter = js->this_val; 140 - ant_value_t str = js_get_slot(iter, SLOT_DATA); 141 - ant_value_t idx_v = js_get_slot(iter, SLOT_ITER_STATE); 142 - int idx = (vtype(idx_v) == T_NUM) ? (int)js_getnum(idx_v) : 0; 143 - 144 - size_t slen; 145 - char *s = js_getstr(js, str, &slen); 146 - ant_value_t result = js_mkobj(js); 147 - 148 - if (idx >= (int)slen) { 149 - js_set(js, result, "done", js_true); 150 - js_set(js, result, "value", js_mkundef()); 151 - return result; 152 - } 153 - 154 - unsigned char c = (unsigned char)s[idx]; 155 - int char_bytes = utf8_sequence_length(c); 156 - if (char_bytes < 1) char_bytes = 1; 157 - if (idx + char_bytes > (int)slen) char_bytes = (int)slen - idx; 158 - 159 - js_set(js, result, "value", js_mkstr(js, s + idx, (ant_offset_t)char_bytes)); 160 - js_set(js, result, "done", js_false); 161 - js_set_slot(iter, SLOT_ITER_STATE, js_mknum(idx + char_bytes)); 162 - 163 - return result; 154 + js_iter_t it = { .iterator = js->this_val }; 155 + ant_value_t value; 156 + return js_iter_result(js, advance_string(js, &it, &value), value); 164 157 } 165 158 166 159 static ant_value_t get_string_iterator_prototype(ant_t *js) { ··· 184 177 return iter; 185 178 } 186 179 180 + static struct { 181 + ant_value_t proto; 182 + js_iter_advance_fn fn; 183 + } g_advance_table[8]; 184 + 185 + static int g_advance_count = 0; 186 + 187 + void js_iter_register_advance(ant_value_t proto, js_iter_advance_fn fn) { 188 + if (g_advance_count < 8) 189 + g_advance_table[g_advance_count++] = (typeof(g_advance_table[0])){ proto, fn }; 190 + } 191 + 192 + bool js_iter_open(ant_t *js, ant_value_t iterable, js_iter_t *it) { 193 + memset(it, 0, sizeof(*it)); 194 + 195 + ant_value_t iter_fn = js_get_sym(js, iterable, get_iterator_sym()); 196 + if (!is_callable(iter_fn)) return false; 197 + 198 + ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, iterable, NULL, 0, NULL, false); 199 + if (is_err(iterator)) return false; 200 + 201 + it->iterator = iterator; 202 + it->next_fn = js_getprop_fallback(js, iterator, "next"); 203 + it->advance = NULL; 204 + 205 + ant_value_t proto = (vtype(iterator) == T_OBJ) ? js_get_proto(js, iterator) : js_mkundef(); 206 + for (int i = 0; i < g_advance_count; i++) 207 + if (proto == g_advance_table[i].proto) { it->advance = g_advance_table[i].fn; break; } 208 + 209 + return true; 210 + } 211 + 212 + bool js_iter_next(ant_t *js, js_iter_t *it, ant_value_t *out) { 213 + if (it->advance) return it->advance(js, it, out); 214 + 215 + ant_value_t next_fn = it->next_fn; 216 + ant_value_t result; 217 + 218 + if (vtype(next_fn) == T_CFUNC) { 219 + ant_value_t old_this = js->this_val; 220 + js->this_val = it->iterator; 221 + result = js_as_cfunc(next_fn)(js, NULL, 0); 222 + js->this_val = old_this; 223 + } 224 + 225 + else if (is_callable(next_fn)) result = sv_vm_call(js->vm, js, next_fn, it->iterator, NULL, 0, NULL, false); 226 + else return false; 227 + 228 + if (is_err(result)) return false; 229 + ant_value_t done = js_getprop_fallback(js, result, "done"); 230 + 231 + if (js_truthy(js, done)) return false; 232 + *out = js_getprop_fallback(js, result, "value"); 233 + 234 + return true; 235 + } 236 + 237 + void js_iter_close(ant_t *js, js_iter_t *it) { 238 + if (it->advance) return; 239 + ant_value_t return_fn = js_getprop_fallback(js, it->iterator, "return"); 240 + if (is_callable(return_fn)) sv_vm_call(js->vm, js, return_fn, it->iterator, NULL, 0, NULL, false); 241 + } 242 + 187 243 ant_value_t maybe_call_symbol_method( 188 244 ant_t *js, ant_value_t target, 189 245 ant_value_t sym, ··· 249 305 250 306 (void)get_array_iterator_prototype(js); 251 307 (void)get_string_iterator_prototype(js); 308 + 309 + js_iter_register_advance(js->sym.array_iterator_proto, advance_array); 310 + js_iter_register_advance(js->sym.string_iterator_proto, advance_string); 252 311 253 312 js_set_sym(js, rt->ant_obj, g_toStringTag, js_mkstr(js, "Ant", 3)); 254 313 js_set_sym(js, array_proto, g_iterator, js_get(js, array_proto, "values"));