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 internal subclassing system

+416 -163
+1
include/common.h
··· 45 45 SLOT_SUPER, 46 46 SLOT_DEFAULT_CTOR, 47 47 SLOT_DEFAULT, 48 + SLOT_ERROR_BRAND, 48 49 SLOT_ERR_TYPE, 49 50 SLOT_OBSERVABLE_SUBSCRIBER, 50 51 SLOT_SUBSCRIPTION_OBSERVER,
+7 -2
include/internal.h
··· 121 121 #define PROPREF_STACK_SIZE 2048 122 122 #define PRIM_PROPREF_STACK_SIZE 512 123 123 124 - #define MAX_STRINGIFY_DEPTH 64 125 - #define MAX_MULTIREF_OBJS 128 124 + #define MAX_STRINGIFY_DEPTH 64 125 + #define MAX_PROTO_CHAIN_DEPTH 256 126 + #define MAX_MULTIREF_OBJS 128 127 + 128 + #define PROTO_WALK_F_OBJECT_ONLY (1u << 0) 129 + #define PROTO_WALK_F_LOOKUP (1u << 1) 126 130 127 131 #define NANBOX_PREFIX 0x7FC0000000000000ULL 128 132 #define NANBOX_PREFIX_CHK 0x3FEULL ··· 158 162 159 163 js_intern_stats_t js_intern_stats(void); 160 164 js_cstr_t js_to_cstr(struct js *js, jsval_t value, char *stack_buf, size_t stack_size); 165 + jsval_t js_instance_proto_from_new_target(struct js *js, jsval_t fallback_proto); 161 166 162 167 jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len); 163 168 jsoff_t lkp_proto(struct js *js, jsval_t obj, const char *buf, size_t len);
+301 -151
src/ant.c
··· 812 812 813 813 static jsval_t js_import_stmt(struct js *js); 814 814 static jsval_t js_export_stmt(struct js *js); 815 + 815 816 static jsval_t builtin_Object(struct js *js, jsval_t *args, int nargs); 816 817 static jsval_t builtin_promise_then(struct js *js, jsval_t *args, int nargs); 817 818 ··· 3475 3476 return js_mkundef(); 3476 3477 } 3477 3478 3478 - static jsval_t get_function_instance_proto_from_new_target(struct js *js, jsval_t fallback_proto) { 3479 + static inline bool proto_walk_next(struct js *js, jsval_t *cur, uint8_t *t, uint8_t flags) { 3480 + uint8_t ct = *t; 3481 + 3482 + if (flags & PROTO_WALK_F_OBJECT_ONLY) { 3483 + if (!is_object_type(*cur)) return false; 3484 + jsval_t next = get_proto(js, *cur); 3485 + uint8_t nt = vtype(next); 3486 + if (nt == T_NULL || nt == T_UNDEF || !is_object_type(next)) return false; 3487 + *cur = next; *t = nt; 3488 + return true; 3489 + } 3490 + 3491 + if (ct == T_OBJ || ct == T_ARR || ct == T_FUNC || ct == T_PROMISE) { 3492 + jsval_t as_obj = mkval(T_OBJ, vdata(*cur)); 3493 + jsval_t proto = get_slot(js, as_obj, SLOT_PROTO); 3494 + 3495 + uint8_t pt = vtype(proto); 3496 + if (pt == T_OBJ || pt == T_ARR || pt == T_FUNC) { 3497 + *cur = proto; 3498 + *t = pt; 3499 + return true; 3500 + } 3501 + 3502 + if (TYPE_FLAG(ct) & T_NEEDS_PROTO_FALLBACK) { 3503 + jsval_t fallback = get_prototype_for_type(js, ct); 3504 + uint8_t ft = vtype(fallback); 3505 + if (ft == T_NULL || ft == T_UNDEF) return false; 3506 + *cur = fallback; 3507 + *t = ft; 3508 + return true; 3509 + } 3510 + 3511 + return false; 3512 + } 3513 + 3514 + if (ct == T_STR || ct == T_NUM || ct == T_BOOL || ct == T_BIGINT) { 3515 + jsval_t proto = get_prototype_for_type(js, ct); 3516 + uint8_t pt = vtype(proto); 3517 + if (pt == T_NULL || pt == T_UNDEF) return false; 3518 + *cur = proto; *t = pt; 3519 + return true; 3520 + } 3521 + 3522 + return false; 3523 + } 3524 + 3525 + jsval_t js_instance_proto_from_new_target(struct js *js, jsval_t fallback_proto) { 3479 3526 jsval_t instance_proto = js_mkundef(); 3480 3527 3481 - if (vtype(js->new_target) == T_FUNC) { 3528 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 3482 3529 jsval_t nt_obj = mkval(T_OBJ, vdata(js->new_target)); 3483 3530 jsoff_t nt_proto_off = lkp_interned(js, nt_obj, INTERN_PROTOTYPE, 9); 3484 3531 if (nt_proto_off != 0) { ··· 3492 3539 } return instance_proto; 3493 3540 } 3494 3541 3542 + static inline bool proto_chain_contains(struct js *js, jsval_t obj, jsval_t proto_target) { 3543 + if (!is_object_type(obj) || !is_object_type(proto_target)) return false; 3544 + jsval_t cur = obj; uint8_t t = vtype(cur); 3545 + for (int depth = 0; depth < MAX_PROTO_CHAIN_DEPTH; depth++) { 3546 + if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_OBJECT_ONLY)) break; 3547 + if (vdata(cur) == vdata(proto_target)) return true; 3548 + } 3549 + return false; 3550 + } 3551 + 3552 + static inline bool is_wrapper_ctor_target(struct js *js, jsval_t this_val, jsval_t expected_proto) { 3553 + if (vtype(js->new_target) == T_UNDEF) return false; 3554 + if (vtype(this_val) != T_OBJ) return false; 3555 + if (vtype(get_slot(js, this_val, SLOT_PRIMITIVE)) != T_UNDEF) return false; 3556 + return proto_chain_contains(js, this_val, expected_proto); 3557 + } 3558 + 3559 + static jsval_t array_constructor_from_receiver(struct js *js, jsval_t receiver) { 3560 + if (!is_object_type(receiver)) return js_mkundef(); 3561 + 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(); 3567 + } 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; 3575 + } 3576 + 3577 + return ctor; 3578 + } 3579 + 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); 3589 + } 3590 + 3591 + return result; 3592 + } 3593 + 3594 + static jsval_t array_alloc_from_ctor_with_length(struct js *js, jsval_t ctor, jsoff_t length_hint) { 3595 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) { 3596 + return mkarr(js); 3597 + } 3598 + 3599 + jsval_t seed = js_mkobj(js); 3600 + if (is_err(seed)) return seed; 3601 + 3602 + jsval_t proto = js_get(js, ctor, "prototype"); 3603 + if (is_err(proto)) return proto; 3604 + if (is_object_type(proto)) set_proto(js, seed, proto); 3605 + 3606 + jsval_t ctor_args[1] = { tov((double)length_hint) }; 3607 + jsval_t saved_new_target = js->new_target; 3608 + js->new_target = ctor; 3609 + jsval_t constructed = js_call_with_this(js, ctor, seed, ctor_args, 1); 3610 + js->new_target = saved_new_target; 3611 + if (is_err(constructed)) return constructed; 3612 + 3613 + jsval_t result = is_object_type(constructed) ? constructed : seed; 3614 + set_slot(js, mkval(T_OBJ, vdata(result)), SLOT_CTOR, ctor); 3615 + return result; 3616 + } 3617 + 3618 + static jsval_t array_alloc_like(struct js *js, jsval_t receiver) { 3619 + jsval_t ctor = array_constructor_from_receiver(js, receiver); 3620 + if (is_err(ctor)) return ctor; 3621 + return array_alloc_from_ctor(js, ctor); 3622 + } 3623 + 3495 3624 static void infer_func_name(struct js *js, jsval_t func, const char *name, size_t len) { 3496 3625 jsval_t func_obj = mkval(T_OBJ, vdata(func)); 3497 3626 if (vtype(get_slot(js, func_obj, SLOT_NAME)) != T_UNDEF) return; ··· 5585 5714 jsval_t cur = obj; 5586 5715 int depth = 0; 5587 5716 5588 - while (depth < 32) { 5717 + while (depth < MAX_PROTO_CHAIN_DEPTH) { 5589 5718 if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE) { 5590 5719 jsval_t as_obj = mkval(T_OBJ, vdata(cur)); 5591 5720 jsoff_t off = lkp_interned(js, as_obj, key_intern, len); 5592 5721 if (off != 0) return off; 5593 - 5594 - jsval_t proto = get_slot(js, as_obj, SLOT_PROTO); 5595 - uint8_t pt = vtype(proto); 5596 - if (pt == T_NULL) break; 5597 - if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) { 5598 - if (TYPE_FLAG(t) & T_NEEDS_PROTO_FALLBACK) { 5599 - cur = get_prototype_for_type(js, t); 5600 - t = vtype(cur); 5601 - if (t == T_NULL || t == T_UNDEF) break; 5602 - depth++; continue; 5603 - } 5604 - break; 5605 - } 5606 - cur = proto; 5607 - t = vtype(cur); 5608 - if (t == T_NULL || t == T_UNDEF) break; 5609 - depth++; 5610 - } else if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) { 5611 - cur = get_prototype_for_type(js, t); 5612 - t = vtype(cur); 5613 - if (t == T_NULL || t == T_UNDEF) break; 5614 - depth++; 5615 5722 } else if (t == T_CFUNC) { 5616 5723 jsval_t func_proto = get_ctor_proto(js, "Function", 8); 5617 5724 uint8_t ft = vtype(func_proto); ··· 5620 5727 if (off != 0) return off; 5621 5728 } 5622 5729 break; 5623 - } else { 5624 - break; 5625 - } 5730 + } else if (t != T_STR && t != T_NUM && t != T_BOOL && t != T_BIGINT) break; 5731 + if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_LOOKUP)) { break; } depth++; 5626 5732 } 5627 5733 5628 5734 return 0; ··· 6219 6325 } else return js_mkerr(js, "bad str op"); 6220 6326 } 6221 6327 6328 + static inline bool parse_string_index_key(jsval_t key_val, const char *kstr, size_t klen, size_t *out_idx) { 6329 + if (vtype(key_val) == T_NUM) { 6330 + double d = tod(key_val); 6331 + if (!isfinite(d) || d < 0.0) return false; 6332 + 6333 + double di = floor(d); 6334 + if (di != d || di > (double)SIZE_MAX) return false; 6335 + 6336 + size_t idx = (size_t)di; 6337 + if ((double)idx != di) return false; 6338 + 6339 + *out_idx = idx; 6340 + return true; 6341 + } 6342 + 6343 + if (!is_array_index(kstr, (jsoff_t)klen)) return false; 6344 + unsigned long idx = 0; 6345 + 6346 + if (!parse_array_index(kstr, klen, (jsoff_t)-1, &idx)) return false; 6347 + *out_idx = (size_t)idx; 6348 + 6349 + return true; 6350 + } 6351 + 6352 + static inline jsval_t try_string_index_access(struct js *js, jsval_t str, jsval_t key_val, const char *keystr, size_t keylen) { 6353 + size_t idx = 0; 6354 + if (!parse_string_index_key(key_val, keystr, keylen, &idx)) return js_mkundef(); 6355 + 6356 + jsoff_t byte_len; 6357 + jsoff_t str_off = vstr(js, str, &byte_len); 6358 + 6359 + const char *str_data = (const char *)&js->mem[str_off]; 6360 + size_t char_bytes; 6361 + 6362 + int byte_offset = utf16_index_to_byte_offset(str_data, byte_len, idx, &char_bytes); 6363 + if (byte_offset < 0) return js_mkundef(); 6364 + 6365 + return js_mkstr(js, str_data + byte_offset, char_bytes); 6366 + } 6367 + 6222 6368 static jsval_t do_bracket_op(struct js *js, jsval_t l, jsval_t r) { 6223 6369 jsval_t obj = resolveprop(js, l); 6224 6370 jsval_t key_val = resolveprop(js, r); ··· 6229 6375 double dv = tod(key_val); 6230 6376 if (dv >= 0 && dv <= 0xFFFFFFFF && dv == (double)(uint32_t)dv) { 6231 6377 keylen = uint_to_str(keybuf, sizeof(keybuf), (uint32_t)dv); 6232 - } else { 6233 - keylen = strnum(key_val, keybuf, sizeof(keybuf)); 6234 - } 6378 + } else keylen = strnum(key_val, keybuf, sizeof(keybuf)); 6235 6379 keystr = keybuf; 6236 6380 } else if (vtype(key_val) == T_STR) { 6237 6381 jsoff_t slen; ··· 6250 6394 keystr = (char *) &js->mem[off]; 6251 6395 keylen = slen; 6252 6396 } 6397 + if (vtype(obj) == T_PROMISE) obj = mkval(T_OBJ, vdata(obj)); 6253 6398 if (streq(keystr, keylen, "length", 6)) { 6254 6399 if (vtype(obj) == T_STR) { 6255 6400 jsoff_t byte_len; ··· 6260 6405 if (vtype(obj) == T_ARR) return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(js->length_str)); 6261 6406 } 6262 6407 if (vtype(obj) == T_STR) { 6263 - double idx_d = JS_NAN; 6264 - if (vtype(key_val) == T_NUM) { 6265 - idx_d = tod(key_val); 6266 - } else { 6267 - char *endptr; 6268 - char temp[64]; 6269 - size_t copy_len = keylen < sizeof(temp) - 1 ? keylen : sizeof(temp) - 1; 6270 - memcpy(temp, keystr, copy_len); 6271 - temp[copy_len] = '\0'; 6272 - idx_d = strtod(temp, &endptr); 6273 - if (endptr == temp || *endptr != '\0') idx_d = JS_NAN; 6274 - } 6275 - if (!isnan(idx_d) && idx_d >= 0 && idx_d == (double)(long)idx_d) { 6276 - jsoff_t idx = (jsoff_t) idx_d; jsoff_t byte_len; 6277 - jsoff_t str_off = vstr(js, obj, &byte_len); 6278 - const char *str_data = (const char *)&js->mem[str_off]; 6279 - size_t char_bytes; 6280 - int byte_offset = utf16_index_to_byte_offset(str_data, byte_len, idx, &char_bytes); 6281 - if (byte_offset >= 0) { 6282 - return js_mkstr(js, str_data + byte_offset, char_bytes); 6283 - } 6284 - } 6408 + jsval_t ch = try_string_index_access(js, obj, key_val, keystr, keylen); 6409 + if (!is_undefined(ch)) return ch; 6285 6410 jsoff_t off = lkp_proto(js, obj, keystr, keylen); 6286 6411 if (off != 0) return resolveprop(js, mkval(T_PROP, off)); 6287 6412 return js_mkundef(); 6413 + } 6414 + if (vtype(obj) == T_OBJ) { 6415 + jsval_t prim = get_slot(js, obj, SLOT_PRIMITIVE); 6416 + if (vtype(prim) == T_STR) { 6417 + jsval_t ch = try_string_index_access(js, prim, key_val, keystr, keylen); 6418 + if (!is_undefined(ch)) return ch; 6419 + } 6288 6420 } 6289 6421 if (vtype(obj) == T_FUNC) { 6290 6422 if ((js->flags & F_STRICT) && (streq(keystr, keylen, "caller", 6) || streq(keystr, keylen, "arguments", 9))) { ··· 6390 6522 if (vtype(r) != T_CODEREF) return js_mkerr_typed(js, JS_ERR_SYNTAX, "ident expected"); 6391 6523 uint8_t t = vtype(l); 6392 6524 6525 + if (t == T_PROMISE) { 6526 + l = mkval(T_OBJ, vdata(l)); 6527 + t = T_OBJ; 6528 + } 6529 + 6393 6530 if (t == T_STR && streq(ptr, plen, "length", 6)) { 6394 6531 jsoff_t byte_len; 6395 6532 jsoff_t str_off = vstr(js, l, &byte_len); ··· 6404 6541 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) { 6405 6542 jsval_t key = js_mkstr(js, ptr, plen); 6406 6543 return mkprim_propref(l, (jsoff_t)vdata(key)); 6407 - } 6408 - 6409 - if (t == T_PROMISE) { 6410 - jsoff_t off = lkp_proto(js, mkval(T_OBJ, vdata(l)), ptr, plen); 6411 - if (off != 0) { 6412 - return resolveprop(js, mkval(T_PROP, off)); 6413 - } 6414 - jsval_t promise_proto = get_ctor_proto(js, "Promise", 7); 6415 - if (vtype(promise_proto) != T_UNDEF && vtype(promise_proto) != T_NULL) { 6416 - off = lkp_proto(js, promise_proto, ptr, plen); 6417 - if (off != 0) { 6418 - return resolveprop(js, mkval(T_PROP, off)); 6419 - } 6420 - } 6421 - return js_mkundef(); 6422 6544 } 6423 6545 6424 6546 if (t == T_FUNC) { ··· 8521 8643 return do_op(js, op, l, tov(vdata(r) ? 1.0 : 0.0)); 8522 8644 } else if ((lt == T_NUM && rtype == T_STR) || (lt == T_STR && rtype == T_NUM)) { 8523 8645 eq = js_to_number(js, l) == js_to_number(js, r); 8524 - } else if (lt == T_ARR || lt == T_OBJ) { 8525 - jsval_t l_prim = js_tostring_val(js, l); 8646 + } else if (is_object_type(l)) { 8647 + jsval_t l_prim = js_to_primitive(js, l, 0); 8526 8648 if (!is_err(l_prim)) return do_op(js, op, l_prim, r); 8527 - } else if (rtype == T_ARR || rtype == T_OBJ) { 8528 - jsval_t r_prim = js_tostring_val(js, r); 8649 + } else if (is_object_type(r)) { 8650 + jsval_t r_prim = js_to_primitive(js, r, 0); 8529 8651 if (!is_err(r_prim)) return do_op(js, op, l, r_prim); 8530 8652 } 8531 8653 return mkval(T_BOOL, op == TOK_EQ ? eq : !eq); ··· 14060 14182 } 14061 14183 14062 14184 jsval_t string_proto = js_get_ctor_proto(js, "String", 6); 14063 - if (is_unboxed_obj(js, js->this_val, string_proto)) { 14185 + if (is_wrapper_ctor_target(js, js->this_val, string_proto)) { 14064 14186 set_slot(js, js->this_val, SLOT_PRIMITIVE, sval); 14065 14187 jsoff_t slen; 14066 14188 vstr(js, sval, &slen); ··· 14129 14251 static jsval_t builtin_Number(struct js *js, jsval_t *args, int nargs) { 14130 14252 jsval_t nval = tov(nargs > 0 ? js_to_number(js, args[0]) : 0.0); 14131 14253 jsval_t number_proto = js_get_ctor_proto(js, "Number", 6); 14132 - if (is_unboxed_obj(js, js->this_val, number_proto)) { 14254 + if (is_wrapper_ctor_target(js, js->this_val, number_proto)) { 14133 14255 set_slot(js, js->this_val, SLOT_PRIMITIVE, nval); 14134 14256 } 14135 14257 return nval; ··· 14138 14260 static jsval_t builtin_Boolean(struct js *js, jsval_t *args, int nargs) { 14139 14261 jsval_t bval = mkval(T_BOOL, nargs > 0 && js_truthy(js, args[0]) ? 1 : 0); 14140 14262 jsval_t boolean_proto = js_get_ctor_proto(js, "Boolean", 7); 14141 - if (is_unboxed_obj(js, js->this_val, boolean_proto)) { 14263 + if (is_wrapper_ctor_target(js, js->this_val, boolean_proto)) { 14142 14264 set_slot(js, js->this_val, SLOT_PRIMITIVE, bval); 14143 14265 } 14144 14266 return bval; ··· 14205 14327 set_slot(js, func_obj, SLOT_SCOPE, js_glob(js)); 14206 14328 14207 14329 jsval_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); 14208 - jsval_t instance_proto = get_function_instance_proto_from_new_target(js, func_proto); 14330 + jsval_t instance_proto = js_instance_proto_from_new_target(js, func_proto); 14209 14331 if (is_object_type(instance_proto)) set_proto(js, func_obj, instance_proto); 14210 14332 14211 14333 jsval_t func = mkval(T_FUNC, (unsigned long) vdata(func_obj)); ··· 14312 14434 set_slot(js, func_obj, SLOT_SCOPE, js_glob(js)); 14313 14435 14314 14436 jsval_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); 14315 - jsval_t instance_proto = get_function_instance_proto_from_new_target(js, func_proto); 14437 + jsval_t instance_proto = js_instance_proto_from_new_target(js, func_proto); 14316 14438 if (is_object_type(instance_proto)) set_proto(js, func_obj, instance_proto); 14317 14439 14318 14440 jsval_t func = mkval(T_FUNC, (unsigned long) vdata(func_obj)); ··· 14676 14798 14677 14799 static jsval_t builtin_Array(struct js *js, jsval_t *args, int nargs) { 14678 14800 jsval_t arr = mkarr(js); 14801 + if (is_err(arr)) return arr; 14679 14802 14680 14803 if (nargs == 1 && vtype(args[0]) == T_NUM) { 14681 14804 jsval_t err = validate_array_length(js, args[0]); ··· 14690 14813 } else if (nargs > 0) { 14691 14814 for (int i = 0; i < nargs; i++) arr_set(js, arr, (jsoff_t)i, args[i]); 14692 14815 } 14816 + 14817 + jsval_t array_proto = get_ctor_proto(js, "Array", 5); 14818 + jsval_t instance_proto = js_instance_proto_from_new_target(js, array_proto); 14819 + 14820 + if (is_object_type(instance_proto)) set_proto(js, arr, instance_proto); 14821 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 14822 + set_slot(js, arr, SLOT_CTOR, js->new_target); 14823 + } 14693 14824 14694 14825 return arr; 14695 14826 } ··· 14723 14854 } 14724 14855 14725 14856 js_mkprop_fast(js, this_val, "name", 4, name); 14857 + set_slot(js, this_val, SLOT_ERROR_BRAND, js_true); 14858 + 14726 14859 return this_val; 14727 14860 } 14728 14861 ··· 14791 14924 } 14792 14925 14793 14926 js_mkprop_fast(js, this_val, "name", 4, ANT_STRING("AggregateError")); 14927 + set_slot(js, this_val, SLOT_ERROR_BRAND, js_true); 14928 + 14794 14929 return this_val; 14795 14930 } 14796 14931 14797 14932 static jsval_t builtin_RegExp(struct js *js, jsval_t *args, int nargs) { 14933 + if (vtype(js->new_target) == T_UNDEF && nargs > 0 && (nargs < 2 || vtype(args[1]) == T_UNDEF) && is_object_type(args[0])) { 14934 + jsval_t regexp_proto = get_ctor_proto(js, "RegExp", 6); 14935 + if (is_object_type(regexp_proto) && proto_chain_contains(js, args[0], regexp_proto)) return args[0]; 14936 + } 14937 + 14798 14938 jsval_t regexp_obj = js->this_val; 14799 - bool use_this = (vtype(regexp_obj) == T_OBJ); 14939 + bool use_this = (vtype(js->new_target) != T_UNDEF && vtype(regexp_obj) == T_OBJ); 14800 14940 14801 14941 if (!use_this) { 14802 14942 regexp_obj = mkobj(js, 0); 14943 + if (is_err(regexp_obj)) return regexp_obj; 14803 14944 } 14945 + 14804 14946 jsval_t regexp_proto = get_ctor_proto(js, "RegExp", 6); 14805 - if (vtype(regexp_proto) == T_OBJ) set_proto(js, regexp_obj, regexp_proto); 14947 + jsval_t instance_proto = js_instance_proto_from_new_target(js, regexp_proto); 14948 + 14949 + if (is_object_type(instance_proto)) set_proto(js, regexp_obj, instance_proto); 14950 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 14951 + set_slot(js, regexp_obj, SLOT_CTOR, js->new_target); 14952 + } 14806 14953 14807 14954 jsval_t pattern = js_mkstr(js, "", 0); 14808 14955 if (nargs > 0) { 14809 - if (vtype(args[0]) == T_STR) { 14810 - pattern = args[0]; 14811 - } else { 14812 - const char *str = js_str(js, args[0]); 14813 - pattern = js_mkstr(js, str, strlen(str)); 14814 - } 14815 - } 14956 + if (vtype(args[0]) == T_STR) pattern = args[0]; 14957 + else { 14958 + const char *str = js_str(js, args[0]); 14959 + pattern = js_mkstr(js, str, strlen(str)); 14960 + }} 14816 14961 14817 14962 jsval_t flags = js_mkstr(js, "", 0); 14818 - if (nargs > 1 && vtype(args[1]) == T_STR) { 14819 - flags = args[1]; 14820 - } 14963 + if (nargs > 1 && vtype(args[1]) == T_STR) flags = args[1]; 14821 14964 14822 14965 jsval_t source_key = js_mkstr(js, "source", 6); 14823 14966 js_setprop(js, regexp_obj, source_key, pattern); ··· 17079 17222 } 17080 17223 } 17081 17224 } 17225 + 17226 + if (is_object_type(obj) && get_slot(js, obj, SLOT_ERROR_BRAND) == js_true) { 17227 + return js_mkstr(js, "[object Error]", 14); 17228 + } 17082 17229 17083 17230 const char *type_name = NULL; 17084 17231 ··· 17342 17489 } 17343 17490 17344 17491 if (start > end) start = end; 17345 - jsval_t result = mkarr(js); 17492 + jsval_t result = array_alloc_like(js, arr); 17346 17493 if (is_err(result)) return result; 17347 17494 jsoff_t result_idx = 0; 17348 17495 ··· 17352 17499 result_idx++; 17353 17500 } 17354 17501 17355 - return mkval(T_ARR, vdata(result)); 17502 + return result; 17356 17503 } 17357 17504 17358 17505 static jsval_t builtin_array_join(struct js *js, jsval_t *args, int nargs) { ··· 17635 17782 if (is_err(callback)) return callback; 17636 17783 17637 17784 jsoff_t len = get_array_length(js, arr); 17638 - 17639 - jsval_t result = mkarr(js); 17785 + jsval_t result = array_alloc_like(js, arr); 17640 17786 if (is_err(result)) return result; 17641 17787 17642 17788 for (jsoff_t i = 0; i < len; i++) { ··· 17648 17794 arr_set(js, result, i, mapped); 17649 17795 } 17650 17796 17651 - return mkval(T_ARR, vdata(result)); 17797 + return result; 17652 17798 } 17653 17799 17654 17800 static jsval_t builtin_array_filter(struct js *js, jsval_t *args, int nargs) { ··· 17661 17807 if (is_err(callback)) return callback; 17662 17808 17663 17809 jsoff_t len = get_array_length(js, arr); 17664 - 17665 - jsval_t result = mkarr(js); 17810 + jsval_t result = array_alloc_like(js, arr); 17666 17811 if (is_err(result)) return result; 17667 17812 17668 17813 jsoff_t result_idx = 0; ··· 17672 17817 jsval_t val = arr_get(js, arr, i); 17673 17818 jsval_t call_args[3] = { val, tov((double)i), arr }; 17674 17819 jsval_t test = call_js_with_args(js, callback, call_args, 3); 17820 + 17675 17821 if (is_err(test)) return test; 17676 - if (js_truthy(js, test)) { 17677 - arr_set(js, result, result_idx, val); 17678 - result_idx++; 17679 - } 17822 + if (js_truthy(js, test)) { arr_set(js, result, result_idx, val); result_idx++; } 17680 17823 } 17681 17824 17682 - return mkval(T_ARR, vdata(result)); 17825 + return result; 17683 17826 } 17684 17827 17685 17828 static jsval_t builtin_array_reduce(struct js *js, jsval_t *args, int nargs) { ··· 17720 17863 17721 17864 if (depth > 0 && (vtype(val) == T_ARR || vtype(val) == T_OBJ)) { 17722 17865 flat_helper(js, val, result, result_idx, depth - 1); 17723 - } else { 17724 - arr_set(js, result, *result_idx, val); 17725 - (*result_idx)++; 17726 - } 17866 + } else { arr_set(js, result, *result_idx, val); (*result_idx)++; } 17727 17867 } 17728 17868 } 17729 17869 ··· 17739 17879 if (depth < 0) depth = 0; 17740 17880 } 17741 17881 17742 - jsval_t result = mkarr(js); 17882 + jsval_t result = array_alloc_like(js, arr); 17743 17883 if (is_err(result)) return result; 17744 17884 jsoff_t result_idx = 0; 17745 17885 17746 17886 flat_helper(js, arr, result, &result_idx, depth); 17747 - 17748 - return mkval(T_ARR, vdata(result)); 17887 + return result; 17749 17888 } 17750 17889 17751 17890 static jsval_t builtin_array_concat(struct js *js, jsval_t *args, int nargs) { ··· 17754 17893 return js_mkerr(js, "concat called on non-array"); 17755 17894 } 17756 17895 17757 - jsval_t result = mkarr(js); 17896 + jsval_t result = array_alloc_like(js, arr); 17758 17897 if (is_err(result)) return result; 17759 - jsoff_t result_idx = 0; 17760 17898 17899 + jsoff_t result_idx = 0; 17761 17900 jsoff_t len = get_array_length(js, arr); 17762 17901 17763 17902 for (jsoff_t i = 0; i < len; i++) { ··· 17782 17921 } 17783 17922 } 17784 17923 17785 - return mkval(T_ARR, vdata(result)); 17924 + return result; 17786 17925 } 17787 17926 17788 17927 static jsval_t builtin_array_at(struct js *js, jsval_t *args, int nargs) { ··· 18362 18501 18363 18502 int insertCount = nargs > 2 ? nargs - 2 : 0; 18364 18503 18365 - jsval_t removed = mkarr(js); 18504 + jsval_t removed = array_alloc_like(js, arr); 18366 18505 if (is_err(removed)) return removed; 18367 18506 18368 18507 jsoff_t doff = get_dense_buf(js, arr); ··· 18401 18540 18402 18541 dense_set_length(js, doff, new_len); 18403 18542 if (deleteCount > 0) js->needs_gc = true; 18404 - return mkval(T_ARR, vdata(removed)); 18543 + return removed; 18405 18544 } 18406 18545 18407 18546 for (int i = 0; i < deleteCount; i++) { ··· 18451 18590 js_setprop(js, arr, js->length_str, tov((double)((int)len + shift))); 18452 18591 if (deleteCount > 0) js->needs_gc = true; 18453 18592 18454 - return mkval(T_ARR, vdata(removed)); 18593 + return removed; 18455 18594 } 18456 18595 18457 18596 static jsval_t builtin_array_copyWithin(struct js *js, jsval_t *args, int nargs) { ··· 18770 18909 18771 18910 jsval_t ctor = js->this_val; 18772 18911 bool use_ctor = (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC); 18773 - jsval_t result; 18774 - 18775 - if (use_ctor) { 18776 - jsval_t ctor_args[1] = { tov(0.0) }; 18777 - result = js_call(js, ctor, ctor_args, 0); 18778 - if (is_err(result)) return result; 18779 - } else { 18780 - result = mkarr(js); 18781 - if (is_err(result)) return result; 18782 - } 18912 + jsval_t result = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, 0) : mkarr(js); 18913 + if (is_err(result)) return result; 18783 18914 18784 18915 bool result_is_proxy = is_proxy(js, result); 18785 18916 jsval_t write_target = result_is_proxy ? proxy_read_target(js, result) : result; ··· 18822 18953 static jsval_t builtin_Array_of(struct js *js, jsval_t *args, int nargs) { 18823 18954 jsval_t ctor = js->this_val; 18824 18955 bool use_ctor = (vtype(ctor) == T_FUNC || vtype(ctor) == T_CFUNC); 18825 - jsval_t arr; 18826 - 18827 - if (use_ctor) { 18828 - jsval_t ctor_args[1] = { tov((double)nargs) }; 18829 - arr = js_call(js, ctor, ctor_args, 1); 18830 - if (is_err(arr)) return arr; 18831 - } else { 18832 - arr = mkarr(js); 18833 - if (is_err(arr)) return arr; 18834 - } 18956 + jsval_t arr = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, (jsoff_t)nargs) : mkarr(js); 18957 + if (is_err(arr)) return arr; 18835 18958 18836 18959 bool arr_is_proxy = is_proxy(js, arr); 18837 18960 jsval_t write_target = arr_is_proxy ? proxy_read_target(js, arr) : arr; ··· 18843 18966 js_setprop(js, write_target, js_mkstr(js, idxstr, idxlen), args[i]); 18844 18967 } 18845 18968 } 18846 - 18969 + 18847 18970 if (vtype(arr) != T_ARR) js_setprop(js, arr, js->length_str, tov((double) nargs)); 18848 18971 if (!use_ctor) return mkval(T_ARR, vdata(arr)); 18849 - 18972 + 18850 18973 return arr; 18851 18974 } 18852 18975 ··· 20824 20947 static jsval_t mkpromise(struct js *js) { 20825 20948 jsval_t obj = mkobj(js, 0); 20826 20949 if (is_err(obj)) return obj; 20950 + 20951 + jsval_t promise_proto = get_ctor_proto(js, "Promise", 7); 20952 + if (is_object_type(promise_proto)) { 20953 + set_slot(js, obj, SLOT_PROTO, promise_proto); 20954 + } 20955 + 20956 + jsval_t promise_ctor = js_get(js, js_glob(js), "Promise"); 20957 + if (vtype(promise_ctor) == T_FUNC || vtype(promise_ctor) == T_CFUNC) { 20958 + set_slot(js, obj, SLOT_CTOR, promise_ctor); 20959 + } 20827 20960 20828 20961 uint32_t pid = next_promise_id++; 20829 20962 set_slot(js, obj, SLOT_PID, tov((double)pid)); ··· 20962 21095 20963 21096 jsval_t p = mkpromise(js); 20964 21097 jsval_t new_target = js->new_target; 21098 + jsval_t p_obj = mkval(T_OBJ, vdata(p)); 20965 21099 20966 - if (vtype(new_target) == T_FUNC) { 20967 - jsoff_t proto_off = lkp_interned(js, mkval(T_OBJ, vdata(new_target)), INTERN_PROTOTYPE, 9); 20968 - jsval_t subclass_proto = proto_off ? resolveprop(js, mkval(T_PROP, proto_off)) : js_mkundef(); 20969 - 20970 - if (vtype(subclass_proto) == T_OBJ) { 20971 - jsval_t p_obj = mkval(T_OBJ, vdata(p)); 20972 - set_slot(js, p_obj, SLOT_PROTO, subclass_proto); 20973 - set_slot(js, p_obj, SLOT_CTOR, new_target); 20974 - } 20975 - } 21100 + jsval_t promise_proto = get_ctor_proto(js, "Promise", 7); 21101 + jsval_t instance_proto = js_instance_proto_from_new_target(js, promise_proto); 21102 + 21103 + if (is_object_type(instance_proto)) set_slot(js, p_obj, SLOT_PROTO, instance_proto); 21104 + if (vtype(new_target) == T_FUNC || vtype(new_target) == T_CFUNC) set_slot(js, p_obj, SLOT_CTOR, new_target); 20976 21105 20977 21106 jsval_t res_obj = mkobj(js, 0); 20978 - 20979 21107 set_slot(js, res_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal)); 20980 21108 set_slot(js, res_obj, SLOT_DATA, p); 20981 21109 ··· 21164 21292 return builtin_Promise_resolve(js, res_args, 1); 21165 21293 } 21166 21294 21295 + static jsval_t mkpromise_with_ctor(struct js *js, jsval_t ctor) { 21296 + jsval_t p = mkpromise(js); 21297 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) return p; 21298 + 21299 + jsval_t proto = js_get(js, ctor, "prototype"); 21300 + if (is_err(proto)) return proto; 21301 + if (is_object_type(proto)) { 21302 + jsval_t p_obj = mkval(T_OBJ, vdata(p)); 21303 + set_slot(js, p_obj, SLOT_PROTO, proto); 21304 + set_slot(js, p_obj, SLOT_CTOR, ctor); 21305 + } 21306 + return p; 21307 + } 21308 + 21167 21309 static jsval_t builtin_Promise_all_resolve_handler(struct js *js, jsval_t *args, int nargs) { 21168 21310 jsval_t me = js->current_func; 21169 21311 jsval_t tracker = js_get(js, me, "tracker"); ··· 21240 21382 uint8_t t = vtype(iterable); 21241 21383 if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.all requires an iterable"); 21242 21384 21243 - jsval_t result_promise = mkpromise(js); 21385 + jsval_t ctor = js->this_val; 21386 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef(); 21387 + 21388 + jsval_t result_promise = mkpromise_with_ctor(js, ctor); 21389 + if (is_err(result_promise)) return result_promise; 21390 + 21244 21391 jsval_t tracker = mkobj(js, 0); 21245 21392 jsval_t results = mkarr(js); 21246 21393 ··· 21263 21410 } 21264 21411 21265 21412 if (len == 0) { 21266 - jsval_t resolve_args[] = { mkval(T_ARR, vdata(results)) }; 21267 - return builtin_Promise_resolve(js, resolve_args, 1); 21413 + resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 21414 + return result_promise; 21268 21415 } 21269 21416 21270 21417 js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); ··· 21318 21465 uint8_t t = vtype(iterable); 21319 21466 if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.race requires an iterable"); 21320 21467 21321 - jsval_t result_promise = mkpromise(js); 21468 + jsval_t ctor = js->this_val; 21469 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef(); 21470 + jsval_t result_promise = mkpromise_with_ctor(js, ctor); 21471 + if (is_err(result_promise)) return result_promise; 21322 21472 21323 21473 jsval_t resolve_obj = mkobj(js, 0); 21324 21474 set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal));
+38 -8
src/modules/collections.c
··· 724 724 return js_mkerr_typed(js, JS_ERR_TYPE, "Map constructor requires 'new'"); 725 725 } 726 726 727 - jsval_t map_obj = js_mkobj(js); 727 + jsval_t map_obj = js->this_val; 728 + if (vtype(map_obj) != T_OBJ) map_obj = js_mkobj(js); 729 + if (is_err(map_obj)) return map_obj; 728 730 jsoff_t obj_offset = (jsoff_t)vdata(map_obj); 729 731 730 732 jsval_t map_proto = js_get_ctor_proto(js, "Map", 3); 731 - if (is_special_object(map_proto)) js_set_proto(js, map_obj, map_proto); 733 + jsval_t instance_proto = js_instance_proto_from_new_target(js, map_proto); 734 + 735 + if (is_special_object(instance_proto)) js_set_proto(js, map_obj, instance_proto); 736 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 737 + js_set_slot(js, map_obj, SLOT_CTOR, js->new_target); 738 + } 732 739 733 740 map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *)); 734 741 if (!map_head) return js_mkerr(js, "out of memory"); ··· 775 782 return js_mkerr_typed(js, JS_ERR_TYPE, "Set constructor requires 'new'"); 776 783 } 777 784 778 - jsval_t set_obj = js_mkobj(js); 785 + jsval_t set_obj = js->this_val; 786 + if (vtype(set_obj) != T_OBJ) set_obj = js_mkobj(js); 787 + if (is_err(set_obj)) return set_obj; 779 788 jsoff_t obj_offset = (jsoff_t)vdata(set_obj); 780 789 781 790 jsval_t set_proto = js_get_ctor_proto(js, "Set", 3); 782 - if (is_special_object(set_proto)) js_set_proto(js, set_obj, set_proto); 791 + jsval_t instance_proto = js_instance_proto_from_new_target(js, set_proto); 792 + 793 + if (is_special_object(instance_proto)) js_set_proto(js, set_obj, instance_proto); 794 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 795 + js_set_slot(js, set_obj, SLOT_CTOR, js->new_target); 796 + } 783 797 784 798 set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *)); 785 799 if (!set_head) return js_mkerr(js, "out of memory"); ··· 815 829 if (vtype(js->new_target) == T_UNDEF) { 816 830 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakMap constructor requires 'new'"); 817 831 } 818 - jsval_t wm_obj = js_mkobj(js); 832 + 833 + jsval_t wm_obj = js->this_val; 834 + if (vtype(wm_obj) != T_OBJ) wm_obj = js_mkobj(js); 835 + if (is_err(wm_obj)) return wm_obj; 819 836 820 837 jsval_t wm_proto = js_get_ctor_proto(js, "WeakMap", 7); 821 - if (is_special_object(wm_proto)) js_set_proto(js, wm_obj, wm_proto); 838 + jsval_t instance_proto = js_instance_proto_from_new_target(js, wm_proto); 839 + 840 + if (is_special_object(instance_proto)) js_set_proto(js, wm_obj, instance_proto); 841 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 842 + js_set_slot(js, wm_obj, SLOT_CTOR, js->new_target); 843 + } 822 844 823 845 weakmap_entry_t **wm_head = ant_calloc(sizeof(weakmap_entry_t *)); 824 846 if (!wm_head) return js_mkerr(js, "out of memory"); ··· 865 887 return js_mkerr_typed(js, JS_ERR_TYPE, "WeakSet constructor requires 'new'"); 866 888 } 867 889 868 - jsval_t ws_obj = js_mkobj(js); 890 + jsval_t ws_obj = js->this_val; 891 + if (vtype(ws_obj) != T_OBJ) ws_obj = js_mkobj(js); 892 + if (is_err(ws_obj)) return ws_obj; 893 + 869 894 jsval_t ws_proto = js_get_ctor_proto(js, "WeakSet", 7); 870 - if (is_special_object(ws_proto)) js_set_proto(js, ws_obj, ws_proto); 895 + jsval_t instance_proto = js_instance_proto_from_new_target(js, ws_proto); 896 + 897 + if (is_special_object(instance_proto)) js_set_proto(js, ws_obj, instance_proto); 898 + if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { 899 + js_set_slot(js, ws_obj, SLOT_CTOR, js->new_target); 900 + } 871 901 872 902 weakset_entry_t **ws_head = ant_calloc(sizeof(weakset_entry_t *)); 873 903 if (!ws_head) return js_mkerr(js, "out of memory");
+1
src/modules/io.c
··· 442 442 [SLOT_SUPER] = "SUPER", 443 443 [SLOT_DEFAULT_CTOR] = "DEFAULT_CTOR", 444 444 [SLOT_DEFAULT] = "DEFAULT", 445 + [SLOT_ERROR_BRAND] = "ERROR_BRAND", 445 446 [SLOT_ERR_TYPE] = "ERR_TYPE", 446 447 [SLOT_OBSERVABLE_SUBSCRIBER] = "OBSERVABLE_SUBSCRIBER", 447 448 [SLOT_SUBSCRIPTION_OBSERVER] = "SUBSCRIPTION_OBSERVER",
+2 -2
src/modules/reflect.c
··· 150 150 151 151 jsval_t result = js_call_with_this(js, target, new_obj, call_args, arg_count); 152 152 js->new_target = saved_new_target; 153 + 153 154 if (call_args) free(call_args); 155 + if (is_object_type(result)) return result; 154 156 155 - int result_type = vtype(result); 156 - if (result_type == T_FUNC || is_special_object(result)) return result; 157 157 return new_obj; 158 158 } 159 159
+66
tests/bench_prototype_chain.js
··· 1 + function nowMs() { 2 + return Date.now(); 3 + } 4 + 5 + function makeChain(depth) { 6 + function C() {} 7 + const root = { marker: 1 }; 8 + C.prototype = root; 9 + 10 + let obj = Object.create(root); 11 + for (let i = 0; i < depth; i++) obj = Object.create(obj); 12 + return { C, obj, root }; 13 + } 14 + 15 + function bench(label, fn, iters) { 16 + const t0 = nowMs(); 17 + const result = fn(iters); 18 + const dt = nowMs() - t0; 19 + const opsPerMs = dt > 0 ? (iters / dt).toFixed(2) : 'inf'; 20 + console.log(label + ': ' + dt + 'ms (' + opsPerMs + ' ops/ms) result=' + result); 21 + } 22 + 23 + function runDepth(depth) { 24 + const iters = 2_000_000; 25 + const { C, obj, root } = makeChain(depth); 26 + 27 + console.log('\n=== depth=' + depth + ' ==='); 28 + console.log('sanity instanceof=' + (obj instanceof C)); 29 + console.log('sanity isPrototypeOf=' + root.isPrototypeOf(obj)); 30 + console.log('sanity marker=' + obj.marker); 31 + 32 + bench( 33 + 'instanceof', 34 + n => { 35 + let c = 0; 36 + for (let i = 0; i < n; i++) if (obj instanceof C) c++; 37 + return c; 38 + }, 39 + iters 40 + ); 41 + 42 + bench( 43 + 'isPrototypeOf', 44 + n => { 45 + let c = 0; 46 + for (let i = 0; i < n; i++) if (root.isPrototypeOf(obj)) c++; 47 + return c; 48 + }, 49 + iters 50 + ); 51 + 52 + bench( 53 + 'prop_lookup', 54 + n => { 55 + let c = 0; 56 + for (let i = 0; i < n; i++) c += obj.marker; 57 + return c; 58 + }, 59 + iters 60 + ); 61 + } 62 + 63 + console.log('prototype-chain benchmark'); 64 + runDepth(8); 65 + runDepth(24); 66 + runDepth(40);