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.

improve dense and overall array indexing performance

+89 -20
+1
include/object.h
··· 86 86 uint8_t is_exotic: 1; 87 87 uint8_t is_constructor: 1; 88 88 uint8_t fast_array: 1; 89 + uint8_t may_have_holes: 1; 89 90 uint8_t gc_permanent: 1; 90 91 uint8_t generation: 1; 91 92 uint8_t in_remember_set: 1;
+84 -20
src/ant.c
··· 907 907 return (ptr && ptr->type_tag == T_ARR) ? ptr : NULL; 908 908 } 909 909 910 + static inline bool array_may_have_holes(ant_value_t obj) { 911 + ant_object_t *ptr = array_obj_ptr(obj); 912 + return ptr ? ptr->may_have_holes : true; 913 + } 914 + 915 + static inline void array_mark_may_have_holes(ant_value_t obj) { 916 + ant_object_t *ptr = array_obj_ptr(obj); 917 + if (ptr) ptr->may_have_holes = 1; 918 + } 919 + 910 920 static inline void array_define_or_set_index(ant_t *js, ant_value_t obj, const char *key, size_t klen) { 911 921 if (!key) return; 912 922 if (!array_obj_ptr(obj)) return; ··· 914 924 unsigned long idx = 0; 915 925 if (!parse_array_index(key, klen, ANT_ARRAY_INDEX_EXCLUSIVE, &idx)) return; 916 926 927 + ant_offset_t cur_len = get_array_length(js, obj); 917 928 ant_offset_t next_len = (ant_offset_t)idx + 1; 918 - if (next_len > get_array_length(js, obj)) { 929 + 930 + if (next_len > cur_len) { 931 + if ((ant_offset_t)idx > cur_len) array_mark_may_have_holes(obj); 919 932 array_len_set(js, obj, next_len); 920 933 } 921 934 } ··· 2704 2717 if (doff == 0) goto sparse; 2705 2718 } 2706 2719 2720 + if (idx > len) array_mark_may_have_holes(arr); 2707 2721 for (ant_offset_t i = len; i < idx; i++) { 2708 2722 ant_value_t v = dense_get(doff, i); 2709 2723 if (!is_empty_slot(v) && vtype(v) == T_UNDEF) dense_set(js, doff, i, T_EMPTY); 2710 2724 } 2725 + 2711 2726 dense_set(js, doff, idx, val); 2712 2727 array_len_set(js, arr, idx + 1); 2728 + 2713 2729 return; 2714 2730 } 2715 2731 ··· 2906 2922 static inline void arr_del(ant_t *js, ant_value_t arr, ant_offset_t idx) { 2907 2923 ant_offset_t semantic_len = get_array_length(js, arr); 2908 2924 if (idx >= semantic_len) return; 2925 + 2926 + array_mark_may_have_holes(arr); 2909 2927 ant_offset_t doff = get_dense_buf(arr); 2910 2928 2911 2929 if (doff) { ··· 2915 2933 2916 2934 char idxstr[16]; 2917 2935 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 2936 + 2918 2937 js_delete_prop(js, arr, idxstr, idxlen); 2919 2938 } 2920 2939 ··· 3000 3019 if (obj->u.array.data) { 3001 3020 for (uint32_t i = 0; i < obj->u.array.cap; i++) obj->u.array.data[i] = T_EMPTY; 3002 3021 obj->fast_array = 1; 3022 + obj->may_have_holes = 0; 3003 3023 } else { 3004 3024 obj->u.array.cap = 0; 3005 3025 obj->u.array.len = 0; 3006 3026 obj->fast_array = 0; 3027 + obj->may_have_holes = 1; 3007 3028 } 3008 3029 3009 3030 return arr; ··· 3959 3980 if (array_obj_ptr(obj) && is_length_key(key, klen)) { 3960 3981 ant_value_t err = validate_array_length(js, v); 3961 3982 if (is_err(err)) return err; 3983 + 3962 3984 ant_offset_t doff = get_dense_buf(obj); 3985 + ant_offset_t cur_len = get_array_length(js, obj); 3963 3986 ant_offset_t new_len_val = (ant_offset_t) tod(v); 3987 + 3964 3988 if (doff) { 3965 3989 ant_offset_t cap = dense_capacity(doff); 3966 - ant_offset_t cur_len = get_array_length(js, obj); 3967 3990 ant_offset_t clear_to = (cur_len < cap) ? cur_len : cap; 3968 - if (new_len_val < clear_to) { 3969 - for (ant_offset_t i = new_len_val; i < clear_to; i++) 3970 - dense_set(js, doff, i, T_EMPTY); 3971 - } 3991 + if (new_len_val < clear_to) 3992 + for (ant_offset_t i = new_len_val; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY); 3972 3993 } 3994 + 3995 + if (new_len_val > cur_len) array_mark_may_have_holes(obj); 3973 3996 array_len_set(js, obj, new_len_val); 3997 + 3974 3998 return v; 3975 3999 } 3976 4000 ··· 5036 5060 ant_offset_t doff = get_dense_buf(original_obj); 5037 5061 unsigned long del_idx = 0; 5038 5062 if (doff && parse_array_index(key, len, get_array_length(js, original_obj), &del_idx)) { 5063 + array_mark_may_have_holes(original_obj); 5039 5064 ant_offset_t dense_len = dense_iterable_length(js, original_obj); 5040 5065 if ((ant_offset_t)del_idx < dense_len) dense_set(js, doff, (ant_offset_t)del_idx, T_EMPTY); 5041 5066 } ··· 5775 5800 if (is_err(err)) return err; 5776 5801 ant_offset_t new_len = (ant_offset_t)tod(args[0]); 5777 5802 ant_offset_t doff = get_dense_buf(arr); 5778 - if (doff && new_len <= 1024) { 5803 + if (doff && new_len <= 1024) 5779 5804 if (new_len > dense_capacity(doff)) doff = dense_grow(js, arr, new_len); 5780 - } 5805 + if (new_len > 0) array_mark_may_have_holes(arr); 5781 5806 array_len_set(js, arr, new_len); 5782 - } else if (nargs > 0) { 5783 - for (int i = 0; i < nargs; i++) arr_set(js, arr, (ant_offset_t)i, args[i]); 5784 - } 5807 + } else if (nargs > 0) for (int i = 0; i < nargs; i++) 5808 + arr_set(js, arr, (ant_offset_t)i, args[i]); 5785 5809 5786 5810 ant_value_t array_proto = get_ctor_proto(js, "Array", 5); 5787 5811 ant_value_t instance_proto = js_instance_proto_from_new_target(js, array_proto); ··· 7020 7044 for (ant_offset_t i = new_len; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY); 7021 7045 } 7022 7046 } 7023 - 7047 + 7048 + if (new_len > get_array_length(js, as_obj)) array_mark_may_have_holes(as_obj); 7024 7049 array_len_set(js, as_obj, new_len); 7050 + 7025 7051 return obj; 7026 7052 } 7027 7053 7028 7054 ant_offset_t existing_off = sym_key ? lkp_sym(js, as_obj, sym_off) : lkp(js, as_obj, prop_str, prop_len); 7029 7055 prop_meta_t existing_sym_meta; 7056 + 7030 7057 bool has_existing_sym_meta = sym_key && lookup_symbol_prop_meta(as_obj, sym_off, &existing_sym_meta); 7031 7058 bool has_existing_prop = (existing_off > 0) || has_existing_sym_meta; 7032 7059 ant_object_t *obj_ptr = js_obj_ptr(as_obj); ··· 8094 8121 doff = dense_grow(js, arr, len + 1); 8095 8122 if (doff == 0) return js_mkerr(js, "oom"); 8096 8123 } 8124 + if (is_empty_slot(args[i])) array_mark_may_have_holes(arr); 8097 8125 dense_set(js, doff, len, args[i]); 8098 8126 len++; 8099 8127 } ··· 8124 8152 doff = dense_grow(js, arr, len + 1); 8125 8153 if (doff == 0) return; 8126 8154 } 8155 + if (is_empty_slot(val)) array_mark_may_have_holes(arr); 8127 8156 dense_set(js, doff, len, val); 8128 8157 array_len_set(js, arr, len + 1); 8129 8158 return; ··· 8449 8478 return js_getprop_super(js, get_proto(js, arr), arr, idxstr); 8450 8479 } 8451 8480 8481 + static ant_value_t array_includes_get_array_index_value( 8482 + ant_t *js, ant_value_t arr, ant_offset_t 8483 + idx, char *idxstr, size_t idxlen 8484 + ) { 8485 + if (arr_has(js, arr, idx)) return arr_get(js, arr, idx); 8486 + if (lkp_proto(js, arr, idxstr, idxlen) == 0) return js_mkundef(); 8487 + idxstr[idxlen] = '\0'; 8488 + return js_getprop_super(js, get_proto(js, arr), arr, idxstr); 8489 + } 8490 + 8452 8491 static ant_value_t array_includes_dense_fast( 8453 - ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_value_t *args, int nargs 8492 + ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_offset_t len, ant_offset_t start 8454 8493 ) { 8455 8494 if (!array_obj_ptr(arr) || is_proxy(arr)) return js_mkundef(); 8456 8495 8457 - ant_offset_t len = get_array_length(js, arr); 8458 - if (len == 0) return mkval(T_BOOL, 0); 8459 - 8460 8496 ant_offset_t doff = get_dense_buf(arr); 8461 8497 if (!doff) return js_mkundef(); 8462 8498 ··· 8466 8502 ant_offset_t dense_len = dense_iterable_length(js, arr); 8467 8503 if (dense_len != len) return js_mkundef(); 8468 8504 8469 - ant_offset_t start = array_includes_start_index(js, args, nargs, len); 8470 - if (start >= len) return mkval(T_BOOL, 0); 8505 + if (!array_may_have_holes(arr)) { 8506 + for (ant_offset_t i = start; i < len; i++) 8507 + if (array_includes_matches(js, query, dense[i])) return mkval(T_BOOL, 1); 8508 + return mkval(T_BOOL, 0); 8509 + } 8471 8510 8472 8511 for (ant_offset_t i = start; i < len; i++) { 8473 8512 ant_value_t val = dense[i]; ··· 8478 8517 return mkval(T_BOOL, 0); 8479 8518 } 8480 8519 8520 + static ant_value_t array_includes_array_slow( 8521 + ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_offset_t len, ant_offset_t start 8522 + ) { 8523 + for (ant_offset_t i = start; i < len; i++) { 8524 + char idxstr[16]; 8525 + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (uint64_t)i); 8526 + 8527 + ant_value_t val = array_includes_get_array_index_value(js, arr, i, idxstr, idxlen); 8528 + if (is_err(val)) return val; 8529 + if (array_includes_matches(js, query, val)) return mkval(T_BOOL, 1); 8530 + } 8531 + 8532 + return mkval(T_BOOL, 0); 8533 + } 8534 + 8481 8535 static ant_value_t array_includes_generic( 8482 8536 ant_t *js, ant_value_t arr, const array_includes_query_t *query, ant_value_t *args, int nargs 8483 8537 ) { ··· 8512 8566 (nargs > 0) ? args[0] : js_mkundef() 8513 8567 ); 8514 8568 8515 - ant_value_t fast = array_includes_dense_fast(js, arr, &query, args, nargs); 8516 - if (vtype(fast) != T_UNDEF) return fast; 8569 + if (array_obj_ptr(arr) && !is_proxy(arr)) { 8570 + ant_offset_t len = get_array_length(js, arr); 8571 + if (len == 0) return mkval(T_BOOL, 0); 8572 + 8573 + ant_offset_t start = array_includes_start_index(js, args, nargs, len); 8574 + if (start >= len) return mkval(T_BOOL, 0); 8575 + 8576 + ant_value_t fast = array_includes_dense_fast(js, arr, &query, len, start); 8577 + if (vtype(fast) != T_UNDEF) return fast; 8578 + 8579 + return array_includes_array_slow(js, arr, &query, len, start); 8580 + } 8517 8581 8518 8582 return array_includes_generic(js, arr, &query, args, nargs); 8519 8583 }
+4
tests/test_array_includes_regression_big.cjs
··· 18 18 assert.strictEqual(holey.includes("tail"), true); 19 19 assert.strictEqual(holey.includes("tail", 4), false); 20 20 21 + const grown = [1]; 22 + grown.length = 3; 23 + assert.strictEqual(grown.includes(undefined), true); 24 + 21 25 let steps = 0; 22 26 const generic = { 23 27 get length() {