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.

RegExp species handling and related spec fixes

+490 -77
+490 -77
src/ant.c
··· 815 815 816 816 static jsval_t builtin_Object(struct js *js, jsval_t *args, int nargs); 817 817 static jsval_t builtin_promise_then(struct js *js, jsval_t *args, int nargs); 818 + static jsval_t string_split_impl(struct js *js, jsval_t str, jsval_t *args, int nargs); 819 + static jsval_t is_regexp_like(struct js *js, jsval_t value); 820 + static jsval_t should_regexp_passthrough(struct js *js, jsval_t *args, int nargs); 818 821 819 822 static jsval_t proxy_get(struct js *js, jsval_t proxy, const char *key, size_t key_len); 820 823 static jsval_t proxy_set(struct js *js, jsval_t proxy, const char *key, size_t key_len, jsval_t value); ··· 3556 3559 return proto_chain_contains(js, this_val, expected_proto); 3557 3560 } 3558 3561 3559 - static jsval_t array_constructor_from_receiver(struct js *js, jsval_t receiver) { 3560 - if (!is_object_type(receiver)) return js_mkundef(); 3562 + static jsval_t get_ctor_species_value(struct js *js, jsval_t ctor) { 3563 + const char *species_key = get_species_sym_key(); 3564 + if (!species_key || !species_key[0]) return js_mkundef(); 3565 + if (!is_object_type(ctor) && vtype(ctor) != T_CFUNC) return js_mkundef(); 3566 + return js_get(js, ctor, species_key); 3567 + } 3561 3568 3562 - jsval_t ctor = get_slot(js, receiver, SLOT_CTOR); 3563 - if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) { 3564 - ctor = js_get(js, receiver, "constructor"); 3565 - if (is_err(ctor)) return ctor; 3566 - if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) return js_mkundef(); 3569 + static inline bool same_ctor_identity(struct js *js, jsval_t a, jsval_t b) { 3570 + if (vtype(a) == vtype(b) && vdata(a) == vdata(b)) return true; 3571 + 3572 + if (vtype(a) == T_FUNC && vtype(b) == T_CFUNC) { 3573 + jsval_t c = get_slot(js, a, SLOT_CFUNC); 3574 + return vtype(c) == T_CFUNC && vdata(c) == vdata(b); 3567 3575 } 3568 - 3569 - const char *species_key = get_species_sym_key(); 3570 - if (species_key && species_key[0] != '\0') { 3571 - jsval_t species = js_get(js, ctor, species_key); 3572 - if (is_err(species)) return species; 3573 - if (vtype(species) == T_NULL) return js_mkundef(); 3574 - if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) return species; 3576 + 3577 + if (vtype(a) == T_CFUNC && vtype(b) == T_FUNC) { 3578 + jsval_t c = get_slot(js, b, SLOT_CFUNC); 3579 + return vtype(c) == T_CFUNC && vdata(c) == vdata(a); 3575 3580 } 3576 - 3577 - return ctor; 3581 + 3582 + if (vtype(a) == T_FUNC && vtype(b) == T_FUNC) { 3583 + jsval_t ca = get_slot(js, a, SLOT_CFUNC); 3584 + jsval_t cb = get_slot(js, b, SLOT_CFUNC); 3585 + if (vtype(ca) == T_CFUNC && vtype(cb) == T_CFUNC && vdata(ca) == vdata(cb)) return true; 3586 + } 3587 + 3588 + return false; 3578 3589 } 3579 3590 3580 - static jsval_t array_alloc_from_ctor(struct js *js, jsval_t ctor) { 3581 - jsval_t result = mkarr(js); 3582 - if (is_err(result)) return result; 3583 - 3584 - if (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC) { 3585 - jsval_t proto = js_get(js, ctor, "prototype"); 3586 - if (is_err(proto)) return proto; 3587 - if (is_object_type(proto)) set_proto(js, result, proto); 3588 - set_slot(js, result, SLOT_CTOR, ctor); 3591 + static jsval_t array_constructor_from_receiver(struct js *js, jsval_t receiver) { 3592 + if (!is_object_type(receiver)) return js_mkundef(); 3593 + 3594 + jsval_t species_source = receiver; 3595 + if (is_proxy(js, species_source)) { 3596 + species_source = proxy_read_target(js, species_source); 3589 3597 } 3598 + 3599 + bool receiver_is_array = (vtype(species_source) == T_ARR); 3600 + if (!receiver_is_array) { 3601 + jsval_t array_proto = get_ctor_proto(js, "Array", 5); 3602 + if (is_object_type(array_proto) && is_object_type(species_source)) { 3603 + receiver_is_array = proto_chain_contains(js, species_source, array_proto); 3604 + } 3605 + } 3606 + if (!receiver_is_array) return js_mkundef(); 3590 3607 3591 - return result; 3608 + jsval_t ctor = js_getprop_fallback(js, receiver, "constructor"); 3609 + if (is_err(ctor)) return ctor; 3610 + 3611 + jsval_t species = get_ctor_species_value(js, ctor); 3612 + if (is_err(species)) return species; 3613 + 3614 + if (vtype(species) == T_NULL) return js_mkundef(); 3615 + if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) return species; 3616 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) return js_mkundef(); 3617 + 3618 + return ctor; 3592 3619 } 3593 3620 3594 3621 static jsval_t array_alloc_from_ctor_with_length(struct js *js, jsval_t ctor, jsoff_t length_hint) { ··· 3613 3640 jsval_t result = is_object_type(constructed) ? constructed : seed; 3614 3641 set_slot(js, mkval(T_OBJ, vdata(result)), SLOT_CTOR, ctor); 3615 3642 return result; 3643 + } 3644 + 3645 + static inline jsval_t array_alloc_from_ctor(struct js *js, jsval_t ctor) { 3646 + return array_alloc_from_ctor_with_length(js, ctor, 0); 3616 3647 } 3617 3648 3618 3649 static jsval_t array_alloc_like(struct js *js, jsval_t receiver) { ··· 5616 5647 jsoff_t saved_tlen = js->tlen; 5617 5648 5618 5649 jsval_t saved_this = js->this_val; 5619 - js->this_val = prim; 5620 - push_this(prim); 5650 + js->this_val = prim; push_this(prim); 5621 5651 jsval_t result = call_js_with_args(js, accessor, arg, arg_count); 5622 - pop_this(); 5623 - js->this_val = saved_this; 5652 + pop_this(); js->this_val = saved_this; 5653 + 5654 + bool had_throw = (js->flags & F_THROW) != 0; 5655 + jsval_t thrown = js->thrown_value; 5624 5656 5625 5657 JS_RESTORE_STATE(js, saved); 5626 5658 js->flags = saved_flags; 5627 5659 js->toff = saved_toff; 5628 5660 js->tlen = saved_tlen; 5661 + 5662 + if (had_throw) { 5663 + js->flags |= F_THROW; 5664 + js->thrown_value = thrown; 5665 + } 5629 5666 5630 5667 if (is_setter) return is_err(result) ? result : (arg ? *arg : js_mkundef()); 5631 5668 return result; ··· 5784 5821 return entry->deleter(js, obj, key, key_len); 5785 5822 } 5786 5823 5824 + static jsval_t with_scope_name_blocked(struct js *js, jsval_t with_obj, const char *key, size_t key_len) { 5825 + const char *unscopables_key = get_unscopables_sym_key(); 5826 + if (!unscopables_key || !unscopables_key[0]) return js_false; 5827 + 5828 + jsval_t unscopables = js_get(js, with_obj, unscopables_key); 5829 + if (is_err(unscopables)) return unscopables; 5830 + if (!is_object_type(unscopables)) return js_false; 5831 + 5832 + jsoff_t prop_off = lkp_interned(js, unscopables, intern_string(key, key_len), key_len); 5833 + if (prop_off == 0) prop_off = lkp_proto(js, unscopables, key, key_len); 5834 + if (prop_off == 0) return js_false; 5835 + 5836 + jsval_t blocked = resolveprop(js, mkval(T_PROP, prop_off)); 5837 + if (is_err(blocked)) return blocked; 5838 + return js_bool(js_truthy(js, blocked)); 5839 + } 5840 + 5787 5841 static jsval_t lookup(struct js *js, const char *buf, size_t len) { 5788 5842 if (js->flags & F_NOEXEC) return 0; 5789 5843 ··· 5803 5857 } 5804 5858 5805 5859 const char *key_intern = intern_string(key_str, key_len); 5806 - 5807 5860 jsval_t parent_scope = upper(js, js->scope); 5808 5861 5809 5862 jsoff_t off = lkp_interned(js, js->scope, key_intern, key_len); 5810 - if (off != 0) { 5811 - return mkval(T_PROP, off); 5812 - } 5863 + if (off != 0) return mkval(T_PROP, off); 5813 5864 5814 5865 jsval_t with_slot = get_slot(js, js->scope, SLOT_WITH); 5815 5866 if (vtype(with_slot) != T_UNDEF) { ··· 5822 5873 5823 5874 jsoff_t prop_off = lkp_interned(js, with_obj, key_intern, key_len); 5824 5875 if (prop_off != 0) { 5876 + jsval_t blocked = with_scope_name_blocked(js, with_obj, key_str, key_len); 5877 + if (is_err(blocked)) return blocked; 5878 + if (js_truthy(js, blocked)) goto check_parent_scopes; 5825 5879 jsval_t key = js_mkstr(js, key_str, key_len); 5826 5880 if (is_err(key)) return key; 5827 5881 return mkpropref((jsoff_t)vdata(with_obj), (jsoff_t)vdata(key)); 5828 5882 } 5829 5883 } 5830 - 5884 + 5885 + check_parent_scopes: 5831 5886 uint8_t depth = 1; 5832 5887 for (jsval_t scope = parent_scope; depth < 255; depth++) { 5833 5888 off = lkp_interned(js, scope, key_intern, key_len); ··· 5846 5901 5847 5902 jsoff_t prop_off = lkp_interned(js, with_obj, key_intern, key_len); 5848 5903 if (prop_off != 0) { 5904 + jsval_t blocked = with_scope_name_blocked(js, with_obj, key_str, key_len); 5905 + if (is_err(blocked)) return blocked; 5906 + if (js_truthy(js, blocked)) goto continue_scope_walk; 5849 5907 jsval_t key = js_mkstr(js, key_str, key_len); 5850 5908 if (is_err(key)) return key; 5851 5909 return mkpropref((jsoff_t)vdata(with_obj), (jsoff_t)vdata(key)); 5852 5910 } 5853 5911 } 5854 - 5912 + continue_scope_walk: 5855 5913 if (vdata(scope) == 0) break; 5856 5914 scope = upper(js, scope); 5857 5915 } ··· 5939 5997 proto = get_prototype_for_type(js, T_ARR); 5940 5998 } 5941 5999 6000 + jsoff_t prop_off = lkp(js, obj, key_str, len); 6001 + if (prop_off != 0) return resolveprop(js, mkval(T_PROP, prop_off)); 6002 + 5942 6003 if (is_object_type(proto)) { 5943 6004 const char *key_intern = intern_string(key_str, len); 5944 6005 if (key_intern) { ··· 5949 6010 jsoff_t proto_off = lkp_proto(js, proto, key_str, len); 5950 6011 if (proto_off != 0) return resolveprop(js, mkval(T_PROP, proto_off)); 5951 6012 } 5952 - 5953 - jsoff_t prop_off = lkp(js, obj, key_str, len); 5954 - if (prop_off != 0) return resolveprop(js, mkval(T_PROP, prop_off)); 5955 6013 5956 6014 jsval_t dyn_result = try_dynamic_getter(js, obj, key_str, len); 5957 6015 if (vtype(dyn_result) != T_UNDEF) return dyn_result; ··· 7560 7618 js_setprop(js, arguments_obj, js_mkstr(js, toStringTag_key, strlen(toStringTag_key)), js_mkstr(js, "Arguments", 9)); 7561 7619 } 7562 7620 7621 + const char *iter_key = get_iterator_sym_key(); 7622 + if (iter_key && iter_key[0] != '\0') { 7623 + jsval_t array_proto = get_prototype_for_type(js, T_ARR); 7624 + jsval_t iter_fn = is_object_type(array_proto) ? js_get(js, array_proto, iter_key) : js_mkundef(); 7625 + if (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC) js_setprop( 7626 + js, arguments_obj, js_mkstr(js, iter_key, strlen(iter_key)), iter_fn 7627 + ); 7628 + } 7629 + 7563 7630 arguments_obj = mkval(T_ARR, vdata(arguments_obj)); 7564 7631 setprop_interned(js, scope, INTERN_ARGUMENTS, 9, arguments_obj); 7565 7632 ··· 8736 8803 L_TOK_LE: 8737 8804 L_TOK_GT: 8738 8805 L_TOK_GE: { 8806 + if (is_object_type(l) || is_object_type(r)) { 8807 + jsval_t lp = l, rp = r; 8808 + if (is_object_type(lp)) { 8809 + lp = js_to_primitive(js, lp, 2); 8810 + if (is_err(lp)) return lp; 8811 + } 8812 + if (is_object_type(rp)) { 8813 + rp = js_to_primitive(js, rp, 2); 8814 + if (is_err(rp)) return rp; 8815 + } 8816 + if (vtype(lp) != vtype(l) || vdata(lp) != vdata(l) || vtype(rp) != vtype(r) || vdata(rp) != vdata(r)) { 8817 + return do_op(js, op, lp, rp); 8818 + } 8819 + } 8820 + 8739 8821 uint8_t lt = vtype(l), rtype = vtype(r); 8740 8822 if (lt == T_NUM && rtype == T_NUM) { 8741 8823 double a = tod(l), b = tod(r); ··· 13312 13394 js->scope = saved_scope; 13313 13395 } else res = js_block_or_stmt(js); 13314 13396 13315 - js->flags = flags; 13397 + const uint8_t flow_mask = F_RETURN | F_THROW | F_BREAK | F_BREAK_LABEL | F_CONTINUE_LABEL; 13398 + js->flags = (flags & (uint8_t)~flow_mask) | (js->flags & flow_mask); 13399 + 13316 13400 return res; 13317 13401 } 13318 13402 ··· 14980 15064 } 14981 15065 14982 15066 static jsval_t builtin_RegExp(struct js *js, jsval_t *args, int nargs) { 14983 - if (vtype(js->new_target) == T_UNDEF && nargs > 0 && (nargs < 2 || vtype(args[1]) == T_UNDEF) && is_object_type(args[0])) { 14984 - jsval_t regexp_proto = get_ctor_proto(js, "RegExp", 6); 14985 - if (is_object_type(regexp_proto) && proto_chain_contains(js, args[0], regexp_proto)) return args[0]; 14986 - } 15067 + jsval_t passthrough = should_regexp_passthrough(js, args, nargs); 15068 + if (is_err(passthrough)) return passthrough; 15069 + if (js_truthy(js, passthrough)) return args[0]; 14987 15070 14988 15071 jsval_t regexp_obj = js->this_val; 14989 15072 bool use_this = (vtype(js->new_target) != T_UNDEF && vtype(regexp_obj) == T_OBJ); ··· 15241 15324 return ret; 15242 15325 } 15243 15326 15327 + static jsval_t maybe_call_symbol_method( 15328 + struct js *js, jsval_t target, 15329 + const char *symbol_key, 15330 + jsval_t this_arg, jsval_t *args, 15331 + int nargs, bool *called 15332 + ) { 15333 + *called = false; 15334 + if (!symbol_key || !symbol_key[0] || !is_object_type(target)) return js_mkundef(); 15335 + 15336 + jsval_t method = js_get(js, target, symbol_key); 15337 + if (is_err(method)) return method; 15338 + 15339 + uint8_t mt = vtype(method); 15340 + if (mt == T_UNDEF || mt == T_NULL) return js_mkundef(); 15341 + if (mt != T_FUNC && mt != T_CFUNC) { 15342 + return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol method is not callable"); 15343 + } 15344 + 15345 + *called = true; 15346 + return js_call_with_this(js, method, this_arg, args, nargs); 15347 + } 15348 + 15349 + static jsval_t is_regexp_like(struct js *js, jsval_t value) { 15350 + if (!is_object_type(value)) return js_false; 15351 + 15352 + const char *match_key = get_match_sym_key(); 15353 + if (match_key && match_key[0]) { 15354 + jsval_t match_val = js_get(js, value, match_key); 15355 + if (is_err(match_val)) return match_val; 15356 + if (vtype(match_val) != T_UNDEF) return js_bool(js_truthy(js, match_val)); 15357 + } 15358 + 15359 + jsval_t regexp_ctor = js_get(js, js_glob(js), "RegExp"); 15360 + if (is_err(regexp_ctor)) return regexp_ctor; 15361 + 15362 + jsval_t regexp_proto = js_get(js, regexp_ctor, "prototype"); 15363 + if (is_err(regexp_proto)) return regexp_proto; 15364 + if (!is_object_type(regexp_proto)) return js_false; 15365 + 15366 + return js_bool(proto_chain_contains(js, value, regexp_proto)); 15367 + } 15368 + 15369 + static jsval_t should_regexp_passthrough(struct js *js, jsval_t *args, int nargs) { 15370 + if (vtype(js->new_target) != T_UNDEF) return js_false; 15371 + if (nargs <= 0) return js_false; 15372 + 15373 + if (nargs >= 2 && vtype(args[1]) != T_UNDEF) return js_false; 15374 + if (!is_object_type(args[0])) return js_false; 15375 + 15376 + jsval_t ctor = js_getprop_fallback(js, args[0], "constructor"); 15377 + if (is_err(ctor)) return ctor; 15378 + 15379 + jsval_t regexp_ctor = js_get(js, js_glob(js), "RegExp"); 15380 + if (is_err(regexp_ctor)) return regexp_ctor; 15381 + 15382 + return js_bool(same_ctor_identity(js, ctor, regexp_ctor)); 15383 + } 15384 + 15385 + static jsval_t reject_regexp_arg(struct js *js, jsval_t value, const char *method_name) { 15386 + jsval_t is_re = is_regexp_like(js, value); 15387 + if (is_err(is_re)) return is_re; 15388 + if (js_truthy(js, is_re)) { 15389 + return js_mkerr_typed(js, JS_ERR_TYPE, "First argument to %s must not be a RegExp", method_name); 15390 + } 15391 + return js_mkundef(); 15392 + } 15393 + 15394 + jsval_t builtin_regexp_symbol_split(struct js *js, jsval_t *args, int nargs) { 15395 + jsval_t regexp = js_getthis(js); 15396 + if (!is_object_type(regexp)) { 15397 + return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.prototype[@@split] called on non-object"); 15398 + } 15399 + 15400 + jsval_t ctor = js_get(js, regexp, "constructor"); 15401 + if (is_err(ctor)) return ctor; 15402 + 15403 + jsval_t species = get_ctor_species_value(js, ctor); 15404 + if (is_err(species)) return species; 15405 + 15406 + if (vtype(species) != T_UNDEF && vtype(species) != T_NULL && 15407 + vtype(species) != T_FUNC && vtype(species) != T_CFUNC) { 15408 + return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp species is not a constructor"); 15409 + } 15410 + 15411 + jsval_t flags_val = js_get(js, regexp, "flags"); 15412 + if (is_err(flags_val)) return flags_val; 15413 + jsval_t exec_val = js_get(js, regexp, "exec"); 15414 + if (is_err(exec_val)) return exec_val; 15415 + (void)flags_val; 15416 + (void)exec_val; 15417 + 15418 + jsval_t str = nargs > 0 ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0); 15419 + if (is_err(str)) return str; 15420 + 15421 + jsval_t split_args[2]; 15422 + int split_nargs = 1; 15423 + 15424 + split_args[0] = regexp; 15425 + if (nargs >= 2) { 15426 + split_args[1] = args[1]; 15427 + split_nargs = 2; 15428 + } 15429 + 15430 + return string_split_impl(js, str, split_args, split_nargs); 15431 + } 15432 + 15244 15433 static jsval_t builtin_string_search(struct js *js, jsval_t *args, int nargs) { 15245 15434 jsval_t this_unwrapped = unwrap_primitive(js, js->this_val); 15246 15435 jsval_t str = js_tostring_val(js, this_unwrapped); 15247 15436 if (is_err(str)) return str; 15248 15437 if (nargs < 1) return tov(-1); 15438 + 15439 + if (is_object_type(args[0])) { 15440 + bool called = false; 15441 + jsval_t call_args[1] = { str }; 15442 + jsval_t dispatched = maybe_call_symbol_method( 15443 + js, args[0], get_search_sym_key(), args[0], call_args, 1, &called 15444 + ); 15445 + if (is_err(dispatched)) return dispatched; 15446 + if (called) return dispatched; 15447 + } 15249 15448 15250 15449 jsval_t pattern = args[0]; 15251 15450 const char *pattern_ptr = NULL; ··· 17277 17476 return js_mkstr(js, "[object Error]", 14); 17278 17477 } 17279 17478 17479 + // refactor 17480 + if (t == T_STR || t == T_NUM || t == T_BOOL) { 17481 + const char *tostr_tag_key = get_toStringTag_sym_key(); 17482 + jsval_t proto = get_prototype_for_type(js, t); 17483 + if (tostr_tag_key && tostr_tag_key[0] && is_object_type(proto)) { 17484 + jsoff_t tag_off = lkp(js, proto, tostr_tag_key, strlen(tostr_tag_key)); 17485 + if (tag_off == 0) tag_off = lkp_proto(js, proto, tostr_tag_key, strlen(tostr_tag_key)); 17486 + if (tag_off != 0) { 17487 + jsval_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); 17488 + if (vtype(tag_val) == T_STR) { 17489 + jsoff_t tag_len, tag_str_off = vstr(js, tag_val, &tag_len); 17490 + const char *tag_str = (const char *)&js->mem[tag_str_off]; 17491 + char buf[256]; 17492 + int n = snprintf(buf, sizeof(buf), "[object %.*s]", (int)tag_len, tag_str); 17493 + return js_mkstr(js, buf, n); 17494 + } 17495 + } 17496 + } 17497 + } 17498 + 17280 17499 const char *type_name = NULL; 17281 17500 17282 17501 switch (t) { ··· 17947 18166 if (is_err(result)) return result; 17948 18167 17949 18168 jsoff_t result_idx = 0; 17950 - jsoff_t len = get_array_length(js, arr); 17951 - 17952 - for (jsoff_t i = 0; i < len; i++) { 17953 - jsval_t elem = arr_get(js, arr, i); 17954 - arr_set(js, result, result_idx, elem); 17955 - result_idx++; 17956 - } 17957 - 17958 - for (int a = 0; a < nargs; a++) { 17959 - jsval_t arg = args[a]; 18169 + for (int a = -1; a < nargs; a++) { 18170 + jsval_t arg = (a < 0) ? arr : args[a]; 18171 + bool spreadable = false; 18172 + 17960 18173 if (vtype(arg) == T_ARR || vtype(arg) == T_OBJ) { 17961 - jsoff_t arg_len = get_array_length(js, arg); 18174 + bool array_default_spreadable = (vtype(arg) == T_ARR); 18175 + if (!array_default_spreadable && is_proxy(js, arg)) { 18176 + jsval_t target = proxy_read_target(js, arg); 18177 + array_default_spreadable = (vtype(target) == T_ARR); 18178 + } 18179 + 18180 + const char *spread_key = get_isConcatSpreadable_sym_key(); 18181 + if (spread_key && spread_key[0]) { 18182 + jsval_t spread_val = js_get(js, arg, spread_key); 18183 + if (is_err(spread_val)) return spread_val; 18184 + if (vtype(spread_val) == T_UNDEF) spreadable = array_default_spreadable; 18185 + else spreadable = js_truthy(js, spread_val); 18186 + } else spreadable = array_default_spreadable; 18187 + } 18188 + 18189 + if (spreadable) { 18190 + jsoff_t arg_len = 0; 18191 + jsval_t len_val = js_get(js, arg, "length"); 18192 + if (is_err(len_val)) return len_val; 18193 + if (vtype(len_val) == T_NUM && tod(len_val) > 0) arg_len = (jsoff_t)tod(len_val); 17962 18194 17963 18195 for (jsoff_t i = 0; i < arg_len; i++) { 17964 - jsval_t elem = arr_get(js, arg, i); 18196 + char idxstr[32]; 18197 + uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i); 18198 + jsval_t elem = js_get(js, arg, idxstr); 18199 + if (is_err(elem)) return elem; 17965 18200 arr_set(js, result, result_idx, elem); 17966 18201 result_idx++; 17967 18202 } ··· 19117 19352 return js_mkstr(js, str_ptr + byte_start, byte_end - byte_start); 19118 19353 } 19119 19354 19120 - static jsval_t builtin_string_split(struct js *js, jsval_t *args, int nargs) { 19121 - jsval_t str = to_string_val(js, js->this_val); 19355 + static jsval_t string_split_impl(struct js *js, jsval_t str, jsval_t *args, int nargs) { 19122 19356 if (vtype(str) != T_STR) return js_mkerr(js, "split called on non-string"); 19123 19357 jsoff_t str_len, str_off = vstr(js, str, &str_len); 19124 19358 const char *str_ptr = (char *) &js->mem[str_off]; ··· 19298 19532 return mkval(T_ARR, vdata(arr)); 19299 19533 } 19300 19534 19535 + static jsval_t builtin_string_split(struct js *js, jsval_t *args, int nargs) { 19536 + jsval_t str = to_string_val(js, js->this_val); 19537 + if (vtype(str) != T_STR) return js_mkerr(js, "split called on non-string"); 19538 + 19539 + if (nargs > 0 && is_object_type(args[0])) { 19540 + bool called = false; 19541 + jsval_t call_args[2]; 19542 + int call_nargs = 1; 19543 + call_args[0] = str; 19544 + if (nargs >= 2) { 19545 + call_args[1] = args[1]; 19546 + call_nargs = 2; 19547 + } 19548 + jsval_t dispatched = maybe_call_symbol_method( 19549 + js, args[0], get_split_sym_key(), args[0], call_args, call_nargs, &called 19550 + ); 19551 + if (is_err(dispatched)) return dispatched; 19552 + if (called) return dispatched; 19553 + } 19554 + 19555 + return string_split_impl(js, str, args, nargs); 19556 + } 19557 + 19301 19558 static jsval_t builtin_string_slice(struct js *js, jsval_t *args, int nargs) { 19302 19559 jsval_t this_unwrapped = unwrap_primitive(js, js->this_val); 19303 19560 jsval_t str = js_tostring_val(js, this_unwrapped); 19304 19561 if (is_err(str)) return str; 19562 + 19305 19563 jsoff_t byte_len, str_off = vstr(js, str, &byte_len); 19306 19564 const char *str_ptr = (char *) &js->mem[str_off]; 19307 19565 size_t utf16_len = utf16_strlen(str_ptr, byte_len); ··· 19312 19570 double d = tod(args[0]); 19313 19571 if (d < 0) { 19314 19572 start = (jsoff_t) (d + dstr_len < 0 ? 0 : d + dstr_len); 19315 - } else { 19316 - start = (jsoff_t) (d > dstr_len ? dstr_len : d); 19317 - } 19573 + } else start = (jsoff_t) (d > dstr_len ? dstr_len : d); 19318 19574 } 19575 + 19319 19576 if (nargs >= 2 && vtype(args[1]) == T_NUM) { 19320 19577 double d = tod(args[1]); 19321 19578 if (d < 0) { 19322 19579 end = (jsoff_t) (d + dstr_len < 0 ? 0 : d + dstr_len); 19323 - } else { 19324 - end = (jsoff_t) (d > dstr_len ? dstr_len : d); 19325 - } 19580 + } else end = (jsoff_t) (d > dstr_len ? dstr_len : d); 19326 19581 } 19327 19582 19328 19583 if (start > end) start = end; ··· 19337 19592 if (nargs == 0) return mkval(T_BOOL, 0); 19338 19593 jsval_t search = args[0]; 19339 19594 19340 - if (vtype(search) != T_STR) return mkval(T_BOOL, 0); 19595 + if (is_object_type(search)) { 19596 + jsval_t maybe_err = reject_regexp_arg(js, search, "includes"); 19597 + if (is_err(maybe_err)) return maybe_err; 19598 + } 19599 + search = js_tostring_val(js, search); 19600 + if (is_err(search)) return search; 19601 + 19341 19602 jsoff_t str_len, str_off = vstr(js, str, &str_len); 19342 19603 jsoff_t search_len, search_off = vstr(js, search, &search_len); 19343 19604 const char *str_ptr = (char *) &js->mem[str_off]; ··· 19366 19627 if (nargs == 0) return mkval(T_BOOL, 0); 19367 19628 jsval_t search = args[0]; 19368 19629 19369 - if (vtype(search) != T_STR) return mkval(T_BOOL, 0); 19630 + if (is_object_type(search)) { 19631 + jsval_t maybe_err = reject_regexp_arg(js, search, "startsWith"); 19632 + if (is_err(maybe_err)) return maybe_err; 19633 + } 19634 + search = js_tostring_val(js, search); 19635 + if (is_err(search)) return search; 19636 + 19370 19637 jsoff_t str_len, str_off = vstr(js, str, &str_len); 19371 19638 jsoff_t search_len, search_off = vstr(js, search, &search_len); 19372 19639 const char *str_ptr = (char *) &js->mem[str_off]; ··· 19384 19651 if (nargs == 0) return mkval(T_BOOL, 0); 19385 19652 jsval_t search = args[0]; 19386 19653 19387 - if (vtype(search) != T_STR) return mkval(T_BOOL, 0); 19654 + if (is_object_type(search)) { 19655 + jsval_t maybe_err = reject_regexp_arg(js, search, "endsWith"); 19656 + if (is_err(maybe_err)) return maybe_err; 19657 + } 19658 + search = js_tostring_val(js, search); 19659 + if (is_err(search)) return search; 19660 + 19388 19661 jsoff_t str_len, str_off = vstr(js, str, &str_len); 19389 19662 jsoff_t search_len, search_off = vstr(js, search, &search_len); 19390 19663 ··· 19401 19674 jsval_t this_unwrapped = unwrap_primitive(js, js->this_val); 19402 19675 jsval_t str = js_tostring_val(js, this_unwrapped); 19403 19676 if (is_err(str)) return str; 19677 + if (nargs < 1) return str; 19678 + 19679 + if (is_object_type(args[0])) { 19680 + bool called = false; 19681 + jsval_t replacement_arg = nargs > 1 ? args[1] : js_mkundef(); 19682 + jsval_t call_args[2] = { str, replacement_arg }; 19683 + jsval_t dispatched = maybe_call_symbol_method( 19684 + js, args[0], get_replace_sym_key(), args[0], call_args, 2, &called 19685 + ); 19686 + if (is_err(dispatched)) return dispatched; 19687 + if (called) return dispatched; 19688 + } 19404 19689 if (nargs < 2) return str; 19405 19690 19406 19691 jsval_t search = args[0]; ··· 19890 20175 jsval_t str = js_tostring_val(js, this_unwrapped); 19891 20176 if (is_err(str)) return str; 19892 20177 if (nargs < 1) return js_mknull(); 20178 + 20179 + if (is_object_type(args[0])) { 20180 + bool called = false; 20181 + jsval_t call_args[1] = { str }; 20182 + jsval_t dispatched = maybe_call_symbol_method( 20183 + js, args[0], get_match_sym_key(), args[0], call_args, 1, &called 20184 + ); 20185 + if (is_err(dispatched)) return dispatched; 20186 + if (called) return dispatched; 20187 + } 19893 20188 19894 20189 jsval_t pattern = args[0]; 19895 20190 const char *pattern_ptr = NULL; ··· 21185 21480 return p; 21186 21481 } 21187 21482 21483 + static jsval_t promise_species_noop_executor(struct js *js, jsval_t *args, int nargs) { 21484 + return js_mkundef(); 21485 + } 21486 + 21188 21487 static jsval_t builtin_promise_then(struct js *js, jsval_t *args, int nargs) { 21189 21488 jsval_t p = js->this_val; 21190 21489 if (vtype(p) != T_PROMISE) return js_mkerr(js, "not a promise"); 21191 21490 21192 - jsval_t nextP = mkpromise(js); 21491 + jsval_t promise_ctor = js_get(js, js_glob(js), "Promise"); 21492 + jsval_t species_ctor = promise_ctor; 21493 + jsval_t p_obj = mkval(T_OBJ, vdata(p)); 21494 + jsval_t ctor = js_get(js, p_obj, "constructor"); 21495 + 21496 + if (is_err(ctor)) return ctor; 21497 + if (vtype(ctor) == T_UNDEF) ctor = get_slot(js, p_obj, SLOT_CTOR); 21193 21498 21499 + jsval_t species = get_ctor_species_value(js, ctor); 21500 + if (is_err(species)) return species; 21501 + 21502 + if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) { 21503 + species_ctor = species; 21504 + } else if (vtype(species) == T_NULL) { 21505 + species_ctor = promise_ctor; 21506 + } else if (vtype(species) != T_UNDEF) { 21507 + return js_mkerr_typed(js, JS_ERR_TYPE, "Promise species is not a constructor"); 21508 + } 21509 + 21510 + if ((vtype(species_ctor) == T_FUNC || vtype(species_ctor) == T_CFUNC) 21511 + && !(vtype(species_ctor) == vtype(promise_ctor) 21512 + && vdata(species_ctor) == vdata(promise_ctor)) 21513 + ) { 21514 + jsval_t seed = js_mkobj(js); 21515 + if (is_err(seed)) return seed; 21516 + jsval_t species_proto = js_get(js, species_ctor, "prototype"); 21517 + 21518 + if (is_err(species_proto)) return species_proto; 21519 + if (is_object_type(species_proto)) set_proto(js, seed, species_proto); 21520 + 21521 + jsval_t exec = js_mkfun(promise_species_noop_executor); 21522 + jsval_t ctor_args[1] = { exec }; 21523 + jsval_t saved_new_target = js->new_target; 21524 + 21525 + js->new_target = species_ctor; 21526 + jsval_t constructed = js_call_with_this(js, species_ctor, seed, ctor_args, 1); 21527 + js->new_target = saved_new_target; 21528 + 21529 + if (is_err(constructed)) return constructed; 21530 + return is_object_type(constructed) ? constructed : seed; 21531 + } 21532 + 21533 + jsval_t nextP = mkpromise(js); 21194 21534 jsval_t p_proto = get_slot(js, mkval(T_OBJ, vdata(p)), SLOT_PROTO); 21535 + 21195 21536 if (vtype(p_proto) == T_OBJ) { 21196 21537 set_slot(js, mkval(T_OBJ, vdata(nextP)), SLOT_PROTO, p_proto); 21197 21538 jsval_t p_ctor = get_slot(js, mkval(T_OBJ, vdata(p)), SLOT_CTOR); ··· 21667 22008 } 21668 22009 21669 22010 const char *hi_key = get_hasInstance_sym_key(); 21670 - if (hi_key && *hi_key) proxy_get(js, r, hi_key, strlen(hi_key)); 22011 + if (hi_key && *hi_key) { 22012 + jsval_t has_instance = proxy_get(js, r, hi_key, strlen(hi_key)); 22013 + if (is_err(has_instance)) return has_instance; 22014 + uint8_t hit = vtype(has_instance); 22015 + if (hit == T_FUNC || hit == T_CFUNC) { 22016 + jsval_t args[1] = { l }; 22017 + jsval_t result = js_call_with_this(js, has_instance, r, args, 1); 22018 + if (is_err(result)) return result; 22019 + return js_bool(js_truthy(js, result)); 22020 + } 22021 + if (hit != T_UNDEF && hit != T_NULL) { 22022 + return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable"); 22023 + } 22024 + } 21671 22025 21672 22026 jsval_t proto_val = proxy_get(js, r, "prototype", 9); 21673 22027 uint8_t pt = vtype(proto_val); ··· 21730 22084 return handle_cfunc_instanceof(l, r, ltype); 21731 22085 } 21732 22086 22087 + const char *hi_key = get_hasInstance_sym_key(); 22088 + if (hi_key && hi_key[0]) { 22089 + jsval_t has_instance = js_get(js, r, hi_key); 22090 + if (is_err(has_instance)) return has_instance; 22091 + uint8_t hit = vtype(has_instance); 22092 + if (hit == T_FUNC || hit == T_CFUNC) { 22093 + jsval_t args[1] = { l }; 22094 + jsval_t result = js_call_with_this(js, has_instance, r, args, 1); 22095 + if (is_err(result)) return result; 22096 + return js_bool(js_truthy(js, result)); 22097 + } 22098 + if (hit != T_UNDEF && hit != T_NULL) { 22099 + return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable"); 22100 + } 22101 + } 22102 + 21733 22103 jsval_t func_obj = mkval(T_OBJ, vdata(r)); 21734 22104 jsoff_t proto_off = lkp_interned(js, func_obj, INTERN_PROTOTYPE, 9); 21735 22105 if (proto_off == 0) return mkval(T_BOOL, 0); ··· 21754 22124 jsoff_t prop_len; 21755 22125 const char *prop_name; 21756 22126 char num_buf[32]; 22127 + char sym_buf[64]; 21757 22128 21758 - if (vtype(l) == T_STR) { 21759 - jsoff_t prop_off = vstr(js, l, &prop_len); 21760 - prop_name = (char *) &js->mem[prop_off]; 21761 - } else if (vtype(l) == T_NUM) { 21762 - prop_len = (jsoff_t) strnum(l, num_buf, sizeof(num_buf)); 22129 + jsval_t key = js_to_primitive(js, l, 1); 22130 + if (is_err(key)) return key; 22131 + 22132 + if (vtype(key) == T_SYMBOL) { 22133 + int klen = sym_to_prop_key(key, sym_buf, sizeof(sym_buf)); 22134 + if (klen <= 0) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid symbol key"); 22135 + prop_len = (jsoff_t)klen; 22136 + prop_name = sym_buf; 22137 + } else if (vtype(key) == T_NUM) { 22138 + prop_len = (jsoff_t)strnum(key, num_buf, sizeof(num_buf)); 21763 22139 prop_name = num_buf; 21764 22140 } else { 21765 - return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot use 'in' operator to search for '%s' in non-object", js_str(js, l)); 22141 + jsval_t key_str = js_tostring_val(js, key); 22142 + if (is_err(key_str)) return key_str; 22143 + jsoff_t prop_off = vstr(js, key_str, &prop_len); 22144 + prop_name = (char *)&js->mem[prop_off]; 21766 22145 } 21767 22146 21768 22147 if (vtype(r) != T_OBJ && vtype(r) != T_ARR && vtype(r) != T_FUNC) { ··· 23716 24095 return true; 23717 24096 } return false; 23718 24097 } 24098 + 23719 24099 jsval_t arr_obj = mkval(T_OBJ, vdata(obj)); 23720 24100 jsoff_t off = lkp(js, arr_obj, key, key_len); 23721 - if (off == 0) return false; 24101 + if (off == 0) { 24102 + jsval_t accessor_result; 24103 + if (try_accessor_getter(js, arr_obj, key, key_len, &accessor_result)) { 24104 + *out = accessor_result; return true; 24105 + } return false; 24106 + } 24107 + 24108 + descriptor_entry_t *desc = lookup_descriptor(js, (jsoff_t)vdata(arr_obj), key, key_len); 24109 + if (desc && desc->has_getter) { 24110 + jsval_t accessor_result; 24111 + if (try_accessor_getter(js, arr_obj, key, key_len, &accessor_result)) { 24112 + *out = accessor_result; return true; 24113 + } 24114 + } 24115 + 23722 24116 *out = resolveprop(js, mkval(T_PROP, off)); 23723 24117 return true; 23724 24118 } 23725 24119 23726 24120 uint8_t t = vtype(obj); 23727 24121 bool is_promise = (t == T_PROMISE); 24122 + if (t == T_OBJ && is_proxy(js, obj)) { 24123 + *out = proxy_get(js, obj, key, key_len); 24124 + return true; 24125 + } 23728 24126 if (is_promise) obj = mkval(T_OBJ, vdata(obj)); 23729 24127 else if (t != T_OBJ) return false; 23730 24128 jsoff_t off = lkp(js, obj, key, key_len); ··· 23742 24140 } 23743 24141 } 23744 24142 23745 - if (off == 0) return false; 24143 + if (off == 0) { 24144 + jsval_t accessor_result; 24145 + if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) { 24146 + *out = accessor_result; return true; 24147 + } 24148 + return false; 24149 + } 24150 + 24151 + descriptor_entry_t *desc = lookup_descriptor(js, (jsoff_t)vdata(obj), key, key_len); 24152 + if (desc && desc->has_getter) { 24153 + jsval_t accessor_result; 24154 + if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) { 24155 + *out = accessor_result; return true; 24156 + } 24157 + } 24158 + 23746 24159 *out = resolveprop(js, mkval(T_PROP, off)); 23747 24160 return true; 23748 24161 }