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 support for Symbol.unscopables well-known symbol key

+162 -55
+1
include/modules/symbol.h
··· 19 19 const char *get_toPrimitive_sym_key(void); 20 20 const char *get_hasInstance_sym_key(void); 21 21 const char *get_species_sym_key(void); 22 + const char *get_unscopables_sym_key(void); 22 23 23 24 jsval_t get_wellknown_sym_by_key(const char *key, size_t key_len); 24 25 const char *get_symbol_description_from_key(const char *sym_key, size_t key_len);
+89 -47
src/ant.c
··· 11728 11728 typedef enum { ITER_CONTINUE, ITER_BREAK, ITER_ERROR } iter_action_t; 11729 11729 typedef iter_action_t (*iter_callback_t)(struct js *js, jsval_t value, void *ctx, jsval_t *out); 11730 11730 11731 - static jsval_t iter_foreach(struct js *js, jsval_t iterable, iter_callback_t cb, void *ctx) { 11732 - const char *iter_key = get_iterator_sym_key(); 11733 - jsoff_t iter_prop = iter_key ? lkp_proto(js, iterable, iter_key, strlen(iter_key)) : 0; 11734 - if (iter_prop == 0) return js_mkerr(js, "not iterable"); 11735 - 11731 + static jsval_t iter_call_noargs_with_this(struct js *js, jsval_t this_val, jsval_t method) { 11736 11732 js_parse_state_t saved_state; 11737 11733 JS_SAVE_STATE(js, saved_state); 11738 11734 uint8_t saved_flags = js->flags; 11739 - 11740 - jsval_t iter_method = loadval(js, iter_prop + sizeof(jsoff_t) * 2); 11741 - jsval_t outer_saved_this = js->this_val; 11742 - push_this(iterable); 11743 - js->this_val = iterable; 11744 - jsval_t iterator = call_js_with_args(js, iter_method, NULL, 0); 11745 - pop_this(); 11746 - js->this_val = outer_saved_this; 11735 + jsval_t saved_this = js->this_val; 11736 + push_this(this_val); js->this_val = this_val; 11737 + jsval_t result = call_js_with_args(js, method, NULL, 0); 11738 + pop_this(); js->this_val = saved_this; 11747 11739 JS_RESTORE_STATE(js, saved_state); 11748 11740 js->flags = saved_flags; 11749 - 11741 + return result; 11742 + } 11743 + 11744 + static jsval_t iter_close_iterator(struct js *js, jsval_t iterator) { 11745 + jsoff_t return_off = lkp_proto(js, iterator, "return", 6); 11746 + if (return_off == 0) return js_mkundef(); 11747 + jsval_t return_method = loadval(js, return_off + sizeof(jsoff_t) * 2); 11748 + if (vtype(return_method) != T_FUNC && vtype(return_method) != T_CFUNC) { 11749 + return js_mkerr(js, "iterator.return is not a function"); 11750 + } 11751 + return iter_call_noargs_with_this(js, iterator, return_method); 11752 + } 11753 + 11754 + static jsval_t iter_foreach(struct js *js, jsval_t iterable, iter_callback_t cb, void *ctx) { 11755 + const char *iter_key = get_iterator_sym_key(); 11756 + jsoff_t iter_prop = iter_key ? lkp_proto(js, iterable, iter_key, strlen(iter_key)) : 0; 11757 + if (iter_prop == 0) return js_mkerr(js, "not iterable"); 11758 + 11759 + jsval_t iter_method = loadval(js, iter_prop + sizeof(jsoff_t) * 2); 11760 + jsval_t iterator = iter_call_noargs_with_this(js, iterable, iter_method); 11750 11761 if (is_err(iterator)) return iterator; 11751 11762 11752 11763 jshdl_t h_iterator = js_root(js, iterator); ··· 11764 11775 } 11765 11776 11766 11777 cur_iter = js_deref(js, h_iterator); 11767 - jsval_t saved_this = js->this_val; 11768 - push_this(cur_iter); 11769 - js->this_val = cur_iter; 11770 - jsval_t result = call_js_with_args(js, next_method, NULL, 0); 11771 - pop_this(); 11772 - js->this_val = saved_this; 11773 - JS_RESTORE_STATE(js, saved_state); 11774 - js->flags = saved_flags; 11775 - 11778 + jsval_t result = iter_call_noargs_with_this(js, cur_iter, next_method); 11776 11779 if (is_err(result)) { js_unroot(js, h_iterator); return result; } 11777 11780 11778 11781 jsoff_t done_off = lkp(js, result, "done", 4); ··· 11783 11786 jsval_t value = value_off ? loadval(js, value_off + sizeof(jsoff_t) * 2) : js_mkundef(); 11784 11787 11785 11788 iter_action_t action = cb(js, value, ctx, &out); 11786 - if (action == ITER_BREAK) break; 11787 - if (action == ITER_ERROR) { js_unroot(js, h_iterator); return out; } 11789 + if (action == ITER_BREAK) { 11790 + jsval_t close_result = iter_close_iterator(js, cur_iter); 11791 + if (is_err(close_result)) { js_unroot(js, h_iterator); return close_result; } 11792 + break; 11793 + } 11794 + if (action == ITER_ERROR) { 11795 + jsval_t close_result = iter_close_iterator(js, cur_iter); 11796 + js_unroot(js, h_iterator); 11797 + if (is_err(close_result)) return close_result; 11798 + return out; 11799 + } 11788 11800 } 11789 11801 11790 11802 js_unroot(js, h_iterator); ··· 16884 16896 uint8_t t = vtype(obj); 16885 16897 16886 16898 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); 16887 - if (vtype(key) != T_STR) { 16888 - char buf[64]; 16889 - size_t n = tostr(js, key, buf, sizeof(buf)); 16890 - key = js_mkstr(js, buf, n); 16891 - } 16892 - 16893 16899 jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 16894 - jsoff_t key_len, key_off = vstr(js, key, &key_len); 16895 - const char *key_str = (char *) &js->mem[key_off]; 16896 - 16900 + 16901 + char sym_buf[64]; 16902 + const char *key_str = NULL; 16903 + jsoff_t key_len = 0; 16904 + if (vtype(key) == T_SYMBOL) { 16905 + int klen = sym_to_prop_key(key, sym_buf, sizeof(sym_buf)); 16906 + if (klen <= 0) return mkval(T_BOOL, 0); 16907 + key_str = sym_buf; 16908 + key_len = (jsoff_t)klen; 16909 + } else { 16910 + if (vtype(key) != T_STR) { 16911 + char buf[64]; 16912 + size_t n = tostr(js, key, buf, sizeof(buf)); 16913 + key = js_mkstr(js, buf, n); 16914 + } 16915 + jsoff_t key_off = vstr(js, key, &key_len); 16916 + key_str = (char *) &js->mem[key_off]; 16917 + } 16918 + 16919 + if (t == T_ARR && streq(key_str, key_len, "length", 6)) return mkval(T_BOOL, 1); 16920 + if (t == T_ARR && is_array_index(key_str, key_len)) { 16921 + unsigned long idx; 16922 + if (parse_array_index(key_str, key_len, get_array_length(js, obj), &idx)) { 16923 + return mkval(T_BOOL, arr_has(js, obj, (jsoff_t)idx) ? 1 : 0); 16924 + } 16925 + } 16926 + 16897 16927 jsoff_t off = lkp(js, as_obj, key_str, key_len); 16898 16928 return mkval(T_BOOL, off != 0 ? 1 : 0); 16899 16929 } ··· 16932 16962 uint8_t t = vtype(obj); 16933 16963 16934 16964 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); 16935 - if (vtype(key) != T_STR) { 16936 - char buf[64]; 16937 - size_t n = tostr(js, key, buf, sizeof(buf)); 16938 - key = js_mkstr(js, buf, n); 16939 - } 16940 - 16941 16965 jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 16942 - jsoff_t key_len, key_off = vstr(js, key, &key_len); 16943 - const char *key_str = (char *) &js->mem[key_off]; 16944 - 16966 + 16967 + char sym_buf[64]; 16968 + const char *key_str = NULL; 16969 + jsoff_t key_len = 0; 16970 + if (vtype(key) == T_SYMBOL) { 16971 + int klen = sym_to_prop_key(key, sym_buf, sizeof(sym_buf)); 16972 + if (klen <= 0) return mkval(T_BOOL, 0); 16973 + key_str = sym_buf; 16974 + key_len = (jsoff_t)klen; 16975 + } else { 16976 + if (vtype(key) != T_STR) { 16977 + char buf[64]; 16978 + size_t n = tostr(js, key, buf, sizeof(buf)); 16979 + key = js_mkstr(js, buf, n); 16980 + } 16981 + jsoff_t key_off = vstr(js, key, &key_len); 16982 + key_str = (char *) &js->mem[key_off]; 16983 + } 16984 + 16945 16985 if (t == T_ARR && streq(key_str, key_len, "length", 6)) { 16946 16986 return mkval(T_BOOL, 0); 16947 16987 } ··· 18650 18690 jsval_t write_target; 18651 18691 jsval_t result; 18652 18692 jsval_t mapFn; 18693 + jsval_t mapThis; 18653 18694 jsoff_t index; 18654 18695 } array_from_iter_ctx_t; 18655 18696 ··· 18659 18700 18660 18701 if (is_callable(fctx->mapFn)) { 18661 18702 jsval_t call_args[2] = { elem, tov((double)fctx->index) }; 18662 - elem = call_js_with_args(js, fctx->mapFn, call_args, 2); 18703 + elem = js_call_with_this(js, fctx->mapFn, fctx->mapThis, call_args, 2); 18663 18704 if (is_err(elem)) { *out = elem; return ITER_ERROR; } 18664 18705 } 18665 18706 ··· 18678 18719 18679 18720 jsval_t src = args[0]; 18680 18721 jsval_t mapFn = (nargs >= 2 && is_callable(args[1])) ? args[1] : js_mkundef(); 18722 + jsval_t mapThis = (nargs >= 3) ? args[2] : js_mkundef(); 18681 18723 18682 18724 jsval_t ctor = js->this_val; 18683 18725 bool use_ctor = (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC); ··· 18696 18738 jsval_t write_target = result_is_proxy ? proxy_read_target(js, result) : result; 18697 18739 18698 18740 if (vtype(src) == T_STR) { 18699 - array_from_iter_ctx_t ctx = { write_target, result, mapFn, 0 }; 18741 + array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; 18700 18742 jsoff_t str_len, str_off = vstr(js, src, &str_len); 18701 18743 const char *str_ptr = (const char *)&js->mem[str_off]; 18702 18744 for (jsoff_t i = 0; i < str_len; i++) { ··· 18710 18752 jsoff_t iter_prop = iter_key ? lkp_proto(js, src, iter_key, strlen(iter_key)) : 0; 18711 18753 18712 18754 if (iter_prop != 0) { 18713 - array_from_iter_ctx_t ctx = { write_target, result, mapFn, 0 }; 18755 + array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; 18714 18756 jsval_t iter_result = iter_foreach(js, src, array_from_iter_cb, &ctx); 18715 18757 if (is_err(iter_result)) return iter_result; 18716 18758 if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double) ctx.index)); 18717 18759 } else if (vtype(src) == T_ARR || vtype(src) == T_OBJ) { 18718 - array_from_iter_ctx_t ctx = { write_target, result, mapFn, 0 }; 18760 + array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; 18719 18761 jsoff_t len = get_array_length(js, src); 18720 18762 for (jsoff_t i = 0; i < len; i++) { 18721 18763 jsval_t unused;
+3
src/modules/io.c
··· 452 452 [SLOT_MODULE_SCOPE] = "MODULE_SCOPE", 453 453 [SLOT_STRICT_ARGS] = "STRICT_ARGS", 454 454 [SLOT_NO_FUNC_DECLS] = "NO_FUNC_DECLS", 455 + [SLOT_ITER_STATE] = "ITER_STATE", 456 + [SLOT_ENTRIES] = "ENTRIES", 457 + [SLOT_DENSE_BUF] = "DENSE_BUF", 455 458 }; 456 459 457 460 if (slot < sizeof(slot_names) / sizeof(slot_names[0]) && slot_names[slot]) {
+69 -8
src/modules/symbol.c
··· 20 20 static wellknown_sym_t g_observable = {0}; 21 21 static wellknown_sym_t g_toPrimitive = {0}; 22 22 static wellknown_sym_t g_species = {0}; 23 + static wellknown_sym_t g_unscopables = {0}; 24 + 25 + static jsval_t g_iterator_proto_obj = {0}; 26 + static jsval_t g_array_iterator_proto_obj = {0}; 27 + static jsval_t g_string_iterator_proto_obj = {0}; 23 28 24 29 const char *get_iterator_sym_key(void) { return g_iterator.key; } 25 30 const char *get_asyncIterator_sym_key(void) { return g_asyncIterator.key; } ··· 28 33 const char *get_toPrimitive_sym_key(void) { return g_toPrimitive.key; } 29 34 const char *get_hasInstance_sym_key(void) { return g_hasInstance.key; } 30 35 const char *get_species_sym_key(void) { return g_species.key; } 36 + const char *get_unscopables_sym_key(void) { return g_unscopables.key; } 31 37 32 38 static const struct { jsval_t *sym; const char *name; } sym_table[] = { 33 39 { &g_iterator.sym, "Symbol.iterator" }, ··· 37 43 { &g_observable.sym, "Symbol.observable" }, 38 44 { &g_toPrimitive.sym, "Symbol.toPrimitive" }, 39 45 { &g_species.sym, "Symbol.species" }, 46 + { &g_unscopables.sym, "Symbol.unscopables" }, 40 47 }; 41 48 42 49 bool is_symbol_key(const char *key, size_t key_len) { ··· 54 61 jsval_t get_wellknown_sym_by_key(const char *key, size_t key_len) { 55 62 static const struct { wellknown_sym_t *sym; } table[] = { 56 63 { &g_iterator }, { &g_asyncIterator }, { &g_toStringTag }, 57 - { &g_hasInstance }, { &g_observable }, { &g_toPrimitive }, { &g_species } 64 + { &g_hasInstance }, { &g_observable }, { &g_toPrimitive }, 65 + { &g_species }, { &g_unscopables } 58 66 }; 59 67 for (size_t i = 0; i < sizeof(table)/sizeof(table[0]); i++) { 60 68 if (table[i].sym->key[0] && strlen(table[i].sym->key) == key_len && ··· 149 157 return result; 150 158 } 151 159 160 + static jsval_t iter_return_this(struct js *js, jsval_t *args, int nargs) { 161 + return js->this_val; 162 + } 163 + 164 + static jsval_t get_iterator_prototype(struct js *js) { 165 + if (vtype(g_iterator_proto_obj) == T_OBJ) return g_iterator_proto_obj; 166 + 167 + g_iterator_proto_obj = js_mkobj(js); 168 + js_set_proto(js, g_iterator_proto_obj, js->object); 169 + if (g_iterator.key[0]) js_set(js, g_iterator_proto_obj, g_iterator.key, js_mkfun(iter_return_this)); 170 + 171 + return g_iterator_proto_obj; 172 + } 173 + 174 + static jsval_t get_array_iterator_prototype(struct js *js) { 175 + if (vtype(g_array_iterator_proto_obj) == T_OBJ) return g_array_iterator_proto_obj; 176 + 177 + jsval_t iterator_proto = get_iterator_prototype(js); 178 + g_array_iterator_proto_obj = js_mkobj(js); 179 + js_set(js, g_array_iterator_proto_obj, "next", js_mkfun(iterator_next)); 180 + js_set_proto(js, g_array_iterator_proto_obj, iterator_proto); 181 + 182 + return g_array_iterator_proto_obj; 183 + } 184 + 152 185 static jsval_t array_iterator(struct js *js, jsval_t *args, int nargs) { 153 186 jsval_t arr = js_getthis(js); 154 - 155 187 jsval_t len_val = js_get(js, arr, "length"); 156 188 int len = vtype(len_val) == T_NUM ? (int)js_getnum(len_val) : 0; 157 189 ··· 159 191 js_set(js, iter, "__arr", arr); 160 192 js_set(js, iter, "__idx", js_mknum(0)); 161 193 js_set(js, iter, "__len", js_mknum(len)); 162 - js_set(js, iter, "next", js_mkfun(iterator_next)); 194 + js_set_proto(js, iter, get_array_iterator_prototype(js)); 163 195 164 196 return iter; 165 197 } ··· 190 222 return result; 191 223 } 192 224 225 + static jsval_t get_string_iterator_prototype(struct js *js) { 226 + if (vtype(g_string_iterator_proto_obj) == T_OBJ) return g_string_iterator_proto_obj; 227 + 228 + jsval_t iterator_proto = get_iterator_prototype(js); 229 + g_string_iterator_proto_obj = js_mkobj(js); 230 + js_set(js, g_string_iterator_proto_obj, "next", js_mkfun(string_iterator_next)); 231 + js_set_proto(js, g_string_iterator_proto_obj, iterator_proto); 232 + 233 + return g_string_iterator_proto_obj; 234 + } 235 + 193 236 static jsval_t string_iterator(struct js *js, jsval_t *args, int nargs) { 194 - (void)args; (void)nargs; 195 237 jsval_t str = js_getthis(js); 196 - 197 - size_t len; 198 - js_getstr(js, str, &len); 238 + size_t len; js_getstr(js, str, &len); 199 239 200 240 jsval_t iter = js_mkobj(js); 201 241 js_set(js, iter, "__str", str); 202 242 js_set(js, iter, "__idx", js_mknum(0)); 203 243 js_set(js, iter, "__len", js_mknum((double)len)); 204 - js_set(js, iter, "next", js_mkfun(string_iterator_next)); 244 + js_set_proto(js, iter, get_string_iterator_prototype(js)); 205 245 206 246 return iter; 207 247 } ··· 244 284 245 285 void init_symbol_module(void) { 246 286 struct js *js = rt->js; 287 + 288 + g_iterator_proto_obj = js_mkundef(); 289 + g_array_iterator_proto_obj = js_mkundef(); 290 + g_string_iterator_proto_obj = js_mkundef(); 247 291 248 292 init_symbol(js, &g_iterator, "Symbol.iterator"); 249 293 init_symbol(js, &g_asyncIterator, "Symbol.asyncIterator"); ··· 252 296 init_symbol(js, &g_toPrimitive, "Symbol.toPrimitive"); 253 297 init_symbol(js, &g_hasInstance, "Symbol.hasInstance"); 254 298 init_symbol(js, &g_species, "Symbol.species"); 299 + init_symbol(js, &g_unscopables, "Symbol.unscopables"); 255 300 256 301 jsval_t symbol_proto = js_mkobj(js); 257 302 js_set(js, symbol_proto, "toString", js_mkfun(builtin_Symbol_toString)); ··· 269 314 js_set(js, symbol_ctor, "observable", g_observable.sym); 270 315 js_set(js, symbol_ctor, "toPrimitive", g_toPrimitive.sym); 271 316 js_set(js, symbol_ctor, "species", g_species.sym); 317 + js_set(js, symbol_ctor, "unscopables", g_unscopables.sym); 272 318 273 319 jsval_t func_symbol = js_obj_to_func(symbol_ctor); 274 320 js_set(js, js_glob(js), "Symbol", func_symbol); ··· 278 324 279 325 jsval_t array_ctor = js_get(js, js_glob(js), "Array"); 280 326 jsval_t array_proto = js_get(js, array_ctor, "prototype"); 327 + 328 + (void)get_array_iterator_prototype(js); 329 + (void)get_string_iterator_prototype(js); 281 330 js_set(js, array_proto, g_iterator.key, js_mkfun(array_iterator)); 331 + 332 + jsval_t array_unscopables = js_mkobj(js); 333 + js_set(js, array_unscopables, "find", js_true); 334 + js_set(js, array_unscopables, "findIndex", js_true); 335 + js_set(js, array_unscopables, "fill", js_true); 336 + js_set(js, array_unscopables, "copyWithin", js_true); 337 + js_set(js, array_unscopables, "entries", js_true); 338 + js_set(js, array_unscopables, "keys", js_true); 339 + js_set(js, array_unscopables, "values", js_true); 340 + js_set(js, array_unscopables, "flat", js_true); 341 + js_set(js, array_unscopables, "flatMap", js_true); 342 + js_set(js, array_proto, g_unscopables.key, array_unscopables); 282 343 283 344 jsval_t string_ctor = js_get(js, js_glob(js), "String"); 284 345 jsval_t string_proto = js_get(js, string_ctor, "prototype");