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.

refactor: optimize array methods and property iteration

- Rewrite Array.prototype.sort to use merge sort with O(n) auxiliary space
instead of O(nยฒ) bubble sort, with proper compareFn validation
- Simplify toSorted/toReversed/toSpliced to reuse sort/reverse/splice on copies
- Replace js_prop_iter_t with ant_iter_t for cleaner property iteration API
- Fix js_del to update object tail pointer when deleting tail property
- Add failed flag to gc_ctx_t for proper OOM handling during compaction
- Optimize JSON.parse with hash table for duplicate key handling
- Fix JSON.stringify replacer function to pass transformed value correctly
- Fix JSON.parse reviver to use js_call_with_this for correct `this` binding

+1284 -813
+2
.gitignore
··· 10 10 /build 11 11 /traces 12 12 /src/strip/target 13 + 14 + /test262 13 15 /javascript-zoo 14 16 15 17 /vendor/*/
+3 -3
examples/spec/forin.js
··· 24 24 var arr = [10, 20, 30]; 25 25 var arrKeys = []; 26 26 for (var k in arr) arrKeys.push(k); 27 - testDeep('array for-in keys', arrKeys, ['2', '1', '0']); 27 + testDeep('array for-in keys', arrKeys, ['0', '1', '2']); 28 28 29 29 var obj = { a: 1, b: 2, c: 3 }; 30 30 var objKeys = []; 31 31 for (var k in obj) objKeys.push(k); 32 - testDeep('object for-in keys', objKeys, ['c', 'b', 'a']); 32 + testDeep('object for-in keys', objKeys, ['a', 'b', 'c']); 33 33 34 34 var arrWithProps = [1, 2]; 35 35 arrWithProps.foo = 'bar'; 36 36 var arrPropsKeys = []; 37 37 for (var k in arrWithProps) arrPropsKeys.push(k); 38 - testDeep('array with properties for-in keys', arrPropsKeys, ['foo', '1', '0']); 38 + testDeep('array with properties for-in keys', arrPropsKeys, ['0', '1', 'foo']); 39 39 40 40 summary();
+10 -7
include/ant.h
··· 108 108 jsval_t js_mksym(ant_t *, const char *desc); 109 109 jsval_t js_mkfun(jsval_t (*fn)(ant_t *, jsval_t *, int)); 110 110 jsval_t js_heavy_mkfun(ant_t *js, jsval_t (*fn)(ant_t *, jsval_t *, int), jsval_t data); 111 + 111 112 jsval_t js_mkprop_fast(ant_t *js, jsval_t obj, const char *key, size_t len, jsval_t v); 113 + jsoff_t js_mkprop_fast_off(ant_t *js, jsval_t obj, const char *key, size_t len, jsval_t v); 112 114 113 115 jsval_t js_call(ant_t *js, jsval_t func, jsval_t *args, int nargs); 114 116 jsval_t js_call_with_this(ant_t *js, jsval_t func, jsval_t this_val, jsval_t *args, int nargs); 115 117 116 118 void js_set(ant_t *, jsval_t, const char *, jsval_t); 119 + void js_saveval(ant_t *js, jsoff_t off, jsval_t v); 117 120 bool js_del(ant_t *, jsval_t obj, const char *key); 118 121 void js_merge_obj(ant_t *, jsval_t dst, jsval_t src); 119 122 ··· 138 141 const char *js_str(ant_t *, jsval_t val); 139 142 140 143 typedef struct { 141 - jsval_t obj; 142 - void *current; 143 - void *js_internal; 144 - } js_prop_iter_t; 144 + void *ctx; 145 + jsoff_t off; 146 + } ant_iter_t; 145 147 146 - js_prop_iter_t js_prop_iter_begin(ant_t *js, jsval_t obj); 147 - bool js_prop_iter_next(js_prop_iter_t *iter, const char **key, size_t *key_len, jsval_t *value); 148 - void js_prop_iter_end(js_prop_iter_t *iter); 148 + ant_iter_t js_prop_iter_begin(ant_t *js, jsval_t obj); 149 + 150 + bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, jsval_t *value); 151 + void js_prop_iter_end(ant_iter_t *iter); 149 152 150 153 jsval_t js_obj_to_func(jsval_t obj); 151 154 jsval_t js_mkpromise(ant_t *js);
+3
include/internal.h
··· 82 82 #define T_NEEDS_PROTO_FALLBACK (TYPE_FLAG(T_FUNC) | TYPE_FLAG(T_ARR) | TYPE_FLAG(T_PROMISE)) 83 83 #define T_NON_NUMERIC_MASK (TYPE_FLAG(T_STR) | TYPE_FLAG(T_ARR) | TYPE_FLAG(T_FUNC) | TYPE_FLAG(T_CFUNC) | TYPE_FLAG(T_OBJ)) 84 84 85 + jsoff_t esize(jsoff_t w); 86 + 85 87 void js_gc_update_roots(GC_UPDATE_ARGS); 86 88 bool js_has_pending_coroutines(void); 89 + bool is_internal_prop(const char *key, jsoff_t klen); 87 90 88 91 #define is_non_numeric(v) ((1u << vtype(v)) & T_NON_NUMERIC_MASK) 89 92
+1 -1
meson/version/meson.build
··· 4 4 timestamp_opt = get_option('build_timestamp') 5 5 timestamp = timestamp_opt != '' ? timestamp_opt : run_command('date', '+%s', check: true).stdout().strip() 6 6 7 - ant_version = '0.4.1.' + timestamp + '-g' + git_hash 7 + ant_version = '0.4.2.' + timestamp + '-g' + git_hash 8 8 cmd_cc = meson.get_compiler('c') 9 9 10 10 target_triple = run_command(cmd_cc.cmd_array(), '-dumpmachine', check: true).stdout().strip()
+925 -639
src/ant.c
··· 236 236 jsoff_t obj_off; 237 237 const char *intern_ptr; 238 238 jsoff_t prop_off; 239 - jsoff_t first_prop; 239 + jsoff_t tail; 240 240 } intern_prop_cache_entry_t; 241 241 static intern_prop_cache_entry_t intern_prop_cache[ANT_LIMIT_SIZE_CACHE]; 242 242 ··· 552 552 static void saveoff(struct js *js, jsoff_t off, jsoff_t val) { 553 553 memcpy(&js->mem[off], &val, sizeof(val)); 554 554 } 555 + 555 556 static void saveval(struct js *js, jsoff_t off, jsval_t val) { 556 557 memcpy(&js->mem[off], &val, sizeof(val)); 557 558 } ··· 783 784 static size_t strstring(struct js *js, jsval_t value, char *buf, size_t len); 784 785 static size_t strkey(struct js *js, jsval_t value, char *buf, size_t len); 785 786 786 - static inline jsoff_t loadoff(struct js *js, jsoff_t off) { 787 - assert(js->brk <= js->size); 788 - return *(jsoff_t *)(&js->mem[off]); 787 + static inline jsoff_t loadoff(struct js *js, jsoff_t off) { 788 + assert(off + sizeof(jsoff_t) <= js->brk); jsoff_t val; 789 + memcpy(&val, &js->mem[off], sizeof(val)); return val; 789 790 } 790 791 791 792 static bool is_arr_off(struct js *js, jsoff_t off) { ··· 832 833 static size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size); 833 834 834 835 static jsval_t js_stmt_impl(struct js *js); 835 - static inline jsoff_t esize(jsoff_t w); 836 - 837 836 static jsval_t js_expr(struct js *js); 838 837 static jsval_t js_call_valueOf(struct js *js, jsval_t value); 839 838 static jsval_t js_call_toString(struct js *js, jsval_t value); ··· 2266 2265 return entry->str; 2267 2266 } 2268 2267 2269 - static bool is_internal_prop(const char *key, jsoff_t klen) { 2268 + bool is_internal_prop(const char *key, jsoff_t klen) { 2270 2269 if (klen < 2) return false; 2271 2270 if (key[0] != '_' || key[1] != '_') return false; 2272 2271 if (klen == STR_PROTO_LEN && memcmp(key, STR_PROTO, STR_PROTO_LEN) == 0) return false; ··· 3192 3191 } 3193 3192 3194 3193 static jsval_t mkobj(struct js *js, jsoff_t parent) { 3195 - return mkentity(js, 0 | T_OBJ, &parent, sizeof(parent)); 3194 + jsoff_t buf[2] = { parent, 0 }; 3195 + return mkentity(js, 0 | T_OBJ, buf, sizeof(buf)); 3196 3196 } 3197 3197 3198 3198 static jsval_t mkarr(struct js *js) { ··· 3309 3309 3310 3310 if (ce->obj_off == obj_off && ce->intern_ptr == interned) { 3311 3311 ce->obj_off = 0; ce->intern_ptr = NULL; 3312 - ce->prop_off = 0; ce->first_prop = 0; 3312 + ce->prop_off = 0; ce->tail = 0; 3313 3313 } 3314 3314 } 3315 3315 3316 3316 static jsval_t mkprop(struct js *js, jsval_t obj, jsval_t k, jsval_t v, jsoff_t flags) { 3317 3317 jsoff_t koff = (jsoff_t) vdata(k); 3318 - jsoff_t b, head = (jsoff_t) vdata(obj); 3318 + jsoff_t head = (jsoff_t) vdata(obj); 3319 3319 char buf[sizeof(koff) + sizeof(v)]; 3320 3320 3321 - memcpy(&b, &js->mem[head], sizeof(b)); 3321 + jsoff_t header = loadoff(js, head); 3322 + jsoff_t first_prop = header & ~(3U | FLAGMASK); 3323 + jsoff_t tail = loadoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t)); 3324 + 3322 3325 memcpy(buf, &koff, sizeof(koff)); 3323 3326 memcpy(buf + sizeof(koff), &v, sizeof(v)); 3324 - jsoff_t brk = js->brk | T_OBJ; 3325 - 3326 - if (b & ARRMASK) brk |= ARRMASK; 3327 - memcpy(&js->mem[head], &brk, sizeof(brk)); 3328 3327 3329 3328 jsoff_t klen = (loadoff(js, koff) >> 2) - 1; 3330 3329 const char *p = (char *) &js->mem[koff + sizeof(koff)]; 3331 3330 (void)intern_string(p, klen); 3332 3331 3333 - jsoff_t prop_header = (b & ~(3U | FLAGMASK)) | T_PROP | flags; 3334 - return mkentity(js, prop_header, buf, sizeof(buf)); 3332 + jsoff_t new_prop_off = js->brk; 3333 + jsval_t prop = mkentity(js, 0 | T_PROP | flags, buf, sizeof(buf)); 3334 + if (is_err(prop)) return prop; 3335 + 3336 + if (first_prop == 0) { 3337 + jsoff_t new_header = new_prop_off | (header & (3U | FLAGMASK)); 3338 + saveoff(js, head, new_header); 3339 + } else { 3340 + jsoff_t tail_header = loadoff(js, tail); 3341 + jsoff_t new_tail_header = new_prop_off | (tail_header & (3U | FLAGMASK)); 3342 + saveoff(js, tail, new_tail_header); 3343 + } 3344 + saveoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t), new_prop_off); 3345 + 3346 + return prop; 3335 3347 } 3336 3348 3337 3349 static inline jsval_t mkprop_fast(struct js *js, jsval_t obj, jsval_t k, jsval_t v, jsoff_t flags) { 3338 3350 jsoff_t koff = (jsoff_t) vdata(k); 3339 - jsoff_t b, head = (jsoff_t) vdata(obj); 3351 + jsoff_t head = (jsoff_t) vdata(obj); 3340 3352 char buf[sizeof(koff) + sizeof(v)]; 3341 3353 3342 - memcpy(&b, &js->mem[head], sizeof(b)); 3354 + jsoff_t header = loadoff(js, head); 3355 + jsoff_t first_prop = header & ~(3U | FLAGMASK); 3356 + jsoff_t tail = loadoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t)); 3357 + 3343 3358 memcpy(buf, &koff, sizeof(koff)); 3344 3359 memcpy(buf + sizeof(koff), &v, sizeof(v)); 3345 - jsoff_t brk = js->brk | T_OBJ; 3360 + 3361 + jsoff_t new_prop_off = js->brk; 3362 + jsval_t prop = mkentity(js, 0 | T_PROP | flags, buf, sizeof(buf)); 3363 + if (is_err(prop)) return prop; 3346 3364 3347 - if (b & ARRMASK) brk |= ARRMASK; 3348 - memcpy(&js->mem[head], &brk, sizeof(brk)); 3365 + if (first_prop == 0) { 3366 + jsoff_t new_header = new_prop_off | (header & (3U | FLAGMASK)); 3367 + saveoff(js, head, new_header); 3368 + } else { 3369 + jsoff_t tail_header = loadoff(js, tail); 3370 + jsoff_t new_tail_header = new_prop_off | (tail_header & (3U | FLAGMASK)); 3371 + saveoff(js, tail, new_tail_header); 3372 + } 3373 + saveoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t), new_prop_off); 3349 3374 3350 - jsoff_t prop_header = (b & ~(3U | FLAGMASK)) | T_PROP | flags; 3351 - return mkentity(js, prop_header, buf, sizeof(buf)); 3375 + return prop; 3352 3376 } 3353 3377 3354 3378 jsval_t js_mkprop_fast(struct js *js, jsval_t obj, const char *key, size_t len, jsval_t v) { ··· 3357 3381 return mkprop_fast(js, obj, k, v, 0); 3358 3382 } 3359 3383 3384 + jsoff_t js_mkprop_fast_off(struct js *js, jsval_t obj, const char *key, size_t len, jsval_t v) { 3385 + jsval_t k = js_mkstr(js, key, len); 3386 + if (is_err(k)) return 0; 3387 + jsoff_t prop_off = js->brk; 3388 + mkprop_fast(js, obj, k, v, 0); 3389 + return prop_off + sizeof(jsoff_t) * 2; 3390 + } 3391 + 3392 + void js_saveval(struct js *js, jsoff_t off, jsval_t v) { saveval(js, off, v); } 3393 + 3360 3394 static jsval_t mkslot(struct js *js, jsval_t obj, internal_slot_t slot, jsval_t v) { 3361 - jsoff_t b, head = (jsoff_t) vdata(obj); 3395 + jsoff_t head = (jsoff_t) vdata(obj); 3362 3396 char buf[sizeof(jsoff_t) + sizeof(v)]; 3363 - memcpy(&b, &js->mem[head], sizeof(b)); 3397 + 3398 + jsoff_t header = loadoff(js, head); 3399 + jsoff_t first_prop = header & ~(3U | FLAGMASK); 3400 + jsoff_t tail = loadoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t)); 3364 3401 3365 3402 jsoff_t slot_key = (jsoff_t)slot; 3366 3403 memcpy(buf, &slot_key, sizeof(slot_key)); 3367 3404 memcpy(buf + sizeof(slot_key), &v, sizeof(v)); 3368 3405 3369 - jsoff_t brk = js->brk | T_OBJ; 3370 - if (b & ARRMASK) brk |= ARRMASK; 3371 - memcpy(&js->mem[head], &brk, sizeof(brk)); 3372 - jsoff_t prop_header = (b & ~(3U | FLAGMASK)) | T_PROP | SLOTMASK; 3406 + jsoff_t new_prop_off = js->brk; 3407 + jsval_t prop = mkentity(js, 0 | T_PROP | SLOTMASK, buf, sizeof(buf)); 3408 + if (is_err(prop)) return prop; 3373 3409 3374 - return mkentity(js, prop_header, buf, sizeof(buf)); 3410 + if (first_prop == 0) { 3411 + jsoff_t new_header = new_prop_off | (header & (3U | FLAGMASK)); 3412 + saveoff(js, head, new_header); 3413 + } else { 3414 + jsoff_t tail_header = loadoff(js, tail); 3415 + jsoff_t new_tail_header = new_prop_off | (tail_header & (3U | FLAGMASK)); 3416 + saveoff(js, tail, new_tail_header); 3417 + } 3418 + saveoff(js, head + sizeof(jsoff_t) + sizeof(jsoff_t), new_prop_off); 3419 + 3420 + return prop; 3375 3421 } 3376 3422 3377 3423 static jsoff_t search_slot(struct js *js, jsval_t obj, internal_slot_t slot) { ··· 3745 3791 return sym_get_desc(js, sym); 3746 3792 } 3747 3793 3748 - static inline jsoff_t esize(jsoff_t w) { 3794 + jsoff_t esize(jsoff_t w) { 3749 3795 jsoff_t cleaned = w & ~FLAGMASK; 3750 3796 switch (cleaned & 3U) { 3751 - case T_OBJ: return (jsoff_t) (sizeof(jsoff_t) + sizeof(jsoff_t)); 3797 + case T_OBJ: return (jsoff_t) (sizeof(jsoff_t) + sizeof(jsoff_t) + sizeof(jsoff_t)); 3752 3798 case T_PROP: return (jsoff_t) (sizeof(jsoff_t) + sizeof(jsoff_t) + sizeof(jsval_t)); 3753 3799 case T_STR: return (jsoff_t) (sizeof(jsoff_t) + align32(cleaned >> 2U)); 3754 3800 default: return (jsoff_t) ~0U; ··· 4877 4923 4878 4924 saveoff(js, off, (header & FLAGMASK) | T_OBJ); 4879 4925 saveoff(js, off + sizeof(jsoff_t), parent); 4926 + saveoff(js, off + sizeof(jsoff_t) + sizeof(jsoff_t), 0); 4880 4927 } 4881 4928 4882 4929 static bool block_needs_scope(struct js *js) { ··· 5119 5166 static inline jsoff_t lkp_interned(struct js *js, jsval_t obj, const char *search_intern, size_t len) { 5120 5167 jsoff_t obj_off = (jsoff_t)vdata(obj); 5121 5168 jsoff_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); 5169 + jsoff_t tail = loadoff(js, obj_off + sizeof(jsoff_t) * 2); 5122 5170 5123 - uint32_t cache_slot = (((uintptr_t)search_intern >> 3) ^ obj_off) & (ANT_LIMIT_SIZE_CACHE - 1); 5124 - intern_prop_cache_entry_t *ce = &intern_prop_cache[cache_slot]; 5171 + uint32_t slot = (((uintptr_t)search_intern >> 3) ^ obj_off) & (ANT_LIMIT_SIZE_CACHE - 1); 5172 + intern_prop_cache_entry_t *ce = &intern_prop_cache[slot]; 5173 + if (ce->obj_off == obj_off && ce->intern_ptr == search_intern && ce->tail == tail) return ce->prop_off; 5125 5174 5126 - if (ce->obj_off == obj_off && ce->intern_ptr == search_intern && ce->first_prop == first_prop) { 5127 - return ce->prop_off; 5128 - } 5175 + jsoff_t off = first_prop; 5176 + jsoff_t result = 0; 5129 5177 5130 - jsoff_t off = first_prop; 5131 5178 while (off < js->brk && off != 0) { 5132 5179 jsoff_t header = loadoff(js, off); 5133 5180 if (is_slot_prop(header)) { off = next_prop(header); continue; } 5134 5181 5135 - jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(off))); 5182 + jsoff_t koff = loadoff(js, off + sizeof(jsoff_t)); 5136 5183 jsoff_t klen = (loadoff(js, koff) >> 2) - 1; 5184 + 5137 5185 if (klen == len) { 5138 - const char *p = (char *) &js->mem[koff + sizeof(koff)]; 5139 - if (intern_string(p, klen) == search_intern) { 5140 - ce->obj_off = obj_off; 5141 - ce->intern_ptr = search_intern; 5142 - ce->prop_off = off; 5143 - ce->first_prop = first_prop; 5144 - return off; 5145 - } 5186 + const char *p = (char *)&js->mem[koff + sizeof(jsoff_t)]; 5187 + if (intern_string(p, klen) == search_intern) { result = off; break; } 5146 5188 } 5147 5189 off = next_prop(header); 5148 5190 } 5149 - 5191 + 5150 5192 ce->obj_off = obj_off; 5151 5193 ce->intern_ptr = search_intern; 5152 - ce->prop_off = 0; 5153 - ce->first_prop = first_prop; 5194 + ce->prop_off = result; 5195 + ce->tail = tail; 5154 5196 5155 - return 0; 5197 + return result; 5156 5198 } 5157 5199 5158 5200 static inline jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len) { ··· 9128 9170 jsoff_t current = loadoff(js, target); 9129 9171 9130 9172 saveoff(js, target, (deleted_next & ~3U) | (current & (FLAGMASK | 3U))); 9173 + jsoff_t tail = loadoff(js, obj_off + sizeof(jsoff_t) + sizeof(jsoff_t)); 9174 + 9175 + if (tail == prop_off) { 9176 + saveoff(js, obj_off + sizeof(jsoff_t) + sizeof(jsoff_t), prev_off); 9177 + } 9178 + 9131 9179 invalidate_prop_cache(js, obj_off, prop_off); 9132 9180 js->needs_gc = true; 9133 9181 } ··· 11835 11883 jsoff_t parent_scope_offset = (jsoff_t) vdata(js->scope); 11836 11884 if (global_scope_stack == NULL) utarray_new(global_scope_stack, &jsoff_icd); 11837 11885 utarray_push_back(global_scope_stack, &parent_scope_offset); 11838 - jsval_t with_scope = mkentity(js, 0 | T_OBJ, &parent_scope_offset, sizeof(parent_scope_offset)); 11839 11886 11887 + jsval_t with_scope = mkobj(js, parent_scope_offset); 11840 11888 set_slot(js, with_scope, SLOT_WITH, with_obj); 11841 11889 11842 11890 jsval_t saved_scope = js->scope; ··· 11845 11893 res = js_block_or_stmt(js); 11846 11894 if (global_scope_stack && utarray_len(global_scope_stack) > 0) utarray_pop_back(global_scope_stack); 11847 11895 js->scope = saved_scope; 11848 - } else { 11849 - res = js_block_or_stmt(js); 11850 - } 11896 + } else res = js_block_or_stmt(js); 11851 11897 11852 11898 js->flags = flags; 11853 11899 return res; ··· 14233 14279 } 14234 14280 14235 14281 static jsval_t builtin_Math_abs(struct js *js, jsval_t *args, int nargs) { 14236 - (void) js; 14237 14282 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14238 14283 if (isnan(x)) return tov(JS_NAN); 14239 14284 return tov(fabs(x)); 14240 14285 } 14241 14286 14242 14287 static jsval_t builtin_Math_acos(struct js *js, jsval_t *args, int nargs) { 14243 - (void) js; 14244 14288 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14245 14289 if (isnan(x)) return tov(JS_NAN); 14246 14290 return tov(acos(x)); 14247 14291 } 14248 14292 14249 14293 static jsval_t builtin_Math_acosh(struct js *js, jsval_t *args, int nargs) { 14250 - (void) js; 14251 14294 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14252 14295 if (isnan(x)) return tov(JS_NAN); 14253 14296 return tov(acosh(x)); 14254 14297 } 14255 14298 14256 14299 static jsval_t builtin_Math_asin(struct js *js, jsval_t *args, int nargs) { 14257 - (void) js; 14258 14300 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14259 14301 if (isnan(x)) return tov(JS_NAN); 14260 14302 return tov(asin(x)); 14261 14303 } 14262 14304 14263 14305 static jsval_t builtin_Math_asinh(struct js *js, jsval_t *args, int nargs) { 14264 - (void) js; 14265 14306 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14266 14307 if (isnan(x)) return tov(JS_NAN); 14267 14308 return tov(asinh(x)); 14268 14309 } 14269 14310 14270 14311 static jsval_t builtin_Math_atan(struct js *js, jsval_t *args, int nargs) { 14271 - (void) js; 14272 14312 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14273 14313 if (isnan(x)) return tov(JS_NAN); 14274 14314 return tov(atan(x)); 14275 14315 } 14276 14316 14277 14317 static jsval_t builtin_Math_atanh(struct js *js, jsval_t *args, int nargs) { 14278 - (void) js; 14279 14318 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14280 14319 if (isnan(x)) return tov(JS_NAN); 14281 14320 return tov(atanh(x)); 14282 14321 } 14283 14322 14284 14323 static jsval_t builtin_Math_atan2(struct js *js, jsval_t *args, int nargs) { 14285 - (void) js; 14286 14324 double y = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14287 14325 double x = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); 14288 14326 if (isnan(y) || isnan(x)) return tov(JS_NAN); ··· 14290 14328 } 14291 14329 14292 14330 static jsval_t builtin_Math_cbrt(struct js *js, jsval_t *args, int nargs) { 14293 - (void) js; 14294 14331 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14295 14332 if (isnan(x)) return tov(JS_NAN); 14296 14333 return tov(cbrt(x)); 14297 14334 } 14298 14335 14299 14336 static jsval_t builtin_Math_ceil(struct js *js, jsval_t *args, int nargs) { 14300 - (void) js; 14301 14337 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14302 14338 if (isnan(x)) return tov(JS_NAN); 14303 14339 return tov(ceil(x)); 14304 14340 } 14305 14341 14306 14342 static jsval_t builtin_Math_clz32(struct js *js, jsval_t *args, int nargs) { 14307 - (void) js; 14308 14343 if (nargs < 1) return tov(32); 14309 14344 double x = js_to_number(js, args[0]); 14310 14345 if (isnan(x) || isinf(x)) return tov(32); ··· 14316 14351 } 14317 14352 14318 14353 static jsval_t builtin_Math_cos(struct js *js, jsval_t *args, int nargs) { 14319 - (void) js; 14320 14354 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14321 14355 if (isnan(x)) return tov(JS_NAN); 14322 14356 return tov(cos(x)); 14323 14357 } 14324 14358 14325 14359 static jsval_t builtin_Math_cosh(struct js *js, jsval_t *args, int nargs) { 14326 - (void) js; 14327 14360 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14328 14361 if (isnan(x)) return tov(JS_NAN); 14329 14362 return tov(cosh(x)); 14330 14363 } 14331 14364 14332 14365 static jsval_t builtin_Math_exp(struct js *js, jsval_t *args, int nargs) { 14333 - (void) js; 14334 14366 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14335 14367 if (isnan(x)) return tov(JS_NAN); 14336 14368 return tov(exp(x)); 14337 14369 } 14338 14370 14339 14371 static jsval_t builtin_Math_expm1(struct js *js, jsval_t *args, int nargs) { 14340 - (void) js; 14341 14372 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14342 14373 if (isnan(x)) return tov(JS_NAN); 14343 14374 return tov(expm1(x)); 14344 14375 } 14345 14376 14346 14377 static jsval_t builtin_Math_floor(struct js *js, jsval_t *args, int nargs) { 14347 - (void) js; 14348 14378 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14349 14379 if (isnan(x)) return tov(JS_NAN); 14350 14380 return tov(floor(x)); 14351 14381 } 14352 14382 14353 14383 static jsval_t builtin_Math_fround(struct js *js, jsval_t *args, int nargs) { 14354 - (void) js; 14355 14384 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14356 14385 if (isnan(x)) return tov(JS_NAN); 14357 14386 return tov((double)(float)x); 14358 14387 } 14359 14388 14360 14389 static jsval_t builtin_Math_hypot(struct js *js, jsval_t *args, int nargs) { 14361 - (void) js; 14362 14390 if (nargs == 0) return tov(0.0); 14363 14391 double sum = 0.0; 14364 14392 for (int i = 0; i < nargs; i++) { ··· 14381 14409 } 14382 14410 14383 14411 static jsval_t builtin_Math_imul(struct js *js, jsval_t *args, int nargs) { 14384 - (void) js; 14385 14412 if (nargs < 2) return tov(0); 14386 14413 int32_t a = toInt32(js_to_number(js, args[0])); 14387 14414 int32_t b = toInt32(js_to_number(js, args[1])); ··· 14389 14416 } 14390 14417 14391 14418 static jsval_t builtin_Math_log(struct js *js, jsval_t *args, int nargs) { 14392 - (void) js; 14393 14419 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14394 14420 if (isnan(x)) return tov(JS_NAN); 14395 14421 return tov(log(x)); 14396 14422 } 14397 14423 14398 14424 static jsval_t builtin_Math_log1p(struct js *js, jsval_t *args, int nargs) { 14399 - (void) js; 14400 14425 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14401 14426 if (isnan(x)) return tov(JS_NAN); 14402 14427 return tov(log1p(x)); 14403 14428 } 14404 14429 14405 14430 static jsval_t builtin_Math_log10(struct js *js, jsval_t *args, int nargs) { 14406 - (void) js; 14407 14431 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14408 14432 if (isnan(x)) return tov(JS_NAN); 14409 14433 return tov(log10(x)); 14410 14434 } 14411 14435 14412 14436 static jsval_t builtin_Math_log2(struct js *js, jsval_t *args, int nargs) { 14413 - (void) js; 14414 14437 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14415 14438 if (isnan(x)) return tov(JS_NAN); 14416 14439 return tov(log2(x)); 14417 14440 } 14418 14441 14419 14442 static jsval_t builtin_Math_max(struct js *js, jsval_t *args, int nargs) { 14420 - (void) js; 14421 14443 if (nargs == 0) return tov(JS_NEG_INF); 14422 14444 double max_val = JS_NEG_INF; 14423 14445 for (int i = 0; i < nargs; i++) { ··· 14429 14451 } 14430 14452 14431 14453 static jsval_t builtin_Math_min(struct js *js, jsval_t *args, int nargs) { 14432 - (void) js; 14433 14454 if (nargs == 0) return tov(JS_INF); 14434 14455 double min_val = JS_INF; 14435 14456 for (int i = 0; i < nargs; i++) { ··· 14441 14462 } 14442 14463 14443 14464 static jsval_t builtin_Math_pow(struct js *js, jsval_t *args, int nargs) { 14444 - (void) js; 14445 14465 double base = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14446 14466 double exp = (nargs < 2) ? JS_NAN : js_to_number(js, args[1]); 14447 14467 if (isnan(base) || isnan(exp)) return tov(JS_NAN); ··· 14451 14471 static bool random_seeded = false; 14452 14472 14453 14473 static jsval_t builtin_Math_random(struct js *js, jsval_t *args, int nargs) { 14454 - (void) js; 14455 - (void) args; 14456 - (void) nargs; 14457 14474 if (!random_seeded) { 14458 14475 srand((unsigned int) time(NULL)); 14459 14476 random_seeded = true; ··· 14462 14479 } 14463 14480 14464 14481 static jsval_t builtin_Math_round(struct js *js, jsval_t *args, int nargs) { 14465 - (void) js; 14466 14482 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14467 14483 if (isnan(x) || isinf(x)) return tov(x); 14468 14484 return tov(floor(x + 0.5)); 14469 14485 } 14470 14486 14471 14487 static jsval_t builtin_Math_sign(struct js *js, jsval_t *args, int nargs) { 14472 - (void) js; 14473 14488 double v = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14474 14489 if (isnan(v)) return tov(JS_NAN); 14475 14490 if (v > 0) return tov(1.0); ··· 14478 14493 } 14479 14494 14480 14495 static jsval_t builtin_Math_sin(struct js *js, jsval_t *args, int nargs) { 14481 - (void) js; 14482 14496 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14483 14497 if (isnan(x)) return tov(JS_NAN); 14484 14498 return tov(sin(x)); 14485 14499 } 14486 14500 14487 14501 static jsval_t builtin_Math_sinh(struct js *js, jsval_t *args, int nargs) { 14488 - (void) js; 14489 14502 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14490 14503 if (isnan(x)) return tov(JS_NAN); 14491 14504 return tov(sinh(x)); 14492 14505 } 14493 14506 14494 14507 static jsval_t builtin_Math_sqrt(struct js *js, jsval_t *args, int nargs) { 14495 - (void) js; 14496 14508 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14497 14509 if (isnan(x)) return tov(JS_NAN); 14498 14510 return tov(sqrt(x)); 14499 14511 } 14500 14512 14501 14513 static jsval_t builtin_Math_tan(struct js *js, jsval_t *args, int nargs) { 14502 - (void) js; 14503 14514 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14504 14515 if (isnan(x)) return tov(JS_NAN); 14505 14516 return tov(tan(x)); 14506 14517 } 14507 14518 14508 14519 static jsval_t builtin_Math_tanh(struct js *js, jsval_t *args, int nargs) { 14509 - (void) js; 14510 14520 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14511 14521 if (isnan(x)) return tov(JS_NAN); 14512 14522 return tov(tanh(x)); 14513 14523 } 14514 14524 14515 14525 static jsval_t builtin_Math_trunc(struct js *js, jsval_t *args, int nargs) { 14516 - (void) js; 14517 14526 double x = (nargs < 1) ? JS_NAN : js_to_number(js, args[0]); 14518 14527 if (isnan(x)) return tov(JS_NAN); 14519 14528 return tov(trunc(x)); ··· 14550 14559 if (should_include) { 14551 14560 char idxstr[16]; 14552 14561 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14553 - jsval_t idx_key = js_mkstr(js, idxstr, idxlen); 14554 14562 jsval_t key_val = js_mkstr(js, key, klen); 14555 - setprop(js, arr, idx_key, key_val); 14563 + js_mkprop_fast(js, arr, idxstr, idxlen, key_val); 14556 14564 idx++; 14557 14565 } 14558 14566 } ··· 14563 14571 if (!desc->enumerable) continue; 14564 14572 if (!desc->has_getter && !desc->has_setter) continue; 14565 14573 14566 - char idxstr[16]; 14567 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14568 - jsval_t idx_key = js_mkstr(js, idxstr, idxlen); 14574 + char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14569 14575 jsval_t key_val = js_mkstr(js, desc->prop_name, desc->prop_len); 14570 - setprop(js, arr, idx_key, key_val); 14571 - idx++; 14576 + js_mkprop_fast(js, arr, idxstr, idxlen, key_val); idx++; 14572 14577 } 14578 + 14579 + jsval_t len_val = tov((double) idx); 14580 + js_mkprop_fast(js, arr, "length", 6, len_val); 14573 14581 14574 - for (jsoff_t i = 0; i < idx / 2; i++) { 14575 - jsoff_t j = idx - 1 - i; 14576 - char istr[16], jstr[16]; 14577 - snprintf(istr, sizeof(istr), "%u", (unsigned) i); 14578 - snprintf(jstr, sizeof(jstr), "%u", (unsigned) j); 14579 - jsoff_t ioff = lkp(js, arr, istr, strlen(istr)); 14580 - jsoff_t joff = lkp(js, arr, jstr, strlen(jstr)); 14581 - jsval_t iv = loadval(js, ioff + sizeof(jsoff_t) * 2); 14582 - jsval_t jv = loadval(js, joff + sizeof(jsoff_t) * 2); 14583 - saveval(js, ioff + sizeof(jsoff_t) * 2, jv); 14584 - saveval(js, joff + sizeof(jsoff_t) * 2, iv); 14585 - } 14586 - 14587 - jsval_t len_key = js_mkstr(js, "length", 6); 14588 - jsval_t len_val = tov((double) idx); 14589 - setprop(js, arr, len_key, len_val); 14590 14582 return mkval(T_ARR, vdata(arr)); 14591 14583 } 14592 14584 ··· 14623 14615 if (should_include) { 14624 14616 char idxstr[16]; 14625 14617 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14626 - jsval_t idx_key = js_mkstr(js, idxstr, idxlen); 14627 - setprop(js, arr, idx_key, val); 14618 + js_mkprop_fast(js, arr, idxstr, idxlen, val); 14628 14619 idx++; 14629 14620 } 14630 14621 } 14631 14622 14632 - for (jsoff_t i = 0; i < idx / 2; i++) { 14633 - jsoff_t j = idx - 1 - i; 14634 - char istr[16], jstr[16]; 14635 - snprintf(istr, sizeof(istr), "%u", (unsigned) i); 14636 - snprintf(jstr, sizeof(jstr), "%u", (unsigned) j); 14637 - jsoff_t ioff = lkp(js, arr, istr, strlen(istr)); 14638 - jsoff_t joff = lkp(js, arr, jstr, strlen(jstr)); 14639 - jsval_t iv = loadval(js, ioff + sizeof(jsoff_t) * 2); 14640 - jsval_t jv = loadval(js, joff + sizeof(jsoff_t) * 2); 14641 - saveval(js, ioff + sizeof(jsoff_t) * 2, jv); 14642 - saveval(js, joff + sizeof(jsoff_t) * 2, iv); 14643 - } 14623 + jsval_t len_val = tov((double) idx); 14624 + js_mkprop_fast(js, arr, "length", 6, len_val); 14644 14625 14645 - jsval_t len_key = js_mkstr(js, "length", 6); 14646 - jsval_t len_val = tov((double) idx); 14647 - setprop(js, arr, len_key, len_val); 14648 14626 return mkval(T_ARR, vdata(arr)); 14649 14627 } 14650 14628 ··· 14682 14660 if (should_include) { 14683 14661 jsval_t pair = mkarr(js); 14684 14662 jsval_t key_val = js_mkstr(js, key, klen); 14685 - setprop(js, pair, js_mkstr(js, "0", 1), key_val); 14686 - setprop(js, pair, js_mkstr(js, "1", 1), val); 14687 - setprop(js, pair, js_mkstr(js, "length", 6), tov(2.0)); 14663 + js_mkprop_fast(js, pair, "0", 1, key_val); 14664 + js_mkprop_fast(js, pair, "1", 1, val); 14665 + js_mkprop_fast(js, pair, "length", 6, tov(2.0)); 14688 14666 14689 - char idxstr[16]; 14690 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14691 - jsval_t idx_key = js_mkstr(js, idxstr, idxlen); 14692 - setprop(js, arr, idx_key, mkval(T_ARR, vdata(pair))); 14693 - idx++; 14667 + char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 14668 + js_mkprop_fast(js, arr, idxstr, idxlen, mkval(T_ARR, vdata(pair))); idx++; 14694 14669 } 14695 14670 } 14696 14671 14697 - for (jsoff_t i = 0; i < idx / 2; i++) { 14698 - jsoff_t j = idx - 1 - i; 14699 - char istr[16], jstr[16]; 14700 - snprintf(istr, sizeof(istr), "%u", (unsigned) i); 14701 - snprintf(jstr, sizeof(jstr), "%u", (unsigned) j); 14702 - jsoff_t ioff = lkp(js, arr, istr, strlen(istr)); 14703 - jsoff_t joff = lkp(js, arr, jstr, strlen(jstr)); 14704 - jsval_t iv = loadval(js, ioff + sizeof(jsoff_t) * 2); 14705 - jsval_t jv = loadval(js, joff + sizeof(jsoff_t) * 2); 14706 - saveval(js, ioff + sizeof(jsoff_t) * 2, jv); 14707 - saveval(js, joff + sizeof(jsoff_t) * 2, iv); 14708 - } 14672 + jsval_t len_val = tov((double) idx); 14673 + js_mkprop_fast(js, arr, "length", 6, len_val); 14709 14674 14710 - jsval_t len_key = js_mkstr(js, "length", 6); 14711 - jsval_t len_val = tov((double) idx); 14712 - setprop(js, arr, len_key, len_val); 14713 14675 return mkval(T_ARR, vdata(arr)); 14714 14676 } 14715 14677 ··· 15311 15273 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); 15312 15274 if (vtype(obj) == T_FUNC) obj = mkval(T_OBJ, vdata(obj)); 15313 15275 15314 - jsval_t arr = mkarr(js); 15315 - jsoff_t idx = 0; 15276 + jsoff_t count = 0; 15316 15277 jsoff_t next = loadoff(js, (jsoff_t) vdata(obj)) & ~(3U | FLAGMASK); 15317 15278 15318 15279 while (next < js->brk && next != 0) { 15319 15280 jsoff_t header = loadoff(js, next); 15320 - if (is_slot_prop(header)) { next = next_prop(header); continue; } 15321 - 15322 - jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 15323 - jsoff_t klen = offtolen(loadoff(js, koff)); 15324 - const char *key = (char *) &js->mem[koff + sizeof(koff)]; 15325 - 15281 + if (!is_slot_prop(header)) { 15282 + jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 15283 + jsoff_t klen = offtolen(loadoff(js, koff)); 15284 + const char *key = (char *) &js->mem[koff + sizeof(koff)]; 15285 + if (!is_internal_prop(key, klen)) count++; 15286 + } 15287 + next = next_prop(header); 15288 + } 15289 + 15290 + if (count == 0) { 15291 + jsval_t arr = mkarr(js); 15292 + js_mkprop_fast(js, arr, "length", 6, tov(0)); 15293 + return mkval(T_ARR, vdata(arr)); 15294 + } 15295 + 15296 + jsval_t *keys = malloc(count * sizeof(jsval_t)); 15297 + if (!keys) return js_mkerr(js, "out of memory"); 15298 + 15299 + jsoff_t idx = 0; 15300 + next = loadoff(js, (jsoff_t) vdata(obj)) & ~(3U | FLAGMASK); 15301 + while (next < js->brk && next != 0 && idx < count) { 15302 + jsoff_t header = loadoff(js, next); 15303 + if (!is_slot_prop(header)) { 15304 + jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 15305 + jsoff_t klen = offtolen(loadoff(js, koff)); 15306 + const char *key = (char *) &js->mem[koff + sizeof(koff)]; 15307 + if (!is_internal_prop(key, klen)) { 15308 + keys[idx++] = js_mkstr(js, key, klen); 15309 + } 15310 + } 15326 15311 next = next_prop(header); 15327 - if (is_internal_prop(key, klen)) continue; 15328 - 15329 - char idxstr[16]; 15330 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); 15331 - jsval_t idx_key = js_mkstr(js, idxstr, idxlen); 15332 - jsval_t key_val = js_mkstr(js, key, klen); 15333 - setprop(js, arr, idx_key, key_val); 15334 - idx++; 15335 15312 } 15336 15313 15337 - for (jsoff_t i = 0; i < idx / 2; i++) { 15338 - jsoff_t j = idx - 1 - i; 15339 - char istr[16], jstr[16]; 15340 - snprintf(istr, sizeof(istr), "%u", (unsigned) i); 15341 - snprintf(jstr, sizeof(jstr), "%u", (unsigned) j); 15342 - jsoff_t ioff = lkp(js, arr, istr, strlen(istr)); 15343 - jsoff_t joff = lkp(js, arr, jstr, strlen(jstr)); 15344 - jsval_t iv = loadval(js, ioff + sizeof(jsoff_t) * 2); 15345 - jsval_t jv = loadval(js, joff + sizeof(jsoff_t) * 2); 15346 - saveval(js, ioff + sizeof(jsoff_t) * 2, jv); 15347 - saveval(js, joff + sizeof(jsoff_t) * 2, iv); 15314 + jsval_t arr = mkarr(js); 15315 + for (jsoff_t i = 0; i < idx; i++) { 15316 + char idxstr[16]; 15317 + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15318 + js_mkprop_fast(js, arr, idxstr, idxlen, keys[i]); 15348 15319 } 15349 15320 15350 - jsval_t len_key = js_mkstr(js, "length", 6); 15351 - jsval_t len_val = tov((double) idx); 15352 - setprop(js, arr, len_key, len_val); 15321 + free(keys); 15322 + js_mkprop_fast(js, arr, "length", 6, tov((double) idx)); 15353 15323 return mkval(T_ARR, vdata(arr)); 15354 15324 } 15355 15325 ··· 15560 15530 arr = resolveprop(js, arr); 15561 15531 if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) return; 15562 15532 15563 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15533 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15564 15534 jsoff_t len = 0; 15565 - if (off != 0) { 15566 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15535 + if (len_off != 0) { 15536 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15567 15537 if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 15568 15538 } 15569 15539 15570 15540 char idxstr[16]; 15571 15541 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); 15572 - jsval_t key = js_mkstr(js, idxstr, idxlen); 15573 - setprop(js, arr, key, val); 15542 + js_mkprop_fast(js, arr, idxstr, idxlen, val); 15574 15543 15575 - jsval_t len_key = js_mkstr(js, "length", 6); 15576 - setprop(js, arr, len_key, tov((double)(len + 1))); 15544 + if (len_off != 0) saveval(js, len_off + sizeof(jsoff_t) * 2, tov((double)(len + 1))); 15545 + else js_mkprop_fast(js, arr, "length", 6, tov((double)(len + 1))); 15577 15546 } 15578 15547 15579 15548 static jsval_t builtin_array_pop(struct js *js, jsval_t *args, int nargs) { ··· 15764 15733 15765 15734 static jsval_t builtin_array_includes(struct js *js, jsval_t *args, int nargs) { 15766 15735 jsval_t arr = js->this_val; 15767 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 15736 + 15737 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15768 15738 return js_mkerr(js, "includes called on non-array"); 15769 - } 15770 15739 15771 15740 if (nargs == 0) return mkval(T_BOOL, 0); 15772 15741 jsval_t search = args[0]; 15773 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15742 + 15743 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15774 15744 jsoff_t len = 0; 15775 15745 15776 - if (off != 0) { 15777 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15778 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 15746 + if (len_off != 0) { 15747 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15748 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15779 15749 } 15750 + if (len == 0) return mkval(T_BOOL, 0); 15780 15751 15781 - for (jsoff_t i = 0; i < len; i++) { 15782 - char idxstr[16]; 15783 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15784 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 15785 - if (elem_off != 0) { 15786 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 15787 - if (vtype(elem) == vtype(search)) { 15788 - if (vtype(elem) == T_NUM && tod(elem) == tod(search)) { 15789 - return mkval(T_BOOL, 1); 15790 - } else if (vtype(elem) == T_BOOL && vdata(elem) == vdata(search)) { 15791 - return mkval(T_BOOL, 1); 15792 - } else if (vtype(elem) == T_STR) { 15793 - jsoff_t elem_len, elem_off_str = vstr(js, elem, &elem_len); 15794 - jsoff_t search_len, search_off = vstr(js, search, &search_len); 15795 - if (elem_len == search_len && memcmp(&js->mem[elem_off_str], &js->mem[search_off], elem_len) == 0) { 15796 - return mkval(T_BOOL, 1); 15797 - } 15798 - } else if ((vtype(elem) == T_OBJ || vtype(elem) == T_ARR || vtype(elem) == T_FUNC) && vdata(elem) == vdata(search)) { 15799 - return mkval(T_BOOL, 1); 15800 - } 15752 + ant_iter_t iter = js_prop_iter_begin(js, arr); 15753 + const char *key; size_t key_len; jsval_t val; 15754 + 15755 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 15756 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15757 + 15758 + unsigned parsed_idx = 0; 15759 + bool valid = true; 15760 + for (size_t i = 0; i < key_len && valid; i++) { 15761 + if (key[i] < '0' || key[i] > '9') valid = false; 15762 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 15763 + } 15764 + if (!valid || parsed_idx >= len) continue; 15765 + 15766 + if (vtype(val) == vtype(search)) { 15767 + bool match = false; 15768 + if (vtype(val) == T_NUM && tod(val) == tod(search)) match = true; 15769 + else if (vtype(val) == T_BOOL && vdata(val) == vdata(search)) match = true; 15770 + else if (vtype(val) == T_STR) { 15771 + jsoff_t vl, vo = vstr(js, val, &vl); 15772 + jsoff_t sl, so = vstr(js, search, &sl); 15773 + if (vl == sl && memcmp(&js->mem[vo], &js->mem[so], vl) == 0) match = true; 15774 + } 15775 + else if ((vtype(val) == T_OBJ || vtype(val) == T_ARR || vtype(val) == T_FUNC) && vdata(val) == vdata(search)) match = true; 15776 + 15777 + if (match) { 15778 + js_prop_iter_end(&iter); 15779 + return mkval(T_BOOL, 1); 15801 15780 } 15802 15781 } 15803 15782 } 15783 + 15784 + js_prop_iter_end(&iter); 15804 15785 return mkval(T_BOOL, 0); 15805 15786 } 15806 15787 15807 15788 static jsval_t builtin_array_every(struct js *js, jsval_t *args, int nargs) { 15808 15789 jsval_t arr = js->this_val; 15809 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 15790 + 15791 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15810 15792 return js_mkerr(js, "every called on non-array"); 15811 - } 15812 15793 15813 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 15794 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 15814 15795 return js_mkerr(js, "every requires a function argument"); 15815 - } 15816 15796 15817 15797 jsval_t callback = args[0]; 15818 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15819 - jsoff_t len = 0; 15820 15798 15821 - if (off != 0) { 15822 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15823 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 15799 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15800 + jsoff_t len = 0; 15801 + if (len_off != 0) { 15802 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15803 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15824 15804 } 15805 + if (len == 0) return mkval(T_BOOL, 1); 15825 15806 15826 - for (jsoff_t i = 0; i < len; i++) { 15827 - char idxstr[16]; 15828 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15829 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 15830 - if (elem_off == 0) continue; 15807 + ant_iter_t iter = js_prop_iter_begin(js, arr); 15808 + const char *key; 15809 + size_t key_len; 15810 + jsval_t val; 15811 + 15812 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 15813 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15814 + 15815 + unsigned parsed_idx = 0; 15816 + bool valid = true; 15817 + for (size_t i = 0; i < key_len && valid; i++) { 15818 + if (key[i] < '0' || key[i] > '9') valid = false; 15819 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 15820 + } 15821 + if (!valid || parsed_idx >= len) continue; 15831 15822 15832 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 15833 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 15823 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 15834 15824 jsval_t result = call_js_with_args(js, callback, call_args, 3); 15835 15825 15836 - if (is_err(result)) return result; 15837 - 15826 + if (is_err(result)) { 15827 + js_prop_iter_end(&iter); 15828 + return result; 15829 + } 15838 15830 if (!js_truthy(js, result)) { 15831 + js_prop_iter_end(&iter); 15839 15832 return mkval(T_BOOL, 0); 15840 15833 } 15841 15834 } 15835 + js_prop_iter_end(&iter); 15842 15836 15843 15837 return mkval(T_BOOL, 1); 15844 15838 } 15845 15839 15840 + static jsval_t builtin_array_forEach(struct js *js, jsval_t *args, int nargs) { 15841 + jsval_t arr = js->this_val; 15842 + 15843 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15844 + return js_mkerr(js, "forEach called on non-array"); 15845 + 15846 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 15847 + return js_mkerr(js, "forEach requires a function argument"); 15848 + 15849 + jsval_t callback = args[0]; 15850 + 15851 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15852 + jsoff_t len = 0; 15853 + if (len_off != 0) { 15854 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15855 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15856 + } 15857 + if (len == 0) return js_mkundef(); 15858 + 15859 + ant_iter_t iter = js_prop_iter_begin(js, arr); 15860 + const char *key; 15861 + size_t key_len; 15862 + jsval_t val; 15863 + 15864 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 15865 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15866 + 15867 + unsigned parsed_idx = 0; 15868 + bool valid = true; 15869 + for (size_t i = 0; i < key_len && valid; i++) { 15870 + if (key[i] < '0' || key[i] > '9') valid = false; 15871 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 15872 + } 15873 + if (!valid || parsed_idx >= len) continue; 15874 + 15875 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 15876 + jsval_t result = call_js_with_args(js, callback, call_args, 3); 15877 + 15878 + if (is_err(result)) { 15879 + js_prop_iter_end(&iter); 15880 + return result; 15881 + } 15882 + } 15883 + js_prop_iter_end(&iter); 15884 + 15885 + return js_mkundef(); 15886 + } 15887 + 15846 15888 static jsval_t builtin_array_reverse(struct js *js, jsval_t *args, int nargs) { 15847 - (void) args; 15848 - (void) nargs; 15889 + (void)args; (void)nargs; 15849 15890 jsval_t arr = js->this_val; 15850 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 15891 + 15892 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15851 15893 return js_mkerr(js, "reverse called on non-array"); 15852 - } 15853 15894 15854 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15895 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15855 15896 jsoff_t len = 0; 15856 - if (off != 0) { 15857 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15858 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 15897 + if (len_off != 0) { 15898 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15899 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15859 15900 } 15901 + if (len <= 1) return arr; 15860 15902 15861 - for (jsoff_t i = 0; i < len / 2; i++) { 15862 - jsoff_t j = len - 1 - i; 15863 - char idx_i[16], idx_j[16]; 15864 - snprintf(idx_i, sizeof(idx_i), "%u", (unsigned) i); 15865 - snprintf(idx_j, sizeof(idx_j), "%u", (unsigned) j); 15866 - 15867 - jsoff_t off_i = lkp(js, arr, idx_i, strlen(idx_i)); 15868 - jsoff_t off_j = lkp(js, arr, idx_j, strlen(idx_j)); 15903 + jsval_t *vals = malloc(len * sizeof(jsval_t)); 15904 + jsoff_t *offs = malloc(len * sizeof(jsoff_t)); 15905 + if (!vals || !offs) { free(vals); free(offs); return js_mkerr(js, "out of memory"); } 15906 + 15907 + jsoff_t count = 0; 15908 + ant_iter_t iter = js_prop_iter_begin(js, arr); 15909 + const char *key; 15910 + size_t key_len; 15911 + jsval_t val; 15912 + 15913 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 15914 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15869 15915 15870 - jsval_t val_i = off_i ? resolveprop(js, mkval(T_PROP, off_i)) : js_mkundef(); 15871 - jsval_t val_j = off_j ? resolveprop(js, mkval(T_PROP, off_j)) : js_mkundef(); 15916 + unsigned parsed_idx = 0; 15917 + bool valid = true; 15918 + for (size_t i = 0; i < key_len && valid; i++) { 15919 + if (key[i] < '0' || key[i] > '9') valid = false; 15920 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 15921 + } 15922 + if (!valid || parsed_idx >= len || count >= len) continue; 15872 15923 15873 - jsval_t key_i = js_mkstr(js, idx_i, strlen(idx_i)); 15874 - jsval_t key_j = js_mkstr(js, idx_j, strlen(idx_j)); 15875 - setprop(js, arr, key_i, val_j); 15876 - setprop(js, arr, key_j, val_i); 15924 + vals[count] = val; 15925 + offs[count] = iter.off; 15926 + count++; 15927 + } 15928 + js_prop_iter_end(&iter); 15929 + 15930 + for (jsoff_t i = 0; i < count / 2; i++) { 15931 + jsval_t tmp = vals[i]; 15932 + vals[i] = vals[count - 1 - i]; 15933 + vals[count - 1 - i] = tmp; 15877 15934 } 15878 15935 15936 + for (jsoff_t i = 0; i < count; i++) { 15937 + saveval(js, offs[i] + sizeof(jsoff_t) * 2, vals[i]); 15938 + } 15939 + 15940 + free(vals); 15941 + free(offs); 15879 15942 return arr; 15880 15943 } 15881 15944 15882 15945 static jsval_t builtin_array_map(struct js *js, jsval_t *args, int nargs) { 15883 15946 jsval_t arr = js->this_val; 15884 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 15947 + 15948 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15885 15949 return js_mkerr(js, "map called on non-array"); 15886 - } 15887 15950 15888 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 15951 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 15889 15952 return js_mkerr(js, "map requires a function argument"); 15890 - } 15891 15953 15892 15954 jsval_t callback = args[0]; 15893 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15955 + 15956 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15894 15957 jsoff_t len = 0; 15895 - if (off != 0) { 15896 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15897 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 15958 + if (len_off != 0) { 15959 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 15960 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15898 15961 } 15899 15962 15900 15963 jsval_t result = mkarr(js); 15901 15964 if (is_err(result)) return result; 15902 15965 15903 - for (jsoff_t i = 0; i < len; i++) { 15904 - char idxstr[16]; 15905 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15906 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 15966 + if (len == 0) { 15967 + js_mkprop_fast(js, result, "length", 6, tov(0.0)); 15968 + return mkval(T_ARR, vdata(result)); 15969 + } 15970 + 15971 + ant_iter_t iter = js_prop_iter_begin(js, arr); 15972 + const char *key; 15973 + size_t key_len; 15974 + jsval_t val; 15975 + 15976 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 15977 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15978 + 15979 + unsigned parsed_idx = 0; 15980 + bool valid = true; 15981 + for (size_t i = 0; i < key_len && valid; i++) { 15982 + if (key[i] < '0' || key[i] > '9') valid = false; 15983 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 15984 + } 15985 + if (!valid || parsed_idx >= len) continue; 15907 15986 15908 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 15909 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 15987 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 15910 15988 jsval_t mapped = call_js_with_args(js, callback, call_args, 3); 15911 - if (is_err(mapped)) return mapped; 15912 15989 15913 - jsval_t key = js_mkstr(js, idxstr, idxlen); 15914 - setprop(js, result, key, mapped); 15990 + if (is_err(mapped)) { 15991 + js_prop_iter_end(&iter); 15992 + return mapped; 15993 + } 15994 + 15995 + char idxstr[16]; 15996 + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), parsed_idx); 15997 + js_mkprop_fast(js, result, idxstr, idxlen, mapped); 15915 15998 } 15999 + js_prop_iter_end(&iter); 15916 16000 15917 - jsval_t len_key = js_mkstr(js, "length", 6); 15918 - setprop(js, result, len_key, tov((double) len)); 16001 + js_mkprop_fast(js, result, "length", 6, tov((double)len)); 15919 16002 return mkval(T_ARR, vdata(result)); 15920 16003 } 15921 16004 15922 16005 static jsval_t builtin_array_filter(struct js *js, jsval_t *args, int nargs) { 15923 16006 jsval_t arr = js->this_val; 15924 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16007 + 16008 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15925 16009 return js_mkerr(js, "filter called on non-array"); 15926 - } 15927 16010 15928 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16011 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 15929 16012 return js_mkerr(js, "filter requires a function argument"); 15930 - } 15931 16013 15932 16014 jsval_t callback = args[0]; 15933 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16015 + 16016 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15934 16017 jsoff_t len = 0; 15935 - if (off != 0) { 15936 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15937 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16018 + if (len_off != 0) { 16019 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16020 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15938 16021 } 15939 16022 15940 16023 jsval_t result = mkarr(js); 15941 16024 if (is_err(result)) return result; 16025 + 16026 + if (len == 0) { 16027 + js_mkprop_fast(js, result, "length", 6, tov(0.0)); 16028 + return mkval(T_ARR, vdata(result)); 16029 + } 16030 + 15942 16031 jsoff_t result_idx = 0; 16032 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16033 + const char *key; 16034 + size_t key_len; 16035 + jsval_t val; 15943 16036 15944 - for (jsoff_t i = 0; i < len; i++) { 15945 - char idxstr[16]; 15946 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15947 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 15948 - if (elem_off == 0) continue; 16037 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16038 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 15949 16039 15950 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 15951 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 16040 + unsigned parsed_idx = 0; 16041 + bool valid = true; 16042 + for (size_t i = 0; i < key_len && valid; i++) { 16043 + if (key[i] < '0' || key[i] > '9') valid = false; 16044 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16045 + } 16046 + if (!valid || parsed_idx >= len) continue; 16047 + 16048 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 15952 16049 jsval_t test = call_js_with_args(js, callback, call_args, 3); 15953 - if (is_err(test)) return test; 16050 + 16051 + if (is_err(test)) { 16052 + js_prop_iter_end(&iter); 16053 + return test; 16054 + } 15954 16055 15955 16056 if (js_truthy(js, test)) { 15956 - char res_idx[16]; 15957 - snprintf(res_idx, sizeof(res_idx), "%u", (unsigned) result_idx); 15958 - jsval_t key = js_mkstr(js, res_idx, strlen(res_idx)); 15959 - setprop(js, result, key, elem); 16057 + char idxstr[16]; 16058 + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)result_idx); 16059 + js_mkprop_fast(js, result, idxstr, idxlen, val); 15960 16060 result_idx++; 15961 16061 } 15962 16062 } 16063 + js_prop_iter_end(&iter); 15963 16064 15964 - jsval_t len_key = js_mkstr(js, "length", 6); 15965 - setprop(js, result, len_key, tov((double) result_idx)); 16065 + js_mkprop_fast(js, result, "length", 6, tov((double)result_idx)); 15966 16066 return mkval(T_ARR, vdata(result)); 15967 16067 } 15968 16068 15969 16069 static jsval_t builtin_array_reduce(struct js *js, jsval_t *args, int nargs) { 15970 16070 jsval_t arr = js->this_val; 15971 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16071 + 16072 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 15972 16073 return js_mkerr(js, "reduce called on non-array"); 15973 - } 15974 16074 15975 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16075 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 15976 16076 return js_mkerr(js, "reduce requires a function argument"); 15977 - } 15978 16077 15979 16078 jsval_t callback = args[0]; 15980 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16079 + bool has_initial = (nargs >= 2); 16080 + 16081 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 15981 16082 jsoff_t len = 0; 15982 - if (off != 0) { 15983 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 15984 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16083 + if (len_off != 0) { 16084 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16085 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 15985 16086 } 15986 16087 15987 - jsoff_t start_idx = 0; 15988 - jsval_t accumulator; 15989 - bool has_initial = (nargs >= 2); 16088 + if (len == 0) { 16089 + if (has_initial) return args[1]; 16090 + return js_mkerr(js, "reduce of empty array with no initial value"); 16091 + } 15990 16092 15991 - if (has_initial) { 15992 - accumulator = args[1]; 15993 - } else { 15994 - bool found = false; 15995 - for (jsoff_t i = 0; i < len; i++) { 15996 - char idxstr[16]; 15997 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 15998 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 15999 - if (elem_off != 0) { 16000 - accumulator = resolveprop(js, mkval(T_PROP, elem_off)); 16001 - start_idx = i + 1; 16002 - found = true; 16003 - break; 16004 - } 16093 + jsval_t accumulator = has_initial ? args[1] : js_mkundef(); 16094 + bool first = !has_initial; 16095 + 16096 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16097 + const char *key; 16098 + size_t key_len; 16099 + jsval_t val; 16100 + 16101 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16102 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16103 + 16104 + unsigned parsed_idx = 0; 16105 + bool valid = true; 16106 + for (size_t i = 0; i < key_len && valid; i++) { 16107 + if (key[i] < '0' || key[i] > '9') valid = false; 16108 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16005 16109 } 16006 - if (!found) return js_mkerr(js, "reduce of empty array with no initial value"); 16007 - } 16008 - 16009 - for (jsoff_t i = start_idx; i < len; i++) { 16010 - char idxstr[16]; 16011 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16012 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16013 - if (elem_off == 0) continue; 16110 + if (!valid || parsed_idx >= len) continue; 16014 16111 16015 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 16016 - jsval_t call_args[4] = { accumulator, elem, tov((double)i), arr }; 16112 + if (first) { 16113 + accumulator = val; 16114 + first = false; 16115 + continue; 16116 + } 16117 + 16118 + jsval_t call_args[4] = { accumulator, val, tov((double)parsed_idx), arr }; 16017 16119 accumulator = call_js_with_args(js, callback, call_args, 4); 16018 - if (is_err(accumulator)) return accumulator; 16120 + 16121 + if (is_err(accumulator)) { 16122 + js_prop_iter_end(&iter); 16123 + return accumulator; 16124 + } 16019 16125 } 16020 16126 16127 + js_prop_iter_end(&iter); 16128 + if (first) return js_mkerr(js, "reduce of empty array with no initial value"); 16129 + 16021 16130 return accumulator; 16022 16131 } 16023 16132 16024 16133 static void flat_helper(struct js *js, jsval_t arr, jsval_t result, jsoff_t *result_idx, int depth) { 16025 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16134 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16026 16135 jsoff_t len = 0; 16027 - if (off != 0) { 16028 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16029 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16136 + if (len_off != 0) { 16137 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16138 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16030 16139 } 16140 + if (len == 0) return; 16031 16141 16032 - for (jsoff_t i = 0; i < len; i++) { 16033 - char idxstr[16]; 16034 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16035 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16142 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16143 + const char *key; 16144 + size_t key_len; 16145 + jsval_t val; 16146 + 16147 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16148 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16149 + 16150 + unsigned parsed_idx = 0; 16151 + bool valid = true; 16152 + for (size_t i = 0; i < key_len && valid; i++) { 16153 + if (key[i] < '0' || key[i] > '9') valid = false; 16154 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16155 + } 16156 + if (!valid || parsed_idx >= len) continue; 16036 16157 16037 - if (elem_off != 0) { 16038 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 16039 - if (depth > 0 && (vtype(elem) == T_ARR || vtype(elem) == T_OBJ)) { 16040 - flat_helper(js, elem, result, result_idx, depth - 1); 16041 - } else { 16042 - char res_idx[16]; 16043 - snprintf(res_idx, sizeof(res_idx), "%u", (unsigned) *result_idx); 16044 - jsval_t key = js_mkstr(js, res_idx, strlen(res_idx)); 16045 - setprop(js, result, key, elem); 16046 - (*result_idx)++; 16047 - } 16158 + if (depth > 0 && (vtype(val) == T_ARR || vtype(val) == T_OBJ)) { 16159 + flat_helper(js, val, result, result_idx, depth - 1); 16160 + } else { 16161 + char idxstr[16]; 16162 + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)*result_idx); 16163 + js_mkprop_fast(js, result, idxstr, idxlen, val); 16164 + (*result_idx)++; 16048 16165 } 16049 16166 } 16167 + 16168 + js_prop_iter_end(&iter); 16050 16169 } 16051 16170 16052 16171 static jsval_t builtin_array_flat(struct js *js, jsval_t *args, int nargs) { ··· 16208 16327 16209 16328 static jsval_t builtin_array_find(struct js *js, jsval_t *args, int nargs) { 16210 16329 jsval_t arr = js->this_val; 16211 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16330 + 16331 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16212 16332 return js_mkerr(js, "find called on non-array"); 16213 - } 16214 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16333 + 16334 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 16215 16335 return js_mkerr(js, "find requires a function argument"); 16216 - } 16217 16336 16218 16337 jsval_t callback = args[0]; 16219 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16338 + 16339 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16220 16340 jsoff_t len = 0; 16221 - if (off != 0) { 16222 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16223 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16341 + if (len_off != 0) { 16342 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16343 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16224 16344 } 16345 + if (len == 0) return js_mkundef(); 16225 16346 16226 - for (jsoff_t i = 0; i < len; i++) { 16227 - char idxstr[16]; 16228 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16229 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16230 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16231 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 16347 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16348 + const char *key; 16349 + size_t key_len; 16350 + jsval_t val; 16351 + 16352 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16353 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16354 + 16355 + unsigned parsed_idx = 0; 16356 + bool valid = true; 16357 + for (size_t i = 0; i < key_len && valid; i++) { 16358 + if (key[i] < '0' || key[i] > '9') valid = false; 16359 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16360 + } 16361 + if (!valid || parsed_idx >= len) continue; 16362 + 16363 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 16232 16364 jsval_t result = call_js_with_args(js, callback, call_args, 3); 16233 - if (is_err(result)) return result; 16234 - if (js_truthy(js, result)) return elem; 16365 + 16366 + if (is_err(result)) { 16367 + js_prop_iter_end(&iter); 16368 + return result; 16369 + } 16370 + if (js_truthy(js, result)) { 16371 + js_prop_iter_end(&iter); 16372 + return val; 16373 + } 16235 16374 } 16375 + js_prop_iter_end(&iter); 16376 + 16236 16377 return js_mkundef(); 16237 16378 } 16238 16379 16239 16380 static jsval_t builtin_array_findIndex(struct js *js, jsval_t *args, int nargs) { 16240 16381 jsval_t arr = js->this_val; 16241 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16382 + 16383 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16242 16384 return js_mkerr(js, "findIndex called on non-array"); 16243 - } 16244 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16385 + 16386 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 16245 16387 return js_mkerr(js, "findIndex requires a function argument"); 16246 - } 16247 16388 16248 16389 jsval_t callback = args[0]; 16249 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16390 + 16391 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16250 16392 jsoff_t len = 0; 16251 - if (off != 0) { 16252 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16253 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16393 + if (len_off != 0) { 16394 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16395 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16254 16396 } 16397 + if (len == 0) return tov(-1); 16398 + 16399 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16400 + const char *key; 16401 + size_t key_len; 16402 + jsval_t val; 16255 16403 16256 - for (jsoff_t i = 0; i < len; i++) { 16257 - char idxstr[16]; 16258 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16259 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16260 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16261 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 16404 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16405 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16406 + 16407 + unsigned parsed_idx = 0; 16408 + bool valid = true; 16409 + for (size_t i = 0; i < key_len && valid; i++) { 16410 + if (key[i] < '0' || key[i] > '9') valid = false; 16411 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16412 + } 16413 + if (!valid || parsed_idx >= len) continue; 16414 + 16415 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 16262 16416 jsval_t result = call_js_with_args(js, callback, call_args, 3); 16263 - if (is_err(result)) return result; 16264 - if (js_truthy(js, result)) return tov((double)i); 16417 + 16418 + if (is_err(result)) { 16419 + js_prop_iter_end(&iter); 16420 + return result; 16421 + } 16422 + if (js_truthy(js, result)) { 16423 + js_prop_iter_end(&iter); 16424 + return tov((double)parsed_idx); 16425 + } 16265 16426 } 16427 + js_prop_iter_end(&iter); 16428 + 16266 16429 return tov(-1); 16267 16430 } 16268 16431 16269 16432 static jsval_t builtin_array_findLast(struct js *js, jsval_t *args, int nargs) { 16270 16433 jsval_t arr = js->this_val; 16271 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16434 + 16435 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16272 16436 return js_mkerr(js, "findLast called on non-array"); 16273 - } 16274 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16437 + 16438 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 16275 16439 return js_mkerr(js, "findLast requires a function argument"); 16276 - } 16277 16440 16278 16441 jsval_t callback = args[0]; 16279 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16442 + 16443 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16280 16444 jsoff_t len = 0; 16281 - if (off != 0) { 16282 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16283 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16445 + if (len_off != 0) { 16446 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16447 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16448 + } 16449 + if (len == 0) return js_mkundef(); 16450 + 16451 + jsval_t *vals = malloc(len * sizeof(jsval_t)); 16452 + unsigned *idxs = malloc(len * sizeof(unsigned)); 16453 + if (!vals || !idxs) { free(vals); free(idxs); return js_mkerr(js, "out of memory"); } 16454 + 16455 + jsoff_t count = 0; 16456 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16457 + const char *key; size_t key_len; jsval_t val; 16458 + 16459 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16460 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16461 + 16462 + unsigned parsed_idx = 0; 16463 + bool valid = true; 16464 + for (size_t i = 0; i < key_len && valid; i++) { 16465 + if (key[i] < '0' || key[i] > '9') valid = false; 16466 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16467 + } 16468 + if (!valid || parsed_idx >= len || count >= len) continue; 16469 + 16470 + vals[count] = val; 16471 + idxs[count] = parsed_idx; 16472 + count++; 16284 16473 } 16474 + js_prop_iter_end(&iter); 16285 16475 16286 - for (jsoff_t i = len; i > 0; i--) { 16287 - char idxstr[16]; 16288 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(i - 1)); 16289 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16290 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16291 - jsval_t call_args[3] = { elem, tov((double)(i - 1)), arr }; 16476 + for (jsoff_t i = count; i > 0; i--) { 16477 + jsval_t call_args[3] = { vals[i-1], tov((double)idxs[i-1]), arr }; 16292 16478 jsval_t result = call_js_with_args(js, callback, call_args, 3); 16293 - if (is_err(result)) return result; 16294 - if (js_truthy(js, result)) return elem; 16479 + 16480 + if (is_err(result)) { 16481 + free(vals); free(idxs); 16482 + return result; 16483 + } 16484 + if (js_truthy(js, result)) { 16485 + jsval_t found = vals[i-1]; 16486 + free(vals); free(idxs); 16487 + return found; 16488 + } 16295 16489 } 16490 + 16491 + free(vals); free(idxs); 16296 16492 return js_mkundef(); 16297 16493 } 16298 16494 16299 16495 static jsval_t builtin_array_findLastIndex(struct js *js, jsval_t *args, int nargs) { 16300 16496 jsval_t arr = js->this_val; 16301 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16497 + 16498 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16302 16499 return js_mkerr(js, "findLastIndex called on non-array"); 16303 - } 16304 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16500 + 16501 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 16305 16502 return js_mkerr(js, "findLastIndex requires a function argument"); 16306 - } 16307 16503 16308 16504 jsval_t callback = args[0]; 16309 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16505 + 16506 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16310 16507 jsoff_t len = 0; 16311 - if (off != 0) { 16312 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16313 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16508 + if (len_off != 0) { 16509 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16510 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16314 16511 } 16512 + if (len == 0) return tov(-1); 16315 16513 16316 - for (jsoff_t i = len; i > 0; i--) { 16317 - char idxstr[16]; 16318 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(i - 1)); 16319 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16320 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16321 - jsval_t call_args[3] = { elem, tov((double)(i - 1)), arr }; 16514 + jsval_t *vals = malloc(len * sizeof(jsval_t)); 16515 + unsigned *idxs = malloc(len * sizeof(unsigned)); 16516 + if (!vals || !idxs) { free(vals); free(idxs); return js_mkerr(js, "out of memory"); } 16517 + 16518 + jsoff_t count = 0; 16519 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16520 + const char *key; 16521 + size_t key_len; 16522 + jsval_t val; 16523 + 16524 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16525 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16526 + 16527 + unsigned parsed_idx = 0; 16528 + bool valid = true; 16529 + for (size_t i = 0; i < key_len && valid; i++) { 16530 + if (key[i] < '0' || key[i] > '9') valid = false; 16531 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16532 + } 16533 + if (!valid || parsed_idx >= len || count >= len) continue; 16534 + 16535 + vals[count] = val; 16536 + idxs[count] = parsed_idx; 16537 + count++; 16538 + } 16539 + js_prop_iter_end(&iter); 16540 + 16541 + for (jsoff_t i = count; i > 0; i--) { 16542 + jsval_t call_args[3] = { vals[i-1], tov((double)idxs[i-1]), arr }; 16322 16543 jsval_t result = call_js_with_args(js, callback, call_args, 3); 16323 - if (is_err(result)) return result; 16324 - if (js_truthy(js, result)) return tov((double)(i - 1)); 16544 + 16545 + if (is_err(result)) { 16546 + free(vals); free(idxs); 16547 + return result; 16548 + } 16549 + if (js_truthy(js, result)) { 16550 + unsigned found_idx = idxs[i-1]; 16551 + free(vals); free(idxs); 16552 + return tov((double)found_idx); 16553 + } 16325 16554 } 16555 + 16556 + free(vals); free(idxs); 16326 16557 return tov(-1); 16327 16558 } 16328 16559 ··· 16390 16621 return mkval(T_ARR, vdata(result)); 16391 16622 } 16392 16623 16393 - static jsval_t builtin_array_forEach(struct js *js, jsval_t *args, int nargs) { 16394 - jsval_t arr = js->this_val; 16395 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16396 - return js_mkerr(js, "forEach called on non-array"); 16397 - } 16398 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16399 - return js_mkerr(js, "forEach requires a function argument"); 16400 - } 16401 - 16402 - jsval_t callback = args[0]; 16403 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16404 - jsoff_t len = 0; 16405 - if (off != 0) { 16406 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16407 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16408 - } 16409 - 16410 - for (jsoff_t i = 0; i < len; i++) { 16411 - char idxstr[16]; 16412 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16413 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16414 - if (elem_off == 0) continue; 16415 - 16416 - jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); 16417 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 16418 - jsval_t result = call_js_with_args(js, callback, call_args, 3); 16419 - if (is_err(result)) return result; 16624 + static const char *js_tostring(struct js *js, jsval_t v) { 16625 + if (vtype(v) == T_STR) { 16626 + jsoff_t slen, off = vstr(js, v, &slen); 16627 + return (const char *)&js->mem[off]; 16420 16628 } 16421 - return js_mkundef(); 16629 + return js_str(js, v); 16422 16630 } 16423 16631 16424 16632 static int js_compare_values(struct js *js, jsval_t a, jsval_t b, jsval_t compareFn) { 16425 - if (vtype(compareFn) == T_FUNC) { 16633 + uint8_t t = vtype(compareFn); 16634 + if (t == T_FUNC || t == T_CFUNC) { 16426 16635 jsval_t call_args[2] = { a, b }; 16427 16636 jsval_t result = call_js_with_args(js, compareFn, call_args, 2); 16428 - if (vtype(result) == T_NUM) return (int) tod(result); 16637 + if (vtype(result) == T_NUM) return (int)tod(result); 16429 16638 return 0; 16430 16639 } 16431 - const char *sa = js_str(js, a); 16432 - const char *sb = js_str(js, b); 16433 - return strcmp(sa, sb); 16640 + 16641 + if (vtype(a) == T_STR && vtype(b) == T_STR) { 16642 + jsoff_t len_a, len_b; 16643 + const char *sa = (const char *)&js->mem[vstr(js, a, &len_a)]; 16644 + const char *sb = (const char *)&js->mem[vstr(js, b, &len_b)]; 16645 + return strcmp(sa, sb); 16646 + } 16647 + 16648 + const char *sa = js_tostring(js, a); 16649 + size_t len = strlen(sa); 16650 + 16651 + char *copy = alloca(len + 1); 16652 + memcpy(copy, sa, len + 1); 16653 + 16654 + return strcmp(copy, js_tostring(js, b)); 16434 16655 } 16435 16656 16436 16657 static jsval_t builtin_array_indexOf(struct js *js, jsval_t *args, int nargs) { ··· 16638 16859 16639 16860 static jsval_t builtin_array_some(struct js *js, jsval_t *args, int nargs) { 16640 16861 jsval_t arr = js->this_val; 16641 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16862 + 16863 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16642 16864 return js_mkerr(js, "some called on non-array"); 16643 - } 16644 - if (nargs == 0 || vtype(args[0]) != T_FUNC) { 16865 + 16866 + if (nargs == 0 || (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC)) 16645 16867 return js_mkerr(js, "some requires a function argument"); 16646 - } 16647 16868 16648 16869 jsval_t callback = args[0]; 16649 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16870 + 16871 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16650 16872 jsoff_t len = 0; 16651 - if (off != 0) { 16652 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16653 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16873 + if (len_off != 0) { 16874 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 16875 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16654 16876 } 16877 + if (len == 0) return mkval(T_BOOL, 0); 16655 16878 16656 - for (jsoff_t i = 0; i < len; i++) { 16657 - char idxstr[16]; 16658 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16659 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16660 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16661 - jsval_t call_args[3] = { elem, tov((double)i), arr }; 16879 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16880 + const char *key; 16881 + size_t key_len; 16882 + jsval_t val; 16883 + 16884 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16885 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16886 + 16887 + unsigned parsed_idx = 0; 16888 + bool valid = true; 16889 + for (size_t i = 0; i < key_len && valid; i++) { 16890 + if (key[i] < '0' || key[i] > '9') valid = false; 16891 + else parsed_idx = parsed_idx * 10 + (key[i] - '0'); 16892 + } 16893 + if (!valid || parsed_idx >= len) continue; 16894 + 16895 + jsval_t call_args[3] = { val, tov((double)parsed_idx), arr }; 16662 16896 jsval_t result = call_js_with_args(js, callback, call_args, 3); 16663 - if (is_err(result)) return result; 16664 - if (js_truthy(js, result)) return mkval(T_BOOL, 1); 16897 + 16898 + if (is_err(result)) { 16899 + js_prop_iter_end(&iter); 16900 + return result; 16901 + } 16902 + if (js_truthy(js, result)) { 16903 + js_prop_iter_end(&iter); 16904 + return mkval(T_BOOL, 1); 16905 + } 16665 16906 } 16907 + js_prop_iter_end(&iter); 16908 + 16666 16909 return mkval(T_BOOL, 0); 16667 16910 } 16668 16911 16669 16912 static jsval_t builtin_array_sort(struct js *js, jsval_t *args, int nargs) { 16670 16913 jsval_t arr = js->this_val; 16671 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16914 + jsval_t compareFn = js_mkundef(); 16915 + jsval_t *vals = NULL, *keys = NULL, *temp_vals = NULL, *temp_keys = NULL; 16916 + jsoff_t *offs = NULL; 16917 + jsoff_t count = 0, undef_count = 0, len = 0; 16918 + 16919 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16672 16920 return js_mkerr(js, "sort called on non-array"); 16673 - } 16674 16921 16675 - jsval_t compareFn = js_mkundef(); 16676 - if (nargs >= 1 && vtype(args[0]) != T_UNDEF) { 16677 - if (vtype(args[0]) != T_FUNC && vtype(args[0]) != T_CFUNC) { 16678 - return js_mkerr_typed(js, JS_ERR_TYPE, "compareFn must be a function or undefined"); 16679 - } 16680 - compareFn = args[0]; 16922 + if (nargs >= 1) { 16923 + uint8_t t = vtype(args[0]); 16924 + if (t == T_FUNC || t == T_CFUNC) compareFn = args[0]; 16925 + else if (t != T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "compareFn must be a function or undefined"); 16681 16926 } 16682 16927 16683 16928 jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16684 - jsoff_t len = 0; 16685 16929 if (off != 0) { 16686 16930 jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16687 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16931 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16688 16932 } 16933 + if (len == 0) return arr; 16689 16934 16690 - for (jsoff_t i = 0; i < len; i++) { 16691 - for (jsoff_t j = i + 1; j < len; j++) { 16692 - char idx_i[16], idx_j[16]; 16693 - snprintf(idx_i, sizeof(idx_i), "%u", (unsigned) i); 16694 - snprintf(idx_j, sizeof(idx_j), "%u", (unsigned) j); 16695 - 16696 - jsoff_t off_i = lkp(js, arr, idx_i, strlen(idx_i)); 16697 - jsoff_t off_j = lkp(js, arr, idx_j, strlen(idx_j)); 16698 - jsval_t val_i = off_i ? resolveprop(js, mkval(T_PROP, off_i)) : js_mkundef(); 16699 - jsval_t val_j = off_j ? resolveprop(js, mkval(T_PROP, off_j)) : js_mkundef(); 16935 + vals = malloc(len * sizeof(jsval_t)); 16936 + offs = malloc(len * sizeof(jsoff_t)); 16937 + if (!vals || !offs) goto oom; 16938 + 16939 + ant_iter_t iter = js_prop_iter_begin(js, arr); 16940 + const char *key; 16941 + size_t key_len; 16942 + jsval_t val; 16943 + 16944 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 16945 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 16946 + 16947 + unsigned idx = 0; 16948 + bool valid = true; 16949 + for (size_t i = 0; i < key_len && valid; i++) { 16950 + if (key[i] < '0' || key[i] > '9') valid = false; 16951 + else idx = idx * 10 + (key[i] - '0'); 16952 + } 16953 + if (!valid || idx >= len || (count + undef_count) >= len) continue; 16954 + 16955 + offs[count + undef_count] = iter.off; 16956 + if (vtype(val) == T_UNDEF) undef_count++; 16957 + else vals[count++] = val; 16958 + } 16959 + 16960 + js_prop_iter_end(&iter); 16961 + if (count <= 1) goto writeback; 16962 + 16963 + bool use_keys = (vtype(compareFn) == T_UNDEF); 16964 + if (use_keys) { 16965 + keys = malloc(count * sizeof(jsval_t)); 16966 + if (!keys) goto oom; 16967 + for (jsoff_t i = 0; i < count; i++) { 16968 + const char *s = js_tostring(js, vals[i]); 16969 + keys[i] = js_mkstr(js, s, strlen(s)); 16970 + } 16971 + } 16972 + 16973 + temp_vals = malloc(count * sizeof(jsval_t)); 16974 + if (use_keys) temp_keys = malloc(count * sizeof(jsval_t)); 16975 + if (!temp_vals || (use_keys && !temp_keys)) goto oom; 16976 + 16977 + for (jsoff_t width = 1; width < count; width *= 2) { 16978 + for (jsoff_t left = 0; left < count; left += width * 2) { 16979 + jsoff_t mid = left + width; 16980 + jsoff_t right = (mid + width < count) ? mid + width : count; 16981 + if (mid >= count) break; 16700 16982 16701 - if (js_compare_values(js, val_i, val_j, compareFn) > 0) { 16702 - jsval_t key_i = js_mkstr(js, idx_i, strlen(idx_i)); 16703 - jsval_t key_j = js_mkstr(js, idx_j, strlen(idx_j)); 16704 - setprop(js, arr, key_i, val_j); 16705 - setprop(js, arr, key_j, val_i); 16983 + jsoff_t i = left, j = mid, k = 0; 16984 + while (i < mid && j < right) { 16985 + int cmp; 16986 + if (use_keys) { 16987 + jsoff_t len_a, len_b; 16988 + const char *sa = (const char *)&js->mem[vstr(js, keys[i], &len_a)]; 16989 + const char *sb = (const char *)&js->mem[vstr(js, keys[j], &len_b)]; 16990 + cmp = strcmp(sa, sb); 16991 + } else { 16992 + cmp = js_compare_values(js, vals[i], vals[j], compareFn); 16993 + } 16994 + if (cmp <= 0) { 16995 + temp_vals[k] = vals[i]; 16996 + if (use_keys) temp_keys[k] = keys[i]; 16997 + k++; i++; 16998 + } else { 16999 + temp_vals[k] = vals[j]; 17000 + if (use_keys) temp_keys[k] = keys[j]; 17001 + k++; j++; 17002 + } 16706 17003 } 17004 + while (i < mid) { 17005 + temp_vals[k] = vals[i]; 17006 + if (use_keys) temp_keys[k] = keys[i]; 17007 + k++; i++; 17008 + } 17009 + while (j < right) { 17010 + temp_vals[k] = vals[j]; 17011 + if (use_keys) temp_keys[k] = keys[j]; 17012 + k++; j++; 17013 + } 17014 + 17015 + memcpy(&vals[left], temp_vals, k * sizeof(jsval_t)); 17016 + if (use_keys) memcpy(&keys[left], temp_keys, k * sizeof(jsval_t)); 16707 17017 } 16708 17018 } 17019 + 17020 + writeback: 17021 + for (jsoff_t i = 0; i < count; i++) 17022 + saveval(js, offs[i] + sizeof(jsoff_t) * 2, vals[i]); 17023 + for (jsoff_t i = 0; i < undef_count; i++) 17024 + saveval(js, offs[count + i] + sizeof(jsoff_t) * 2, js_mkundef()); 17025 + 17026 + free(temp_keys); 17027 + free(temp_vals); 17028 + free(keys); 17029 + free(vals); 17030 + free(offs); 16709 17031 return arr; 17032 + 17033 + oom: 17034 + free(temp_keys); 17035 + free(temp_vals); 17036 + free(keys); 17037 + free(vals); 17038 + free(offs); 17039 + return js_mkerr(js, "out of memory"); 16710 17040 } 16711 17041 16712 17042 static jsval_t builtin_array_splice(struct js *js, jsval_t *args, int nargs) { ··· 16845 17175 return arr; 16846 17176 } 16847 17177 16848 - static jsval_t builtin_array_toReversed(struct js *js, jsval_t *args, int nargs) { 16849 - (void) args; 16850 - (void) nargs; 17178 + static jsval_t builtin_array_toSorted(struct js *js, jsval_t *args, int nargs) { 16851 17179 jsval_t arr = js->this_val; 16852 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16853 - return js_mkerr(js, "toReversed called on non-array"); 16854 - } 16855 17180 16856 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 17181 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 17182 + return js_mkerr(js, "toSorted called on non-array"); 17183 + 17184 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16857 17185 jsoff_t len = 0; 16858 - if (off != 0) { 16859 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16860 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 17186 + if (len_off != 0) { 17187 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 17188 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16861 17189 } 16862 17190 16863 17191 jsval_t result = mkarr(js); 16864 17192 if (is_err(result)) return result; 16865 17193 16866 - for (jsoff_t i = 0; i < len; i++) { 16867 - char src[16], dst[16]; 16868 - snprintf(src, sizeof(src), "%u", (unsigned)(len - 1 - i)); 16869 - snprintf(dst, sizeof(dst), "%u", (unsigned) i); 16870 - jsoff_t elem_off = lkp(js, arr, src, strlen(src)); 16871 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16872 - jsval_t key = js_mkstr(js, dst, strlen(dst)); 16873 - setprop(js, result, key, elem); 17194 + ant_iter_t iter = js_prop_iter_begin(js, arr); 17195 + const char *key; 17196 + size_t key_len; 17197 + jsval_t val; 17198 + 17199 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 17200 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 17201 + js_mkprop_fast(js, result, key, key_len, val); 16874 17202 } 16875 17203 16876 - jsval_t len_key = js_mkstr(js, "length", 6); 16877 - setprop(js, result, len_key, tov((double) len)); 17204 + js_prop_iter_end(&iter); 17205 + js_mkprop_fast(js, result, "length", 6, tov((double)len)); 17206 + 17207 + jsval_t saved_this = js->this_val; 17208 + js->this_val = result; 17209 + jsval_t sorted = builtin_array_sort(js, args, nargs); 17210 + js->this_val = saved_this; 17211 + 17212 + if (is_err(sorted)) return sorted; 16878 17213 return mkval(T_ARR, vdata(result)); 16879 17214 } 16880 17215 16881 - static jsval_t builtin_array_toSorted(struct js *js, jsval_t *args, int nargs) { 17216 + static jsval_t builtin_array_toReversed(struct js *js, jsval_t *args, int nargs) { 17217 + (void)args; (void)nargs; 16882 17218 jsval_t arr = js->this_val; 16883 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 16884 - return js_mkerr(js, "toSorted called on non-array"); 16885 - } 16886 17219 16887 - jsval_t compareFn = (nargs >= 1 && vtype(args[0]) == T_FUNC) ? args[0] : js_mkundef(); 17220 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 17221 + return js_mkerr(js, "toReversed called on non-array"); 16888 17222 16889 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 17223 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16890 17224 jsoff_t len = 0; 16891 - if (off != 0) { 16892 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16893 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 17225 + if (len_off != 0) { 17226 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 17227 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16894 17228 } 16895 17229 16896 17230 jsval_t result = mkarr(js); 16897 17231 if (is_err(result)) return result; 16898 17232 16899 - for (jsoff_t i = 0; i < len; i++) { 16900 - char idxstr[16]; 16901 - size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); 16902 - jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); 16903 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16904 - jsval_t key = js_mkstr(js, idxstr, idxlen); 16905 - setprop(js, result, key, elem); 16906 - } 16907 - jsval_t len_key = js_mkstr(js, "length", 6); 16908 - setprop(js, result, len_key, tov((double) len)); 17233 + ant_iter_t iter = js_prop_iter_begin(js, arr); 17234 + const char *key; 17235 + size_t key_len; 17236 + jsval_t val; 16909 17237 16910 - for (jsoff_t i = 0; i < len; i++) { 16911 - for (jsoff_t j = i + 1; j < len; j++) { 16912 - char idx_i[16], idx_j[16]; 16913 - snprintf(idx_i, sizeof(idx_i), "%u", (unsigned) i); 16914 - snprintf(idx_j, sizeof(idx_j), "%u", (unsigned) j); 16915 - 16916 - jsoff_t off_i = lkp(js, result, idx_i, strlen(idx_i)); 16917 - jsoff_t off_j = lkp(js, result, idx_j, strlen(idx_j)); 16918 - jsval_t val_i = off_i ? resolveprop(js, mkval(T_PROP, off_i)) : js_mkundef(); 16919 - jsval_t val_j = off_j ? resolveprop(js, mkval(T_PROP, off_j)) : js_mkundef(); 16920 - 16921 - if (js_compare_values(js, val_i, val_j, compareFn) > 0) { 16922 - jsval_t key_i = js_mkstr(js, idx_i, strlen(idx_i)); 16923 - jsval_t key_j = js_mkstr(js, idx_j, strlen(idx_j)); 16924 - setprop(js, result, key_i, val_j); 16925 - setprop(js, result, key_j, val_i); 16926 - } 16927 - } 17238 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 17239 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 17240 + js_mkprop_fast(js, result, key, key_len, val); 16928 17241 } 17242 + 17243 + js_prop_iter_end(&iter); 17244 + js_mkprop_fast(js, result, "length", 6, tov((double)len)); 17245 + 17246 + jsval_t saved_this = js->this_val; 17247 + js->this_val = result; 17248 + jsval_t reversed = builtin_array_reverse(js, NULL, 0); 17249 + js->this_val = saved_this; 17250 + 17251 + if (is_err(reversed)) return reversed; 16929 17252 return mkval(T_ARR, vdata(result)); 16930 17253 } 16931 17254 16932 17255 static jsval_t builtin_array_toSpliced(struct js *js, jsval_t *args, int nargs) { 16933 17256 jsval_t arr = js->this_val; 16934 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 17257 + 17258 + if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) 16935 17259 return js_mkerr(js, "toSpliced called on non-array"); 16936 - } 16937 17260 16938 - jsoff_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); 17261 + jsoff_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); 16939 17262 jsoff_t len = 0; 16940 - if (off != 0) { 16941 - jsval_t len_val = resolveprop(js, mkval(T_PROP, off)); 16942 - if (vtype(len_val) == T_NUM) len = (jsoff_t) tod(len_val); 16943 - } 16944 - 16945 - int start = 0; 16946 - if (nargs >= 1 && vtype(args[0]) == T_NUM) { 16947 - start = (int) tod(args[0]); 16948 - if (start < 0) start = (int)len + start; 16949 - if (start < 0) start = 0; 16950 - if (start > (int)len) start = (int)len; 17263 + if (len_off != 0) { 17264 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 17265 + if (vtype(len_val) == T_NUM) len = (jsoff_t)tod(len_val); 16951 17266 } 16952 17267 16953 - int deleteCount = (int)len - start; 16954 - if (nargs >= 2 && vtype(args[1]) == T_NUM) { 16955 - deleteCount = (int) tod(args[1]); 16956 - if (deleteCount < 0) deleteCount = 0; 16957 - if (deleteCount > (int)len - start) deleteCount = (int)len - start; 16958 - } 16959 - 16960 - int insertCount = nargs > 2 ? nargs - 2 : 0; 16961 - 16962 17268 jsval_t result = mkarr(js); 16963 17269 if (is_err(result)) return result; 16964 - jsoff_t result_idx = 0; 16965 17270 16966 - for (int i = 0; i < start; i++) { 16967 - char src[16], dst[16]; 16968 - snprintf(src, sizeof(src), "%u", (unsigned) i); 16969 - snprintf(dst, sizeof(dst), "%u", (unsigned) result_idx); 16970 - jsoff_t elem_off = lkp(js, arr, src, strlen(src)); 16971 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16972 - jsval_t key = js_mkstr(js, dst, strlen(dst)); 16973 - setprop(js, result, key, elem); 16974 - result_idx++; 16975 - } 17271 + ant_iter_t iter = js_prop_iter_begin(js, arr); 17272 + const char *key; 17273 + size_t key_len; 17274 + jsval_t val; 16976 17275 16977 - for (int i = 0; i < insertCount; i++) { 16978 - char dst[16]; 16979 - snprintf(dst, sizeof(dst), "%u", (unsigned) result_idx); 16980 - jsval_t key = js_mkstr(js, dst, strlen(dst)); 16981 - setprop(js, result, key, args[2 + i]); 16982 - result_idx++; 17276 + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 17277 + if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; 17278 + js_mkprop_fast(js, result, key, key_len, val); 16983 17279 } 16984 17280 16985 - for (int i = start + deleteCount; i < (int)len; i++) { 16986 - char src[16], dst[16]; 16987 - snprintf(src, sizeof(src), "%u", (unsigned) i); 16988 - snprintf(dst, sizeof(dst), "%u", (unsigned) result_idx); 16989 - jsoff_t elem_off = lkp(js, arr, src, strlen(src)); 16990 - jsval_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); 16991 - jsval_t key = js_mkstr(js, dst, strlen(dst)); 16992 - setprop(js, result, key, elem); 16993 - result_idx++; 16994 - } 17281 + js_prop_iter_end(&iter); 17282 + js_mkprop_fast(js, result, "length", 6, tov((double)len)); 17283 + 17284 + jsval_t saved_this = js->this_val; 17285 + js->this_val = result; 17286 + builtin_array_splice(js, args, nargs); 17287 + js->this_val = saved_this; 16995 17288 16996 - jsval_t len_key = js_mkstr(js, "length", 6); 16997 - setprop(js, result, len_key, tov((double) result_idx)); 16998 17289 return mkval(T_ARR, vdata(result)); 16999 17290 } 17000 17291 ··· 20935 21226 if (alias_name) { 20936 21227 setprop(js, js->module_ns, js_mkstr(js, alias_name, alias_len), ns); 20937 21228 } else if (vtype(ns) == T_OBJ) { 20938 - js_prop_iter_t iter = js_prop_iter_begin(js, ns); 20939 - const char *key; 20940 - size_t key_len; 20941 - jsval_t value; 21229 + ant_iter_t iter = js_prop_iter_begin(js, ns); 21230 + const char *key; size_t key_len; jsval_t value; 20942 21231 20943 21232 while (js_prop_iter_next(&iter, &key, &key_len, &value)) { 20944 21233 setprop(js, js->module_ns, js_mkstr(js, key, key_len), resolveprop(js, value)); ··· 22710 22999 if (desc && !desc->configurable) return false; 22711 23000 22712 23001 jsoff_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); 23002 + jsoff_t tail = loadoff(js, obj_off + sizeof(jsoff_t) + sizeof(jsoff_t)); 23003 + 22713 23004 if (first_prop == prop_off) { 22714 23005 jsoff_t deleted_next = loadoff(js, prop_off) & ~FLAGMASK; 22715 23006 jsoff_t current = loadoff(js, obj_off); 23007 + 22716 23008 saveoff(js, obj_off, (deleted_next & ~3U) | (current & (FLAGMASK | 3U))); 23009 + if (tail == prop_off) saveoff(js, obj_off + sizeof(jsoff_t) + sizeof(jsoff_t), 0); 23010 + 22717 23011 invalidate_prop_cache(js, obj_off, prop_off); 22718 23012 js->needs_gc = true; 23013 + 22719 23014 return true; 22720 23015 } 22721 23016 22722 23017 jsoff_t prev = first_prop; 22723 - while (prev != 0) { 22724 - jsoff_t next_prop = loadoff(js, prev) & ~FLAGMASK; 22725 - if (next_prop == prop_off) { 22726 - jsoff_t deleted_next = loadoff(js, prop_off) & ~FLAGMASK; 23018 + while (prev != 0 && prev < js->brk) { 23019 + jsoff_t next = loadoff(js, prev) & ~(3U | FLAGMASK); 23020 + if (next == prop_off) { 23021 + jsoff_t deleted_next = loadoff(js, prop_off) & ~(3U | FLAGMASK); 22727 23022 jsoff_t prev_flags = loadoff(js, prev) & FLAGMASK; 23023 + 22728 23024 saveoff(js, prev, deleted_next | prev_flags); 23025 + if (tail == prop_off) saveoff(js, obj_off + sizeof(jsoff_t) + sizeof(jsoff_t), prev); 23026 + 22729 23027 invalidate_prop_cache(js, obj_off, prop_off); 22730 23028 js->needs_gc = true; 23029 + 22731 23030 return true; 22732 23031 } 22733 - prev = next_prop; 23032 + prev = next; 22734 23033 } 22735 23034 22736 23035 return false; ··· 23212 23511 return js_call_internal(js, func, bound_this, args, nargs, true); 23213 23512 } 23214 23513 23215 - js_prop_iter_t js_prop_iter_begin(struct js *js, jsval_t obj) { 23216 - js_prop_iter_t iter = {0}; 23217 - iter.obj = obj; 23218 - iter.js_internal = (void *)js; 23514 + ant_iter_t js_prop_iter_begin(struct js *js, jsval_t obj) { 23515 + ant_iter_t iter = {.ctx = js, .off = 0}; 23219 23516 23220 - if (vtype(obj) == T_OBJ || vtype(obj) == T_ARR || vtype(obj) == T_FUNC) { 23221 - jsval_t check_obj = (vtype(obj) == T_FUNC) ? mkval(T_OBJ, vdata(obj)) : obj; 23222 - jsoff_t next = loadoff(js, (jsoff_t) vdata(check_obj)) & ~(3U | FLAGMASK); 23223 - iter.current = (void *)(uintptr_t)next; 23517 + uint8_t t = vtype(obj); 23518 + if (t == T_OBJ || t == T_ARR || t == T_FUNC) { 23519 + iter.off = (jsoff_t)vdata(obj); 23224 23520 } 23225 23521 23226 23522 return iter; 23227 23523 } 23228 23524 23229 - bool js_prop_iter_next(js_prop_iter_t *iter, const char **key, size_t *key_len, jsval_t *value) { 23230 - if (!iter || !iter->js_internal) return false; 23525 + bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, jsval_t *value) { 23526 + if (!iter || !iter->ctx) return false; 23231 23527 23232 - struct js *js = (struct js *)iter->js_internal; 23233 - jsoff_t next = (jsoff_t)(uintptr_t)iter->current; 23528 + ant_t *js = (ant_t *)iter->ctx; 23529 + jsoff_t next = loadoff(js, iter->off) & ~(3U | FLAGMASK); 23234 23530 23235 23531 while (next < js->brk && next != 0) { 23236 23532 jsoff_t header = loadoff(js, next); ··· 23239 23535 } 23240 23536 23241 23537 if (next >= js->brk || next == 0) return false; 23538 + iter->off = next; 23242 23539 23243 - jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 23244 - jsval_t val = loadval(js, next + (jsoff_t) (sizeof(next) + sizeof(koff))); 23540 + jsoff_t koff = loadoff(js, next + (jsoff_t)sizeof(jsoff_t)); 23541 + jsval_t val = loadval(js, next + (jsoff_t)(sizeof(jsoff_t) * 2)); 23245 23542 23246 23543 if (key) { 23247 23544 jsoff_t klen = offtolen(loadoff(js, koff)); 23248 - *key = (const char *) &js->mem[koff + sizeof(koff)]; 23545 + *key = (const char *)&js->mem[koff + sizeof(jsoff_t)]; 23249 23546 if (key_len) *key_len = klen; 23250 23547 } 23251 - 23252 23548 if (value) *value = val; 23253 23549 23254 - jsoff_t next_off = loadoff(js, next) & ~(3U | FLAGMASK); 23255 - while (next_off < js->brk && next_off != 0) { 23256 - jsoff_t header = loadoff(js, next_off); 23257 - if (!is_slot_prop(header)) break; 23258 - next_off = next_prop(header); 23259 - } 23260 - iter->current = (void *)(uintptr_t)next_off; 23261 23550 return true; 23262 23551 } 23263 23552 23264 - void js_prop_iter_end(js_prop_iter_t *iter) { 23265 - if (iter) { 23266 - iter->current = NULL; 23267 - iter->js_internal = NULL; 23268 - } 23553 + void js_prop_iter_end(ant_iter_t *iter) { 23554 + if (iter) { iter->off = 0; iter->ctx = NULL; } 23269 23555 } 23270 23556 23271 23557 jsval_t js_mkpromise(struct js *js) { return mkpromise(js); }
+35 -24
src/gc.c
··· 31 31 } gc_work_queue_t; 32 32 33 33 typedef struct { 34 - struct js *js; 34 + ant_t *js; 35 35 uint8_t *new_mem; 36 - jsoff_t new_brk; 37 - jsoff_t new_size; 38 36 uint8_t *mark_bits; 39 37 gc_forward_table_t fwd; 40 38 gc_work_queue_t work; 39 + jsoff_t new_brk; 40 + jsoff_t new_size; 41 + bool failed; 41 42 } gc_ctx_t; 42 43 43 44 static jsval_t gc_update_val(gc_ctx_t *ctx, jsval_t val); ··· 195 196 memcpy(&mem[off], &val, sizeof(val)); 196 197 } 197 198 198 - static jsoff_t gc_esize(jsoff_t w) { 199 - jsoff_t cleaned = w & ~FLAGMASK; 200 - switch (cleaned & 3U) { 201 - case T_OBJ: return (jsoff_t)(sizeof(jsoff_t) + sizeof(jsoff_t)); 202 - case T_PROP: return (jsoff_t)(sizeof(jsoff_t) + sizeof(jsoff_t) + sizeof(jsval_t)); 203 - case T_STR: return (jsoff_t)(sizeof(jsoff_t) + ((cleaned >> 2) + 3) / 4 * 4); 204 - default: return (jsoff_t)~0U; 205 - } 206 - } 207 - 208 199 static jsoff_t gc_alloc(gc_ctx_t *ctx, size_t size) { 209 200 size = (size + 3) / 4 * 4; 210 - if (ctx->new_brk + size > ctx->new_size) return (jsoff_t)~0; 201 + if (ctx->new_brk + size > ctx->new_size) { 202 + ctx->failed = true; 203 + return (jsoff_t)~0; 204 + } 211 205 jsoff_t off = ctx->new_brk; 212 206 ctx->new_brk += (jsoff_t)size; 213 207 return off; ··· 238 232 jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 239 233 if ((header & 3) != T_STR) return old_off; 240 234 241 - jsoff_t size = gc_esize(header); 235 + jsoff_t size = esize(header); 242 236 if (size == (jsoff_t)~0) return old_off; 243 237 244 238 new_off = gc_alloc(ctx, size); 245 239 if (new_off == (jsoff_t)~0) return old_off; 246 240 247 241 memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 248 - fwd_add(&ctx->fwd, old_off, new_off); 242 + if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; 249 243 mark_set(ctx, old_off); 250 244 251 245 return new_off; ··· 265 259 if (new_off == (jsoff_t)~0) return old_off; 266 260 267 261 memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], total); 268 - fwd_add(&ctx->fwd, old_off, new_off); 262 + if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; 269 263 mark_set(ctx, old_off); 270 264 271 265 return new_off; ··· 280 274 jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 281 275 if ((header & 3) != T_OBJ) return old_off; 282 276 283 - jsoff_t size = gc_esize(header); 277 + jsoff_t size = esize(header); 284 278 if (size == (jsoff_t)~0) return old_off; 285 279 286 280 new_off = gc_alloc(ctx, size); 287 281 if (new_off == (jsoff_t)~0) return old_off; 288 282 289 283 memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 290 - fwd_add(&ctx->fwd, old_off, new_off); 284 + if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; 291 285 mark_set(ctx, old_off); 292 - work_push(&ctx->work, old_off); 286 + if (!work_push(&ctx->work, old_off)) ctx->failed = true; 293 287 294 288 return new_off; 295 289 } ··· 303 297 jsoff_t header = gc_loadoff(ctx->js->mem, old_off); 304 298 if ((header & 3) != T_PROP) return old_off; 305 299 306 - jsoff_t size = gc_esize(header); 300 + jsoff_t size = esize(header); 307 301 if (size == (jsoff_t)~0) return old_off; 308 302 309 303 new_off = gc_alloc(ctx, size); 310 304 if (new_off == (jsoff_t)~0) return old_off; 311 305 312 306 memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); 313 - fwd_add(&ctx->fwd, old_off, new_off); 307 + if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; 314 308 mark_set(ctx, old_off); 315 - work_push(&ctx->work, old_off); 309 + if (!work_push(&ctx->work, old_off)) ctx->failed = true; 316 310 317 311 return new_off; 318 312 } ··· 363 357 jsoff_t new_parent = gc_reserve_object(ctx, parent_off); 364 358 gc_saveoff(ctx->new_mem, new_off + sizeof(jsoff_t), new_parent); 365 359 } 360 + 361 + jsoff_t tail_off = gc_loadoff(ctx->js->mem, old_off + sizeof(jsoff_t) + sizeof(jsoff_t)); 362 + if (tail_off != 0 && tail_off < ctx->js->brk) { 363 + jsoff_t new_tail = fwd_lookup(&ctx->fwd, tail_off); 364 + if (new_tail == (jsoff_t)~0) new_tail = gc_reserve_prop(ctx, tail_off); 365 + gc_saveoff(ctx->new_mem, new_off + sizeof(jsoff_t) + sizeof(jsoff_t), new_tail); 366 + } 366 367 } 367 368 368 369 static void gc_drain_work_queue(gc_ctx_t *ctx) { ··· 435 436 return gc_update_val(ctx, val); 436 437 } 437 438 438 - size_t js_gc_compact(struct js *js) { 439 + size_t js_gc_compact(ant_t *js) { 439 440 if (!js || js->brk == 0) return 0; 440 441 441 442 mco_coro *running = mco_running(); ··· 483 484 return 0; 484 485 } 485 486 487 + ctx.failed = false; 488 + 486 489 if (js->brk > 0) { 487 490 jsoff_t header_at_0 = gc_loadoff(js->mem, 0); 488 491 if ((header_at_0 & 3) == T_OBJ) gc_reserve_object(&ctx, 0); ··· 502 505 js_gc_update_roots(js, gc_fwd_off_callback, gc_fwd_val_callback, &ctx); 503 506 504 507 gc_drain_work_queue(&ctx); 508 + 509 + if (ctx.failed) { 510 + free(mark_bits); 511 + work_free(&ctx.work); 512 + fwd_free(&ctx.fwd); 513 + ANT_GC_FREE(new_mem); 514 + return 0; 515 + } 505 516 506 517 uint8_t *old_mem = js->mem; 507 518 js->mem = new_mem;
+2 -1
src/modules/fetch.c
··· 8 8 #include <tlsuv/http.h> 9 9 #include <utarray.h> 10 10 11 + #include "ant.h" 11 12 #include "config.h" 12 13 #include "common.h" 13 14 #include "runtime.h" ··· 303 304 if (js_type(options_val) == JS_OBJ) { 304 305 jsval_t headers_val = js_get(js, options_val, "headers"); 305 306 if (js_type(headers_val) == JS_OBJ) { 306 - js_prop_iter_t iter = js_prop_iter_begin(js, headers_val); 307 + ant_iter_t iter = js_prop_iter_begin(js, headers_val); 307 308 const char *key; 308 309 size_t key_len; 309 310 jsval_t value;
+87 -68
src/modules/json.c
··· 3 3 #include <string.h> 4 4 #include <math.h> 5 5 #include <yyjson.h> 6 + #include <uthash.h> 6 7 7 8 #include "runtime.h" 8 9 #include "internal.h" 9 10 #include "modules/json.h" 10 11 #include "modules/symbol.h" 12 + 13 + typedef struct { 14 + const char *key; 15 + size_t key_len; 16 + jsoff_t prop_off; 17 + UT_hash_handle hh; 18 + } json_key_entry_t; 11 19 12 20 static jsval_t yyjson_to_jsval(struct js *js, yyjson_val *val) { 13 21 if (!val) return js_mkundef(); ··· 36 44 37 45 case YYJSON_TYPE_OBJ: { 38 46 jsval_t obj = js_newobj(js); 39 - size_t idx, max; 40 - yyjson_val *key, *item; 47 + 48 + size_t idx, max; yyjson_val *key, *item; 49 + json_key_entry_t *hash = NULL, *entry, *tmp; 50 + 51 + yyjson_obj_foreach(val, idx, max, key, item) { 52 + const char *k = yyjson_get_str(key); 53 + size_t klen = yyjson_get_len(key); 54 + jsval_t v = yyjson_to_jsval(js, item); 55 + 56 + HASH_FIND(hh, hash, k, klen, entry); 57 + if (entry) js_saveval(js, entry->prop_off, v); else { 58 + jsoff_t off = js_mkprop_fast_off(js, obj, k, klen, v); 59 + entry = malloc(sizeof(json_key_entry_t)); 60 + entry->key = k; entry->key_len = klen; entry->prop_off = off; 61 + HASH_ADD_KEYPTR(hh, hash, entry->key, entry->key_len, entry); 62 + } 63 + } 41 64 42 - yyjson_obj_foreach(val, idx, max, key, item) 43 - js_set(js, obj, yyjson_get_str(key), yyjson_to_jsval(js, item)); 65 + HASH_ITER(hh, hash, entry, tmp) { 66 + HASH_DEL(hash, entry); free(entry); 67 + } 44 68 45 69 return obj; 46 70 } ··· 75 99 ctx->stack[ctx->stack_size++] = val; 76 100 } 77 101 78 - static void json_cycle_pop(json_cycle_ctx *ctx) { 102 + static inline void json_cycle_pop(json_cycle_ctx *ctx) { 79 103 if (ctx->stack_size > 0) ctx->stack_size--; 80 104 } 81 105 82 106 typedef struct { char *key; size_t key_len; jsval_t value; } prop_entry; 83 107 84 108 static int should_skip_prop(struct js *js, const char *key, size_t key_len, jsval_t value) { 85 - if (key_len >= 2 && key[0] == '_' && key[1] == '_') return 1; 109 + if (is_internal_prop(key, (jsoff_t)key_len)) return 1; 86 110 if (js_type(value) != JS_OBJ) return 0; 87 - 88 - jsval_t code = js_get_slot(js, value, SLOT_CODE); 89 - return js_type(code) == T_CFUNC; 111 + return js_type(js_get_slot(js, value, SLOT_CODE)) == T_CFUNC; 90 112 } 91 113 92 114 static prop_entry *collect_props(struct js *js, jsval_t val, int *out_count) { ··· 96 118 size_t key_len; 97 119 jsval_t value; 98 120 99 - js_prop_iter_t iter = js_prop_iter_begin(js, val); 121 + ant_iter_t iter = js_prop_iter_begin(js, val); 100 122 while (js_prop_iter_next(&iter, &key, &key_len, &value)) { 101 123 if (should_skip_prop(js, key, key_len, value)) continue; 102 124 ··· 118 140 return props; 119 141 } 120 142 121 - static void free_props(prop_entry *props, int from, int to) { 143 + static inline void free_props(prop_entry *props, int from, int to) { 122 144 for (int i = from; i <= to; i++) free(props[i].key); 123 145 free(props); 124 146 } 125 147 148 + static inline int key_matches(const char *a, size_t a_len, const char *b, size_t b_len) { 149 + return a_len == b_len && memcmp(a, b, a_len) == 0; 150 + } 151 + 126 152 static int is_key_in_replacer_arr(struct js *js, json_cycle_ctx *ctx, const char *key, size_t key_len) { 127 153 if (js_type(ctx->replacer_arr) != JS_OBJ) return 1; 128 154 ··· 130 156 char idxstr[32]; 131 157 snprintf(idxstr, sizeof(idxstr), "%d", i); 132 158 jsval_t item = js_get(js, ctx->replacer_arr, idxstr); 133 - if (js_type(item) == JS_STR) { 159 + int type = js_type(item); 160 + 161 + if (type == JS_STR) { 134 162 size_t item_len; 135 163 char *item_str = js_getstr(js, item, &item_len); 136 - if (item_len == key_len && memcmp(item_str, key, key_len) == 0) return 1; 137 - } else if (js_type(item) == JS_NUM) { 164 + if (key_matches(item_str, item_len, key, key_len)) return 1; 165 + } else if (type == JS_NUM) { 138 166 char numstr[32]; 139 167 snprintf(numstr, sizeof(numstr), "%.0f", js_getnum(item)); 140 - if (strlen(numstr) == key_len && memcmp(numstr, key, key_len) == 0) return 1; 168 + if (key_matches(numstr, strlen(numstr), key, key_len)) return 1; 141 169 } 142 170 } 143 171 return 0; ··· 218 246 jsval_t saved_holder = ctx->holder; 219 247 ctx->holder = val; 220 248 221 - for (int i = prop_count - 1; i >= 0; i--) { 222 - if (js_type(ctx->replacer_arr) == JS_OBJ) { 223 - if (!is_key_in_replacer_arr(js, ctx, props[i].key, props[i].key_len)) { 224 - free(props[i].key); 225 - continue; 226 - } 227 - } 228 - 229 - int ptype = js_type(props[i].value); 249 + for (int i = 0; i < prop_count; i++) { 250 + prop_entry *p = &props[i]; 230 251 231 - if (ptype == JS_UNDEF || ptype == JS_FUNC) { 232 - free(props[i].key); 233 - continue; 252 + if (!is_key_in_replacer_arr(js, ctx, p->key, p->key_len)) { 253 + free(p->key); continue; 234 254 } 235 255 236 - yyjson_mut_val *jval = jsval_to_yyjson_with_key(js, doc, props[i].key, props[i].value, ctx, 0); 256 + int ptype = js_type(p->value); 257 + if (ptype == JS_UNDEF || ptype == JS_FUNC) { free(p->key); continue; } 258 + 259 + yyjson_mut_val *jval = jsval_to_yyjson_with_key(js, doc, p->key, p->value, ctx, 0); 237 260 if (ctx->has_cycle) { free_props(props, 0, i); ctx->holder = saved_holder; goto done; } 238 - 239 - if (jval == YYJSON_SKIP_VALUE) { 240 - free(props[i].key); 241 - continue; 242 - } 261 + if (jval == YYJSON_SKIP_VALUE) { free(p->key); continue; } 243 262 244 - yyjson_mut_obj_add(obj, yyjson_mut_strncpy(doc, props[i].key, props[i].key_len), jval); 245 - free(props[i].key); 263 + yyjson_mut_obj_add(obj, yyjson_mut_strncpy(doc, p->key, p->key_len), jval); 264 + free(p->key); 246 265 } 247 266 248 267 ctx->holder = saved_holder; ··· 255 274 } 256 275 257 276 static yyjson_mut_val *jsval_to_yyjson_with_key(struct js *js, yyjson_mut_doc *doc, const char *key, jsval_t val, json_cycle_ctx *ctx, int in_array) { 258 - if (js_type(ctx->replacer_func) == JS_FUNC) { 259 - jsval_t key_str = js_mkstr(js, key, strlen(key)); 260 - jsval_t call_args[2] = { key_str, val }; 261 - jsval_t transformed = js_call(js, ctx->replacer_func, call_args, 2); 262 - if (js_type(transformed) == JS_ERR) { 263 - ctx->has_cycle = 1; 264 - return NULL; 265 - } 266 - val = transformed; 277 + if (js_type(ctx->replacer_func) != JS_FUNC) 278 + return jsval_to_yyjson_impl(js, doc, val, ctx, in_array); 279 + 280 + jsval_t key_str = js_mkstr(js, key, strlen(key)); 281 + jsval_t call_args[2] = { key_str, val }; 282 + jsval_t transformed = js_call(js, ctx->replacer_func, call_args, 2); 283 + 284 + if (js_type(transformed) == JS_ERR) { 285 + ctx->has_cycle = 1; 286 + return NULL; 267 287 } 268 288 269 - return jsval_to_yyjson_impl(js, doc, val, ctx, in_array); 289 + return jsval_to_yyjson_impl(js, doc, transformed, ctx, in_array); 270 290 } 271 291 272 292 static yyjson_mut_val *jsval_to_yyjson(struct js *js, yyjson_mut_doc *doc, jsval_t val, json_cycle_ctx *ctx) { ··· 284 304 char idxstr[32]; 285 305 snprintf(idxstr, sizeof(idxstr), "%d", i); 286 306 jsval_t new_elem = apply_reviver(js, val, idxstr, reviver); 287 - if (js_type(new_elem) == JS_UNDEF) { 288 - js_del(js, val, idxstr); 289 - } else js_set(js, val, idxstr, new_elem); 307 + if (js_type(new_elem) == JS_UNDEF) js_del(js, val, idxstr); 308 + else js_set(js, val, idxstr, new_elem); 290 309 } 291 310 } else { 292 311 const char *prop_key; ··· 295 314 296 315 jsval_t keys_arr = js_mkobj(js); 297 316 int key_count = 0; 298 - js_prop_iter_t iter = js_prop_iter_begin(js, val); 317 + ant_iter_t iter = js_prop_iter_begin(js, val); 299 318 while (js_prop_iter_next(&iter, &prop_key, &prop_key_len, &prop_value)) { 300 - if (prop_key_len >= 2 && prop_key[0] == '_' && prop_key[1] == '_') continue; 319 + if (is_internal_prop(prop_key, (jsoff_t)prop_key_len)) continue; 301 320 char idxstr[32]; 302 321 snprintf(idxstr, sizeof(idxstr), "%d", key_count); 303 322 js_set(js, keys_arr, idxstr, js_mkstr(js, prop_key, prop_key_len)); ··· 312 331 size_t klen; 313 332 char *kstr = js_getstr(js, key_str, &klen); 314 333 jsval_t new_val = apply_reviver(js, val, kstr, reviver); 315 - if (js_type(new_val) == JS_UNDEF) { 316 - js_del(js, val, kstr); 317 - } else js_set(js, val, kstr, new_val); 334 + if (js_type(new_val) == JS_UNDEF) js_del(js, val, kstr); 335 + else js_set(js, val, kstr, new_val); 318 336 } 319 337 } 320 338 } 321 339 322 340 jsval_t key_str = js_mkstr(js, key, strlen(key)); 323 341 jsval_t call_args[2] = { key_str, js_get(js, holder, key) }; 324 - return js_call(js, reviver, call_args, 2); 342 + 343 + return js_call_with_this(js, reviver, holder, call_args, 2); 325 344 } 326 345 327 346 jsval_t js_json_parse(struct js *js, jsval_t *args, int nargs) { ··· 348 367 } 349 368 350 369 static yyjson_write_flag get_write_flags(jsval_t *args, int nargs) { 351 - if (nargs < 3 || js_type(args[2]) == JS_UNDEF || js_type(args[2]) == JS_NULL) 352 - return 0; 370 + if (nargs < 3) return 0; 353 371 354 - int indent = 4; 355 - if (js_type(args[2]) == JS_NUM) { 356 - indent = (int)js_getnum(args[2]); 357 - if (indent < 0) indent = 0; 358 - if (indent > 10) indent = 10; 359 - } 372 + int type = js_type(args[2]); 373 + if (type == JS_UNDEF || type == JS_NULL) return 0; 374 + if (type != JS_NUM) return YYJSON_WRITE_PRETTY; 360 375 376 + int indent = (int)js_getnum(args[2]); 377 + if (indent <= 0) return 0; 361 378 if (indent == 2) return YYJSON_WRITE_PRETTY_TWO_SPACES; 362 - if (indent > 0) return YYJSON_WRITE_PRETTY; 363 - return 0; 379 + 380 + return YYJSON_WRITE_PRETTY; 364 381 } 365 382 366 383 jsval_t js_json_stringify(struct js *js, jsval_t *args, int nargs) { ··· 383 400 ctx.holder = js_mkundef(); 384 401 385 402 if (nargs >= 2) { 386 - int replacer_type = js_type(args[1]); 403 + jsval_t replacer = args[1]; 404 + int replacer_type = js_type(replacer); 405 + 387 406 if (replacer_type == JS_FUNC) { 388 - ctx.replacer_func = args[1]; 407 + ctx.replacer_func = replacer; 389 408 } else if (replacer_type == JS_OBJ) { 390 - jsval_t len_val = js_get(js, args[1], "length"); 409 + jsval_t len_val = js_get(js, replacer, "length"); 391 410 if (js_type(len_val) == JS_NUM) { 392 - ctx.replacer_arr = args[1]; 411 + ctx.replacer_arr = replacer; 393 412 ctx.replacer_arr_len = (int)js_getnum(len_val); 394 413 } 395 414 }
+6 -16
src/modules/reflect.c
··· 91 91 return js_mkerr(js, "Reflect.ownKeys called on non-object"); 92 92 } 93 93 94 - jsval_t temp_keys[256]; 95 - int count = 0; 94 + jsval_t keys_arr = js_mkarr(js); 95 + ant_iter_t iter = js_prop_iter_begin(js, target); 96 + const char *key; size_t key_len; jsval_t value; 96 97 97 - js_prop_iter_t iter = js_prop_iter_begin(js, target); 98 - const char *key; 99 - size_t key_len; 100 - jsval_t value; 101 - 102 - while (js_prop_iter_next(&iter, &key, &key_len, &value) && count < 256) { 98 + while (js_prop_iter_next(&iter, &key, &key_len, &value)) { 103 99 if (key_len >= 9 && memcmp(key, STR_PROTO, STR_PROTO_LEN) == 0) continue; 104 100 if (key_len >= 2 && key[0] == '_' && key[1] == '_') continue; 105 - 106 - temp_keys[count++] = js_mkstr(js, key, key_len); 101 + js_arr_push(js, keys_arr, js_mkstr(js, key, key_len)); 107 102 } 103 + 108 104 js_prop_iter_end(&iter); 109 - 110 - jsval_t keys_arr = js_mkarr(js); 111 - for (int i = count - 1; i >= 0; i--) { 112 - js_arr_push(js, keys_arr, temp_keys[i]); 113 - } 114 - 115 105 return keys_arr; 116 106 } 117 107
+14 -4
tests/bench_gc.js
··· 1 1 // GC Benchmark - tests forwarding table and compaction performance 2 2 3 - function formatBytes(bytes) { 3 + function fmt(bytes) { 4 4 if (bytes < 1024) return bytes + ' B'; 5 5 if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; 6 6 return (bytes / 1024 / 1024).toFixed(2) + ' MB'; ··· 27 27 28 28 console.log(`${name}:`); 29 29 console.log(` Time: ${totalTime}ms (${iterations} iterations, avg ${(totalTime / iterations).toFixed(2)}ms)`); 30 - console.log(` Freed: ${formatBytes(totalFreed)} total`); 30 + console.log(` Freed: ${fmt(totalFreed)} total`); 31 31 console.log(''); 32 32 } 33 33 ··· 104 104 } 105 105 106 106 console.log('=== GC Benchmark ===\n'); 107 - console.log('Initial state:', Ant.alloc(), '\n'); 107 + 108 + let initial = Ant.alloc(); 109 + console.log('Initial state:'); 110 + console.log(' heapSize:', fmt(initial.heapSize)); 111 + console.log(' usedBytes:', fmt(initial.usedBytes)); 112 + console.log(' totalBytes:', fmt(initial.totalBytes)); 113 + console.log(''); 108 114 109 115 bench('Many small objects (10k objects, 5k garbage)', manySmallObjects, 5); 110 116 bench('Deep object graph (1000 levels)', deepObjectGraph, 5); ··· 113 119 bench('Mixed workload', mixedWorkload, 5); 114 120 bench('Repeated GC cycles (10 cycles x 1000 objects)', repeatedGcCycles, 3); 115 121 116 - console.log('Final state:', Ant.alloc()); 122 + let final = Ant.alloc(); 123 + console.log('Final state:'); 124 + console.log(' heapSize:', fmt(final.heapSize)); 125 + console.log(' usedBytes:', fmt(final.usedBytes)); 126 + console.log(' totalBytes:', fmt(final.totalBytes));
+44 -38
tests/test_gc.js
··· 1 + function fmt(bytes) { 2 + if (bytes < 1024) return bytes + ' B'; 3 + if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; 4 + return (bytes / 1024 / 1024).toFixed(2) + ' MB'; 5 + } 6 + 1 7 console.log('=== Testing Ant.alloc() (bdwgc) ==='); 2 8 let alloc1 = Ant.alloc(); 3 9 console.log('Initial allocation:'); 4 - console.log(' heapSize:', alloc1.heapSize); 5 - console.log(' usedBytes:', alloc1.usedBytes); 6 - console.log(' freeBytes:', alloc1.freeBytes); 7 - console.log(' totalBytes:', alloc1.totalBytes); 10 + console.log(' heapSize:', fmt(alloc1.heapSize)); 11 + console.log(' usedBytes:', fmt(alloc1.usedBytes)); 12 + console.log(' freeBytes:', fmt(alloc1.freeBytes)); 13 + console.log(' totalBytes:', fmt(alloc1.totalBytes)); 8 14 9 15 console.log('\n=== Creating objects to allocate memory ==='); 10 16 let arr = []; ··· 15 21 16 22 let alloc2 = Ant.alloc(); 17 23 console.log('After allocation:'); 18 - console.log(' heapSize:', alloc2.heapSize); 19 - console.log(' usedBytes:', alloc2.usedBytes); 20 - console.log(' totalBytes:', alloc2.totalBytes); 21 - console.log(' totalBytes increase:', alloc2.totalBytes - alloc1.totalBytes); 24 + console.log(' heapSize:', fmt(alloc2.heapSize)); 25 + console.log(' usedBytes:', fmt(alloc2.usedBytes)); 26 + console.log(' totalBytes:', fmt(alloc2.totalBytes)); 27 + console.log(' totalBytes increase:', fmt(alloc2.totalBytes - alloc1.totalBytes)); 22 28 23 29 console.log('\n=== Testing Ant.stats() ==='); 24 30 let stats1 = Ant.stats(); 25 31 console.log('Memory stats:'); 26 - console.log(' arenaUsed:', stats1.arenaUsed); 27 - console.log(' cstack:', stats1.cstack); 28 - console.log(' gcHeapSize:', stats1.gcHeapSize); 29 - console.log(' gcUsedBytes:', stats1.gcUsedBytes); 30 - console.log(' gcFreeBytes:', stats1.gcFreeBytes); 32 + console.log(' arenaUsed:', fmt(stats1.arenaUsed)); 33 + console.log(' cstack:', fmt(stats1.cstack)); 34 + console.log(' gcHeapSize:', fmt(stats1.gcHeapSize)); 35 + console.log(' gcUsedBytes:', fmt(stats1.gcUsedBytes)); 36 + console.log(' gcFreeBytes:', fmt(stats1.gcFreeBytes)); 31 37 32 38 console.log('\n=== Testing Ant.gc() ==='); 33 39 arr = null; 34 40 35 41 let gcResult = Ant.gc(); 36 42 console.log('GC result:'); 37 - console.log(' heapBefore:', gcResult.heapBefore); 38 - console.log(' heapAfter:', gcResult.heapAfter); 39 - console.log(' usedBefore:', gcResult.usedBefore); 40 - console.log(' usedAfter:', gcResult.usedAfter); 41 - console.log(' freed:', gcResult.freed); 42 - console.log(' arenaBefore:', gcResult.arenaBefore); 43 - console.log(' arenaAfter:', gcResult.arenaAfter); 44 - console.log(' arenaFreed:', gcResult.arenaFreed); 43 + console.log(' heapBefore:', fmt(gcResult.heapBefore)); 44 + console.log(' heapAfter:', fmt(gcResult.heapAfter)); 45 + console.log(' usedBefore:', fmt(gcResult.usedBefore)); 46 + console.log(' usedAfter:', fmt(gcResult.usedAfter)); 47 + console.log(' freed:', fmt(gcResult.freed)); 48 + console.log(' arenaBefore:', fmt(gcResult.arenaBefore)); 49 + console.log(' arenaAfter:', fmt(gcResult.arenaAfter)); 50 + console.log(' arenaFreed:', fmt(gcResult.arenaFreed)); 45 51 46 52 console.log('\n=== Testing Ant.stats() ==='); 47 53 let stats2 = Ant.stats(); 48 54 console.log('Memory stats:'); 49 - console.log(' arenaUsed:', stats2.arenaUsed); 50 - console.log(' cstack:', stats2.cstack); 51 - console.log(' gcHeapSize:', stats2.gcHeapSize); 52 - console.log(' gcUsedBytes:', stats2.gcUsedBytes); 53 - console.log(' gcFreeBytes:', stats2.gcFreeBytes); 55 + console.log(' arenaUsed:', fmt(stats2.arenaUsed)); 56 + console.log(' cstack:', fmt(stats2.cstack)); 57 + console.log(' gcHeapSize:', fmt(stats2.gcHeapSize)); 58 + console.log(' gcUsedBytes:', fmt(stats2.gcUsedBytes)); 59 + console.log(' gcFreeBytes:', fmt(stats2.gcFreeBytes)); 54 60 55 61 console.log('\n=== Verifying memory after GC ==='); 56 62 let alloc3 = Ant.alloc(); 57 63 console.log('After GC:'); 58 - console.log(' heapSize:', alloc3.heapSize); 59 - console.log(' usedBytes:', alloc3.usedBytes); 60 - console.log(' freeBytes:', alloc3.freeBytes); 64 + console.log(' heapSize:', fmt(alloc3.heapSize)); 65 + console.log(' usedBytes:', fmt(alloc3.usedBytes)); 66 + console.log(' freeBytes:', fmt(alloc3.freeBytes)); 61 67 62 68 console.log('\n=== Testing multiple GC cycles ==='); 63 69 for (let cycle = 0; cycle < 3; cycle = cycle + 1) { ··· 68 74 temp.push({ data: 'test data ' + i }); 69 75 } 70 76 let beforeGc = Ant.alloc(); 71 - console.log(' Before GC - usedBytes:', beforeGc.usedBytes); 77 + console.log(' Before GC - usedBytes:', fmt(beforeGc.usedBytes)); 72 78 73 79 temp = null; 74 80 let gc = Ant.gc(); 75 - console.log(' After GC - usedAfter:', gc.usedAfter, 'freed:', gc.freed, 'arenaFreed:', gc.arenaFreed); 81 + console.log(' After GC - usedAfter:', fmt(gc.usedAfter), 'freed:', fmt(gc.freed), 'arenaFreed:', fmt(gc.arenaFreed)); 76 82 } 77 83 78 84 console.log('\n=== Testing Ant.stats() ==='); 79 85 let stats3 = Ant.stats(); 80 86 console.log('Memory stats:'); 81 - console.log(' arenaUsed:', stats3.arenaUsed); 82 - console.log(' cstack:', stats3.cstack); 83 - console.log(' gcHeapSize:', stats3.gcHeapSize); 84 - console.log(' gcUsedBytes:', stats3.gcUsedBytes); 85 - console.log(' gcFreeBytes:', stats3.gcFreeBytes); 87 + console.log(' arenaUsed:', fmt(stats3.arenaUsed)); 88 + console.log(' cstack:', fmt(stats3.cstack)); 89 + console.log(' gcHeapSize:', fmt(stats3.gcHeapSize)); 90 + console.log(' gcUsedBytes:', fmt(stats3.gcUsedBytes)); 91 + console.log(' gcFreeBytes:', fmt(stats3.gcFreeBytes)); 86 92 87 93 console.log('\n=== Testing stats consistency ==='); 88 94 let statsA = Ant.stats(); 89 95 let allocA = Ant.alloc(); 90 96 console.log('Stats and alloc should match:'); 91 - console.log(' stats.gcUsedBytes:', statsA.gcUsedBytes); 92 - console.log(' alloc.usedBytes:', allocA.usedBytes); 97 + console.log(' stats.gcUsedBytes:', fmt(statsA.gcUsedBytes)); 98 + console.log(' alloc.usedBytes:', fmt(allocA.usedBytes)); 93 99 console.log(' match:', statsA.gcUsedBytes === allocA.usedBytes); 94 100 95 101 console.log('\n=== Test complete ===');
+18 -12
tests/test_gc_large.js
··· 1 1 console.log('=== GC Test with Large Objects ===\n'); 2 2 3 + function fmt(bytes) { 4 + if (bytes < 1024) return bytes + ' B'; 5 + if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; 6 + return (bytes / 1024 / 1024).toFixed(2) + ' MB'; 7 + } 8 + 3 9 let alloc1 = Ant.alloc(); 4 10 console.log('Initial:'); 5 - console.log(' heapSize:', alloc1.heapSize); 6 - console.log(' totalBytes:', alloc1.totalBytes); 11 + console.log(' heapSize:', fmt(alloc1.heapSize)); 12 + console.log(' totalBytes:', fmt(alloc1.totalBytes)); 7 13 console.log(''); 8 14 9 15 // Allocate large objects ··· 18 24 19 25 let alloc2 = Ant.alloc(); 20 26 console.log('After allocating 5MB:'); 21 - console.log(' heapSize:', alloc2.heapSize); 22 - console.log(' totalBytes:', alloc2.totalBytes); 27 + console.log(' heapSize:', fmt(alloc2.heapSize)); 28 + console.log(' totalBytes:', fmt(alloc2.totalBytes)); 23 29 console.log(''); 24 30 25 31 // Free the objects 26 32 largeObjects = null; 27 33 28 34 let gc1 = Ant.gc(); 29 - console.log('GC 1: freed:', gc1.freed, 'arenaFreed:', gc1.arenaFreed); 35 + console.log('GC 1: freed:', fmt(gc1.freed), 'arenaFreed:', fmt(gc1.arenaFreed)); 30 36 let gc2 = Ant.gc(); 31 - console.log('GC 2: freed:', gc2.freed, 'arenaFreed:', gc2.arenaFreed); 37 + console.log('GC 2: freed:', fmt(gc2.freed), 'arenaFreed:', fmt(gc2.arenaFreed)); 32 38 let gc3 = Ant.gc(); 33 - console.log('GC 3: freed:', gc3.freed, 'arenaFreed:', gc3.arenaFreed); 39 + console.log('GC 3: freed:', fmt(gc3.freed), 'arenaFreed:', fmt(gc3.arenaFreed)); 34 40 35 41 let alloc3 = Ant.alloc(); 36 42 console.log('After 3x GC:'); 37 - console.log(' heapSize:', alloc3.heapSize); 38 - console.log(' totalBytes:', alloc3.totalBytes); 43 + console.log(' heapSize:', fmt(alloc3.heapSize)); 44 + console.log(' totalBytes:', fmt(alloc3.totalBytes)); 39 45 console.log(''); 40 46 41 47 // Allocate again - if GC worked, heap shouldn't grow much ··· 50 56 51 57 let alloc4 = Ant.alloc(); 52 58 console.log('After allocating another 5MB:'); 53 - console.log(' heapSize:', alloc4.heapSize); 54 - console.log(' totalBytes:', alloc4.totalBytes); 55 - console.log(' heap increase from GC point:', alloc4.heapSize - alloc3.heapSize); 59 + console.log(' heapSize:', fmt(alloc4.heapSize)); 60 + console.log(' totalBytes:', fmt(alloc4.totalBytes)); 61 + console.log(' heap increase from GC point:', fmt(alloc4.heapSize - alloc3.heapSize)); 56 62 console.log(''); 57 63 58 64 console.log('If heapSize stayed same, GC reclaimed memory for reuse!');
+134
tests/test_new_arr.js
··· 1 + // ============ Array.prototype.some ============ 2 + console.log('=== some ==='); 3 + console.log([1, 2, 3].some(x => x > 2) === true); 4 + console.log([1, 2, 3].some(x => x > 5) === false); 5 + console.log([].some(x => true) === false); 6 + 7 + // ============ Array.prototype.every ============ 8 + console.log('=== every ==='); 9 + console.log([1, 2, 3].every(x => x > 0) === true); 10 + console.log([1, 2, 3].every(x => x > 2) === false); 11 + console.log([].every(x => false) === true); 12 + 13 + // ============ Array.prototype.forEach ============ 14 + console.log('=== forEach ==='); 15 + let sum = 0; 16 + [1, 2, 3].forEach(x => (sum += x)); 17 + console.log(sum === 6); 18 + 19 + // ============ Array.prototype.map ============ 20 + console.log('=== map ==='); 21 + let mapped = [1, 2, 3].map(x => x * 2); 22 + console.log(mapped[0] === 2, mapped[1] === 4, mapped[2] === 6); 23 + 24 + // ============ Array.prototype.filter ============ 25 + console.log('=== filter ==='); 26 + let filtered = [1, 2, 3, 4].filter(x => x % 2 === 0); 27 + console.log(filtered.length === 2, filtered[0] === 2, filtered[1] === 4); 28 + 29 + // ============ Array.prototype.find ============ 30 + console.log('=== find ==='); 31 + console.log([1, 2, 3].find(x => x > 1) === 2); 32 + console.log([1, 2, 3].find(x => x > 5) === undefined); 33 + 34 + // ============ Array.prototype.findIndex ============ 35 + console.log('=== findIndex ==='); 36 + console.log([1, 2, 3].findIndex(x => x > 1) === 1); 37 + console.log([1, 2, 3].findIndex(x => x > 5) === -1); 38 + 39 + // ============ Array.prototype.findLast ============ 40 + console.log('=== findLast ==='); 41 + console.log([1, 2, 3, 2].findLast(x => x === 2) === 2); 42 + console.log([1, 2, 3].findLast(x => x > 5) === undefined); 43 + 44 + // ============ Array.prototype.findLastIndex ============ 45 + console.log('=== findLastIndex ==='); 46 + console.log([1, 2, 3, 2].findLastIndex(x => x === 2) === 3); 47 + console.log([1, 2, 3].findLastIndex(x => x > 5) === -1); 48 + 49 + // ============ Array.prototype.reduce ============ 50 + console.log('=== reduce ==='); 51 + console.log([1, 2, 3].reduce((a, b) => a + b) === 6); 52 + console.log([1, 2, 3].reduce((a, b) => a + b, 10) === 16); 53 + 54 + // ============ Array.prototype.includes ============ 55 + console.log('=== includes ==='); 56 + console.log([1, 2, 3].includes(2) === true); 57 + console.log([1, 2, 3].includes(5) === false); 58 + console.log(['a', 'b'].includes('a') === true); 59 + 60 + // ============ Array.prototype.sort ============ 61 + console.log('=== sort ==='); 62 + let sorted1 = [3, 1, 2].sort(); 63 + console.log(sorted1[0] === 1, sorted1[1] === 2, sorted1[2] === 3); 64 + 65 + let sorted2 = [3, 1, 2].sort((a, b) => b - a); 66 + console.log(sorted2[0] === 3, sorted2[1] === 2, sorted2[2] === 1); 67 + 68 + let sorted3 = ['z', null, undefined, 'a'].sort(); 69 + console.log(sorted3[0] === 'a', sorted3[1] === null, sorted3[2] === 'z', sorted3[3] === undefined); 70 + 71 + // ============ Array.prototype.reverse ============ 72 + console.log('=== reverse ==='); 73 + let rev = [1, 2, 3]; 74 + rev.reverse(); 75 + console.log(rev[0] === 3, rev[1] === 2, rev[2] === 1); 76 + 77 + // ============ Array.prototype.toSorted ============ 78 + console.log('=== toSorted ==='); 79 + let orig1 = [3, 1, 2]; 80 + let toSorted1 = orig1.toSorted(); 81 + console.log(orig1[0] === 3); // original unchanged 82 + console.log(toSorted1[0] === 1, toSorted1[1] === 2, toSorted1[2] === 3); 83 + 84 + // ============ Array.prototype.toReversed ============ 85 + console.log('=== toReversed ==='); 86 + let orig2 = [1, 2, 3]; 87 + let toReversed1 = orig2.toReversed(); 88 + console.log(orig2[0] === 1); // original unchanged 89 + console.log(toReversed1[0] === 3, toReversed1[1] === 2, toReversed1[2] === 1); 90 + 91 + // ============ Array.prototype.toSpliced ============ 92 + console.log('=== toSpliced ==='); 93 + let orig3 = [1, 2, 3, 4]; 94 + let toSpliced1 = orig3.toSpliced(1, 2, 'a', 'b'); 95 + console.log(orig3.length === 4); // original unchanged 96 + console.log(toSpliced1.length === 4); 97 + console.log(toSpliced1[0] === 1, toSpliced1[1] === 'a', toSpliced1[2] === 'b', toSpliced1[3] === 4); 98 + 99 + // ============ Array.prototype.flat ============ 100 + console.log('=== flat ==='); 101 + let flat1 = [1, [2, 3], [4, [5]]].flat(); 102 + console.log(flat1.length === 5); 103 + console.log(flat1[0] === 1, flat1[1] === 2, flat1[2] === 3, flat1[3] === 4); 104 + 105 + let flat2 = [1, [2, [3, [4]]]].flat(2); 106 + console.log(flat2.length === 4); 107 + 108 + // ============ Array.prototype.indexOf ============ 109 + console.log('=== indexOf ==='); 110 + console.log([1, 2, 3, 2].indexOf(2) === 1); 111 + console.log([1, 2, 3].indexOf(5) === -1); 112 + 113 + // ============ Array.prototype.lastIndexOf ============ 114 + console.log('=== lastIndexOf ==='); 115 + console.log([1, 2, 3, 2].lastIndexOf(2) === 3); 116 + console.log([1, 2, 3].lastIndexOf(5) === -1); 117 + 118 + // ============ Sparse array handling ============ 119 + console.log('=== sparse arrays ==='); 120 + let sparse = [1, , 3]; 121 + console.log(sparse.some(x => x === undefined) === false); // holes skipped 122 + console.log(sparse.filter(x => true).length === 2); // holes skipped 123 + 124 + // ============ Index correctness ============ 125 + console.log('=== index correctness ==='); 126 + let indices = []; 127 + [10, 20, 30].forEach((v, i) => indices.push(i)); 128 + console.log(indices[0] === 0, indices[1] === 1, indices[2] === 2); 129 + 130 + // ============ Array reference in callback ============ 131 + console.log('=== array reference ==='); 132 + console.log([1, 2, 3].every((v, i, arr) => arr.length === 3) === true); 133 + 134 + console.log('=== ALL TESTS COMPLETE ===');