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.

misc es6 features

- implement Symbol.species support
- add Object.is
- add Number aliases for parseInt/parseFloat
- Promise race/all iterator support

+185 -97
-5
include/modules/symbol.h
··· 1 1 #ifndef SYMBOL_H 2 2 #define SYMBOL_H 3 3 4 - #include "types.h" 5 4 #include <stddef.h> 6 5 7 6 void init_symbol_module(void); 8 - 9 - jsval_t get_iterator_symbol(void); 10 - jsval_t get_asyncIterator_symbol(void); 11 - jsval_t get_observable_symbol(void); 12 7 13 8 const char *get_iterator_sym_key(void); 14 9 const char *get_asyncIterator_sym_key(void);
+172 -88
src/ant.c
··· 659 659 return (loadoff(js, off) & ARRMASK) != 0; 660 660 } 661 661 662 + static bool is_func_off(struct js *js, jsoff_t off) { 663 + jsval_t obj = mkval(T_OBJ, off); 664 + jsval_t code = get_slot(js, obj, SLOT_CODE); 665 + jsval_t cfunc = get_slot(js, obj, SLOT_CFUNC); 666 + return vtype(code) != T_UNDEF || vtype(cfunc) != T_UNDEF; 667 + } 668 + 662 669 static inline jsval_t loadval(struct js *js, jsoff_t off) { 663 670 return *(jsval_t *)(&js->mem[off]); 664 671 } ··· 5372 5379 return tov(D(js_arr_len(js, arr))); 5373 5380 } 5374 5381 5375 - jsval_t obj = mkval(T_OBJ, obj_off); 5382 + uint8_t obj_type = is_arr_off(js, obj_off) ? T_ARR : (is_func_off(js, obj_off) ? T_FUNC : T_OBJ); 5383 + jsval_t obj = mkval(obj_type, obj_off); 5376 5384 if (is_proxy(js, obj)) return proxy_get(js, obj, key_str, len); 5377 5385 5378 5386 if (len == STR_PROTO_LEN && memcmp(key_str, STR_PROTO, STR_PROTO_LEN) == 0) { ··· 5703 5711 if ((js->flags & F_STRICT) && (streq(keystr, keylen, "caller", 6) || streq(keystr, keylen, "arguments", 9))) { 5704 5712 return js_mkerr_typed(js, JS_ERR_TYPE, "'%.*s' not allowed on functions in strict mode", (int)keylen, keystr); 5705 5713 } 5714 + jsoff_t obj_off = (jsoff_t)vdata(obj); 5715 + descriptor_entry_t *desc = lookup_descriptor(obj_off, keystr, keylen); 5716 + if (desc && (desc->has_getter || desc->has_setter)) { 5717 + jsval_t key = js_mkstr(js, keystr, keylen); 5718 + return mkpropref(obj_off, (jsoff_t)vdata(key)); 5719 + } 5706 5720 jsval_t func_obj = mkval(T_OBJ, vdata(obj)); 5707 5721 jsoff_t off = lkp_proto(js, obj, keystr, keylen); 5708 5722 if (off != 0) { 5709 - jsoff_t obj_off = (jsoff_t)vdata(obj); 5710 - descriptor_entry_t *desc = lookup_descriptor(obj_off, keystr, keylen); 5711 5723 if (desc) { 5712 5724 jsval_t key = js_mkstr(js, keystr, keylen); 5713 5725 return mkpropref(obj_off, (jsoff_t)vdata(key)); ··· 5825 5837 if (vtype(proto) != T_UNDEF) return proto; 5826 5838 return get_prototype_for_type(js, T_FUNC); 5827 5839 } 5840 + jsoff_t obj_off = (jsoff_t)vdata(l); 5841 + descriptor_entry_t *desc = lookup_descriptor(obj_off, ptr, plen); 5842 + if (desc && (desc->has_getter || desc->has_setter)) { 5843 + jsval_t key = js_mkstr(js, ptr, plen); 5844 + return mkpropref(obj_off, (jsoff_t)vdata(key)); 5845 + } 5828 5846 jsval_t func_obj = mkval(T_OBJ, vdata(l)); 5829 5847 jsoff_t off = lkp_proto(js, l, ptr, plen); 5830 5848 if (off != 0) { 5831 - jsoff_t obj_off = (jsoff_t)vdata(l); 5832 - descriptor_entry_t *desc = lookup_descriptor(obj_off, ptr, plen); 5833 5849 if (desc) { 5834 5850 jsval_t key = js_mkstr(js, ptr, plen); 5835 5851 return mkpropref(obj_off, (jsoff_t)vdata(key)); ··· 14622 14638 return mkval(T_ARR, vdata(arr)); 14623 14639 } 14624 14640 14641 + static jsval_t builtin_object_is(struct js *js, jsval_t *args, int nargs) { 14642 + if (nargs < 2) return js_false; 14643 + 14644 + jsval_t x = args[0]; 14645 + jsval_t y = args[1]; 14646 + 14647 + uint8_t tx = vtype(x); 14648 + uint8_t ty = vtype(y); 14649 + if (tx != ty) return js_false; 14650 + 14651 + if (tx == T_UNDEF || tx == T_NULL) return js_true; 14652 + 14653 + if (tx == T_NUM) { 14654 + double dx = tod(x); 14655 + double dy = tod(y); 14656 + if (isnan(dx) && isnan(dy)) return js_true; 14657 + if (dx == 0.0 && dy == 0.0) { 14658 + bool x_neg = (1.0 / dx) < 0; 14659 + bool y_neg = (1.0 / dy) < 0; 14660 + return x_neg == y_neg ? js_true : js_false; 14661 + } 14662 + return dx == dy ? js_true : js_false; 14663 + } 14664 + 14665 + if (tx == T_BOOL) return vdata(x) == vdata(y) ? js_true : js_false; 14666 + 14667 + return x == y ? js_true : js_false; 14668 + } 14669 + 14625 14670 static jsval_t builtin_object_keys(struct js *js, jsval_t *args, int nargs) { 14626 14671 if (nargs == 0) return mkarr(js); 14627 14672 jsval_t obj = args[0]; ··· 15351 15396 uint8_t t = vtype(obj); 15352 15397 15353 15398 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_mkundef(); 15354 - if (vtype(key) != T_STR) { 15399 + 15400 + char sym_buf[32]; 15401 + const char *key_str; 15402 + jsoff_t key_len; 15403 + 15404 + if (vtype(key) == T_SYMBOL) { 15405 + snprintf(sym_buf, sizeof(sym_buf), "__sym_%llu__", (unsigned long long)sym_get_id(key)); 15406 + key_str = sym_buf; 15407 + key_len = (jsoff_t)strlen(sym_buf); 15408 + } else if (vtype(key) == T_STR) { 15409 + jsoff_t key_off = vstr(js, key, &key_len); 15410 + key_str = (char *) &js->mem[key_off]; 15411 + } else { 15355 15412 char buf[64]; 15356 15413 size_t n = tostr(js, key, buf, sizeof(buf)); 15357 15414 key = js_mkstr(js, buf, n); 15415 + jsoff_t key_off = vstr(js, key, &key_len); 15416 + key_str = (char *) &js->mem[key_off]; 15358 15417 } 15359 15418 15360 15419 jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 15361 - jsoff_t key_len, key_off = vstr(js, key, &key_len); 15362 - const char *key_str = (char *) &js->mem[key_off]; 15363 15420 15364 15421 jsoff_t obj_off = (jsoff_t)vdata(as_obj); 15365 15422 descriptor_entry_t *desc = lookup_descriptor(obj_off, key_str, key_len); ··· 19981 20038 return js_mkundef(); 19982 20039 } 19983 20040 19984 - static jsval_t builtin_Promise_all(struct js *js, jsval_t *args, int nargs) { 19985 - if (nargs < 1) return js_mkerr(js, "Promise.all requires an array"); 20041 + typedef struct { 20042 + jsval_t tracker; 20043 + int index; 20044 + } promise_all_iter_ctx_t; 20045 + 20046 + static iter_action_t promise_all_iter_cb(struct js *js, jsval_t value, void *ctx, jsval_t *out) { 20047 + promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx; 20048 + jsval_t item = value; 20049 + 20050 + if (vtype(item) != T_PROMISE) { 20051 + jsval_t wrap_args[] = { item }; 20052 + item = builtin_Promise_resolve(js, wrap_args, 1); 20053 + } 20054 + 20055 + jsval_t resolve_obj = mkobj(js, 0); 20056 + set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_resolve_handler)); 20057 + js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)pctx->index)); 20058 + js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), pctx->tracker); 20059 + jsval_t resolve_fn = mkval(T_FUNC, vdata(resolve_obj)); 19986 20060 19987 - jsval_t arr = args[0]; 19988 - if (vtype(arr) != T_ARR) return js_mkerr(js, "Promise.all requires an array"); 20061 + jsval_t reject_obj = mkobj(js, 0); 20062 + set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_reject_handler)); 20063 + js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), pctx->tracker); 20064 + jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 20065 + 20066 + jsval_t then_args[] = { resolve_fn, reject_fn }; 20067 + jsval_t saved_this = js->this_val; 20068 + js->this_val = item; 20069 + builtin_promise_then(js, then_args, 2); 20070 + js->this_val = saved_this; 19989 20071 19990 - jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 19991 - if (len_off == 0) return builtin_Promise_resolve(js, NULL, 0); 19992 - jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 19993 - int len = (int)tod(len_val); 20072 + pctx->index++; 20073 + return ITER_CONTINUE; 20074 + } 20075 + 20076 + static jsval_t builtin_Promise_all(struct js *js, jsval_t *args, int nargs) { 20077 + if (nargs < 1) return js_mkerr(js, "Promise.all requires an iterable"); 19994 20078 19995 - if (len == 0) { 19996 - jsval_t empty_arr = mkarr(js); 19997 - js_setprop(js, empty_arr, js_mkstr(js, "length", 6), tov(0.0)); 19998 - jsval_t resolve_args[] = { mkval(T_ARR, vdata(empty_arr)) }; 19999 - return builtin_Promise_resolve(js, resolve_args, 1); 20000 - } 20079 + jsval_t iterable = args[0]; 20080 + uint8_t t = vtype(iterable); 20081 + if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.all requires an iterable"); 20001 20082 20002 20083 jsval_t result_promise = mkpromise(js); 20003 20084 jsval_t tracker = mkobj(js, 0); 20085 + jsval_t results = mkarr(js); 20004 20086 20005 - js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); 20006 - js_setprop(js, tracker, js_mkstr(js, "results", 7), mkarr(js)); 20087 + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov(0.0)); 20088 + js_setprop(js, tracker, js_mkstr(js, "results", 7), results); 20007 20089 set_slot(js, tracker, SLOT_DATA, result_promise); 20008 20090 20009 - jsval_t results = resolveprop(js, js_get(js, tracker, "results")); 20091 + promise_all_iter_ctx_t ctx = { .tracker = tracker, .index = 0 }; 20092 + jsval_t iter_result = iter_foreach(js, iterable, promise_all_iter_cb, &ctx); 20093 + 20094 + if (is_err(iter_result)) return iter_result; 20095 + 20096 + int len = ctx.index; 20010 20097 js_setprop(js, results, js_mkstr(js, "length", 6), tov((double)len)); 20011 20098 20012 - for (int i = 0; i < len; i++) { 20013 - char idx[16]; 20014 - snprintf(idx, sizeof(idx), "%d", i); 20015 - jsval_t item = resolveprop(js, js_get(js, arr, idx)); 20016 - 20017 - if (vtype(item) != T_PROMISE) { 20018 - jsval_t wrap_args[] = { item }; 20019 - item = builtin_Promise_resolve(js, wrap_args, 1); 20099 + if (len == 0) { 20100 + jsval_t resolve_args[] = { mkval(T_ARR, vdata(results)) }; 20101 + return builtin_Promise_resolve(js, resolve_args, 1); 20102 + } 20103 + 20104 + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); 20105 + return result_promise; 20106 + } 20107 + 20108 + typedef struct { 20109 + jsval_t result_promise; 20110 + jsval_t resolve_fn; 20111 + jsval_t reject_fn; 20112 + bool settled; 20113 + } promise_race_iter_ctx_t; 20114 + 20115 + static iter_action_t promise_race_iter_cb(struct js *js, jsval_t value, void *ctx, jsval_t *out) { 20116 + promise_race_iter_ctx_t *pctx = (promise_race_iter_ctx_t *)ctx; 20117 + jsval_t item = value; 20118 + 20119 + if (vtype(item) != T_PROMISE) { 20120 + resolve_promise(js, pctx->result_promise, item); 20121 + pctx->settled = true; 20122 + return ITER_BREAK; 20123 + } 20124 + 20125 + uint32_t item_pid = get_promise_id(js, item); 20126 + promise_data_entry_t *pd = get_promise_data(item_pid, false); 20127 + if (pd) { 20128 + if (pd->state == 1) { 20129 + resolve_promise(js, pctx->result_promise, pd->value); 20130 + pctx->settled = true; 20131 + return ITER_BREAK; 20132 + } else if (pd->state == 2) { 20133 + reject_promise(js, pctx->result_promise, pd->value); 20134 + pctx->settled = true; 20135 + return ITER_BREAK; 20020 20136 } 20021 - 20022 - jsval_t resolve_obj = mkobj(js, 0); 20023 - set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_resolve_handler)); 20024 - js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)i)); 20025 - js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), tracker); 20026 - jsval_t resolve_fn = mkval(T_FUNC, vdata(resolve_obj)); 20027 - 20028 - jsval_t reject_obj = mkobj(js, 0); 20029 - set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_reject_handler)); 20030 - js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), tracker); 20031 - jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 20032 - 20033 - jsval_t then_args[] = { resolve_fn, reject_fn }; 20034 - jsval_t saved_this = js->this_val; 20035 - js->this_val = item; 20036 - builtin_promise_then(js, then_args, 2); 20037 - js->this_val = saved_this; 20038 20137 } 20039 20138 20040 - return result_promise; 20139 + jsval_t then_args[] = { pctx->resolve_fn, pctx->reject_fn }; 20140 + jsval_t saved_this = js->this_val; 20141 + js->this_val = item; 20142 + builtin_promise_then(js, then_args, 2); 20143 + js->this_val = saved_this; 20144 + 20145 + return ITER_CONTINUE; 20041 20146 } 20042 20147 20043 20148 static jsval_t builtin_Promise_race(struct js *js, jsval_t *args, int nargs) { 20044 - if (nargs < 1) return js_mkerr(js, "Promise.race requires an array"); 20045 - 20046 - jsval_t arr = args[0]; 20047 - if (vtype(arr) != T_ARR) return js_mkerr(js, "Promise.race requires an array"); 20149 + if (nargs < 1) return js_mkerr(js, "Promise.race requires an iterable"); 20048 20150 20049 - jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 20050 - if (len_off == 0) return mkpromise(js); 20051 - jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 20052 - int len = (int)tod(len_val); 20151 + jsval_t iterable = args[0]; 20152 + uint8_t t = vtype(iterable); 20153 + if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.race requires an iterable"); 20053 20154 20054 - if (len == 0) return mkpromise(js); 20055 20155 jsval_t result_promise = mkpromise(js); 20056 20156 20057 20157 jsval_t resolve_obj = mkobj(js, 0); ··· 20064 20164 set_slot(js, reject_obj, SLOT_DATA, result_promise); 20065 20165 jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 20066 20166 20067 - for (int i = 0; i < len; i++) { 20068 - char idx[16]; 20069 - snprintf(idx, sizeof(idx), "%d", i); 20070 - jsval_t item = resolveprop(js, js_get(js, arr, idx)); 20071 - 20072 - if (vtype(item) != T_PROMISE) { 20073 - resolve_promise(js, result_promise, item); 20074 - return result_promise; 20075 - } 20076 - 20077 - uint32_t item_pid = get_promise_id(js, item); 20078 - promise_data_entry_t *pd = get_promise_data(item_pid, false); 20079 - if (pd) { 20080 - if (pd->state == 1) { 20081 - resolve_promise(js, result_promise, pd->value); 20082 - return result_promise; 20083 - } else if (pd->state == 2) { 20084 - reject_promise(js, result_promise, pd->value); 20085 - return result_promise; 20086 - } 20087 - } 20088 - 20089 - jsval_t then_args[] = { resolve_fn, reject_fn }; 20090 - jsval_t saved_this = js->this_val; 20091 - js->this_val = item; 20092 - builtin_promise_then(js, then_args, 2); 20093 - js->this_val = saved_this; 20094 - } 20167 + promise_race_iter_ctx_t ctx = { 20168 + .result_promise = result_promise, 20169 + .resolve_fn = resolve_fn, 20170 + .reject_fn = reject_fn, 20171 + .settled = false 20172 + }; 20173 + 20174 + jsval_t iter_result = iter_foreach(js, iterable, promise_race_iter_cb, &ctx); 20175 + if (is_err(iter_result)) return iter_result; 20095 20176 20096 20177 return result_promise; 20097 20178 } ··· 21745 21826 js_setprop(js, obj_func_obj, js_mkstr(js, "keys", 4), js_mkfun(builtin_object_keys)); 21746 21827 js_setprop(js, obj_func_obj, js_mkstr(js, "values", 6), js_mkfun(builtin_object_values)); 21747 21828 js_setprop(js, obj_func_obj, js_mkstr(js, "entries", 7), js_mkfun(builtin_object_entries)); 21829 + js_setprop(js, obj_func_obj, js_mkstr(js, "is", 2), js_mkfun(builtin_object_is)); 21748 21830 js_setprop(js, obj_func_obj, js_mkstr(js, "getPrototypeOf", 14), js_mkfun(builtin_object_getPrototypeOf)); 21749 21831 js_setprop(js, obj_func_obj, js_mkstr(js, "setPrototypeOf", 14), js_mkfun(builtin_object_setPrototypeOf)); 21750 21832 js_setprop(js, obj_func_obj, js_mkstr(js, "create", 6), js_mkfun(builtin_object_create)); ··· 21810 21892 js_setprop(js, number_ctor_obj, js_mkstr(js, "isFinite", 8), js_mkfun(builtin_Number_isFinite)); 21811 21893 js_setprop(js, number_ctor_obj, js_mkstr(js, "isInteger", 9), js_mkfun(builtin_Number_isInteger)); 21812 21894 js_setprop(js, number_ctor_obj, js_mkstr(js, "isSafeInteger", 13), js_mkfun(builtin_Number_isSafeInteger)); 21895 + js_setprop(js, number_ctor_obj, js_mkstr(js, "parseInt", 8), js_mkfun(builtin_parseInt)); 21896 + js_setprop(js, number_ctor_obj, js_mkstr(js, "parseFloat", 10), js_mkfun(builtin_parseFloat)); 21813 21897 21814 21898 js_setprop(js, number_ctor_obj, js_mkstr(js, "MAX_VALUE", 9), tov(1.7976931348623157e+308)); 21815 21899 js_setprop(js, number_ctor_obj, js_mkstr(js, "MIN_VALUE", 9), tov(5e-324));
+13 -4
src/modules/symbol.c
··· 19 19 static wellknown_sym_t g_hasInstance = {0}; 20 20 static wellknown_sym_t g_observable = {0}; 21 21 static wellknown_sym_t g_toPrimitive = {0}; 22 - 23 - jsval_t get_iterator_symbol(void) { return g_iterator.sym; } 24 - jsval_t get_asyncIterator_symbol(void) { return g_asyncIterator.sym; } 25 - jsval_t get_observable_symbol(void) { return g_observable.sym; } 22 + static wellknown_sym_t g_species = {0}; 26 23 27 24 const char *get_iterator_sym_key(void) { return g_iterator.key; } 28 25 const char *get_asyncIterator_sym_key(void) { return g_asyncIterator.key; } ··· 38 35 { &g_hasInstance.sym, "Symbol.hasInstance" }, 39 36 { &g_observable.sym, "Symbol.observable" }, 40 37 { &g_toPrimitive.sym, "Symbol.toPrimitive" }, 38 + { &g_species.sym, "Symbol.species" }, 41 39 }; 42 40 43 41 const char *get_symbol_description_from_key(const char *sym_key, size_t key_len) { ··· 182 180 return iter; 183 181 } 184 182 183 + static jsval_t species_getter(struct js *js, jsval_t *args, int nargs) { 184 + (void)args; (void)nargs; 185 + return js_getthis(js); 186 + } 187 + 185 188 static jsval_t date_toPrimitive(struct js *js, jsval_t *args, int nargs) { 186 189 jsval_t this_val = js_getthis(js); 187 190 ··· 216 219 init_symbol(js, &g_observable, "Symbol.observable"); 217 220 init_symbol(js, &g_toPrimitive, "Symbol.toPrimitive"); 218 221 init_symbol(js, &g_hasInstance, "Symbol.hasInstance"); 222 + init_symbol(js, &g_species, "Symbol.species"); 219 223 220 224 jsval_t symbol_proto = js_mkobj(js); 221 225 js_set(js, symbol_proto, "toString", js_mkfun(builtin_Symbol_toString)); ··· 232 236 js_set(js, symbol_ctor, "hasInstance", g_hasInstance.sym); 233 237 js_set(js, symbol_ctor, "observable", g_observable.sym); 234 238 js_set(js, symbol_ctor, "toPrimitive", g_toPrimitive.sym); 239 + js_set(js, symbol_ctor, "species", g_species.sym); 235 240 236 241 jsval_t func_symbol = js_obj_to_func(symbol_ctor); 237 242 js_set(js, js_glob(js), "Symbol", func_symbol); ··· 254 259 jsval_t promise_ctor = js_get(js, js_glob(js), "Promise"); 255 260 jsval_t promise_proto = js_get(js, promise_ctor, "prototype"); 256 261 js_set(js, promise_proto, g_toStringTag.key, js_mkstr(js, "Promise", 7)); 262 + 263 + jsval_t species_fn = js_mkfun(species_getter); 264 + js_set_getter_desc(js, promise_ctor, g_species.key, strlen(g_species.key), species_fn, JS_DESC_C); 265 + js_set_getter_desc(js, array_ctor, g_species.key, strlen(g_species.key), species_fn, JS_DESC_C); 257 266 }