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.

add for-in iterator support for objects, arrays, and strings

+229 -88
+50 -88
src/ant.c
··· 9665 9665 bool has_destructure; 9666 9666 jsoff_t destructure_off; 9667 9667 jsoff_t destructure_len; 9668 + int marker_index; 9668 9669 } for_iter_ctx_t; 9669 9670 9670 9671 static jsval_t for_iter_bind_var(struct js *js, for_iter_ctx_t *ctx, jsval_t value) { ··· 9687 9688 return js_block_or_stmt(js); 9688 9689 } 9689 9690 9690 - static bool for_in_should_skip_key(struct js *js, jsval_t obj, const char *key, jsoff_t klen) { 9691 - if (streq(key, klen, STR_PROTO, STR_PROTO_LEN)) return true; 9691 + static inline bool for_iter_handle_continue(struct js *js, for_iter_ctx_t *ctx) { 9692 + if (!(label_flags & F_CONTINUE_LABEL)) return false; 9693 + if (is_this_loop_continue_target(ctx->marker_index)) { 9694 + clear_continue_label(); 9695 + js->flags &= ~(F_BREAK | F_NOEXEC); 9696 + return false; 9697 + } 9698 + js->flags |= F_BREAK; 9699 + return true; 9700 + } 9701 + 9702 + static jsval_t for_in_iter_object(struct js *js, for_iter_ctx_t *ctx, jsval_t obj) { 9703 + uint8_t obj_type = vtype(obj); 9692 9704 9693 - const char *tag_sym_key = get_toStringTag_sym_key(); 9694 - if (streq(key, klen, tag_sym_key, strlen(tag_sym_key))) return true; 9705 + if (obj_type == T_NULL || obj_type == T_UNDEF) return js_mkundef(); 9706 + if (obj_type != T_OBJ && obj_type != T_ARR && obj_type != T_FUNC) return js_mkerr(js, "for-in requires object"); 9695 9707 9696 - jsoff_t obj_off = (jsoff_t)vdata(obj); 9697 - descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 9698 - if (desc && !desc->enumerable) return true; 9708 + jsval_t iter_obj = (obj_type == T_FUNC) ? mkval(T_OBJ, vdata(obj)) : obj; 9709 + jsoff_t iter_obj_off = (jsoff_t)vdata(iter_obj); 9710 + jsoff_t prop_off = loadoff(js, iter_obj_off) & ~(3U | FLAGMASK); 9699 9711 9700 - return false; 9701 - } 9702 - 9703 - static jsval_t for_in_iter_object(struct js *js, for_iter_ctx_t *ctx, jsval_t obj) { 9704 - jsval_t iter_obj = (vtype(obj) == T_FUNC) ? mkval(T_OBJ, vdata(obj)) : obj; 9705 - jsoff_t prop_off = loadoff(js, (jsoff_t) vdata(iter_obj)) & ~(3U | FLAGMASK); 9712 + const char *tag_sym_key = get_toStringTag_sym_key(); 9713 + size_t tag_sym_len = tag_sym_key ? strlen(tag_sym_key) : 0; 9706 9714 9707 9715 while (prop_off < js->brk && prop_off != 0) { 9708 9716 jsoff_t header = loadoff(js, prop_off); ··· 9712 9720 jsoff_t klen = offtolen(loadoff(js, koff)); 9713 9721 const char *key = (char *) &js->mem[koff + sizeof(koff)]; 9714 9722 9715 - if (!for_in_should_skip_key(js, iter_obj, key, klen)) { 9723 + bool should_skip = streq(key, klen, STR_PROTO, STR_PROTO_LEN); 9724 + if (!should_skip && tag_sym_key) should_skip = streq(key, klen, tag_sym_key, tag_sym_len); 9725 + if (!should_skip) { 9726 + descriptor_entry_t *desc = lookup_descriptor(iter_obj_off, key, klen); 9727 + if (desc && !desc->enumerable) should_skip = true; 9728 + } 9729 + 9730 + if (!should_skip) { 9716 9731 jsval_t key_str = js_mkstr(js, key, klen); 9717 9732 9718 9733 jsval_t err = for_iter_bind_var(js, ctx, key_str); ··· 9720 9735 9721 9736 jsval_t v = for_iter_exec_body(js, ctx); 9722 9737 if (is_err(v)) return v; 9738 + if (for_iter_handle_continue(js, ctx)) break; 9723 9739 if (js->flags & F_BREAK) break; 9724 - if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 9725 9740 if (js->flags & F_RETURN) return v; 9726 9741 } 9727 9742 ··· 9776 9791 9777 9792 jsval_t v = for_iter_exec_body(js, ctx); 9778 9793 if (is_err(v)) return v; 9794 + if (for_iter_handle_continue(js, ctx)) break; 9779 9795 if (js->flags & F_BREAK) break; 9780 - if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 9781 9796 if (js->flags & F_RETURN) return v; 9782 9797 } 9783 9798 ··· 9796 9811 9797 9812 jsval_t v = for_iter_exec_body(js, ctx); 9798 9813 if (is_err(v)) return v; 9814 + if (for_iter_handle_continue(js, ctx)) break; 9799 9815 if (js->flags & F_BREAK) break; 9800 - if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 9801 9816 if (js->flags & F_RETURN) return v; 9802 9817 } 9803 9818 ··· 9850 9865 9851 9866 jsval_t v = for_iter_exec_body(js, ctx); 9852 9867 if (is_err(v)) return v; 9868 + if (for_iter_handle_continue(js, ctx)) break; 9853 9869 if (js->flags & F_BREAK) break; 9854 - if (label_flags & F_CONTINUE_LABEL) { js->flags |= F_BREAK; break; } 9855 9870 if (js->flags & F_RETURN) return v; 9856 9871 } 9857 9872 ··· 9983 9998 9984 9999 if (exe) { 9985 10000 jsval_t obj = resolveprop(js, obj_expr); 9986 - uint8_t obj_type = vtype(obj); 9987 - 9988 - if (obj_type == T_NULL || obj_type == T_UNDEF) { 9989 - res = js_mkundef(); 9990 - goto done; 9991 - } 9992 - 9993 - if (obj_type != T_OBJ && obj_type != T_ARR && obj_type != T_FUNC) { 9994 - res = js_mkerr(js, "for-in requires object"); 9995 - goto done; 9996 - } 9997 - 9998 - jsval_t iter_obj = obj; 9999 - if (vtype(obj) == T_FUNC) { 10000 - iter_obj = mkval(T_OBJ, vdata(obj)); 10001 - } 10002 - 10003 - jsoff_t prop_off = loadoff(js, (jsoff_t) vdata(iter_obj)) & ~(3U | FLAGMASK); 10004 - 10005 - while (prop_off < js->brk && prop_off != 0) { 10006 - jsoff_t header = loadoff(js, prop_off); 10007 - 10008 - if (is_slot_prop(header)) { 10009 - prop_off = next_prop(header); 10010 - continue; 10011 - } 10012 - 10013 - jsoff_t koff = loadoff(js, prop_off + (jsoff_t) sizeof(prop_off)); 10014 - jsoff_t klen = offtolen(loadoff(js, koff)); 10015 - 10016 - const char *key = (char *) &js->mem[koff + sizeof(koff)]; 10017 - const char *tag_sym_key = get_toStringTag_sym_key(); 10018 - bool should_skip = streq(key, klen, STR_PROTO, STR_PROTO_LEN) || streq(key, klen, tag_sym_key, strlen(tag_sym_key)); 10019 - 10020 - if (!should_skip) { 10021 - jsoff_t iter_obj_off = (jsoff_t)vdata(iter_obj); 10022 - descriptor_entry_t *desc = lookup_descriptor(iter_obj_off, key, klen); 10023 - if (desc && !desc->enumerable) should_skip = true; 10024 - } 10025 - 10026 - if (!should_skip) { 10027 - jsval_t key_str = js_mkstr(js, key, klen); 10028 - const char *var_name = &js->code[var_name_off]; 10029 - jsoff_t existing = lkp_scope(js, js->scope, var_name, var_name_len); 10030 - 10031 - if (existing > 0) saveval(js, existing + sizeof(jsoff_t) * 2, key_str); else { 10032 - jsval_t x = mkprop(js, js->scope, js_mkstr(js, var_name, var_name_len), key_str, is_const_var ? CONSTMASK : 0); 10033 - if (is_err(x)) { res = x; goto done; } 10034 - } 10035 - 10036 - js->pos = body_start; 10037 - js->consumed = 1; 10038 - js->flags = (flags & ~F_NOEXEC) | F_LOOP; 10039 - v = js_block_or_stmt(js); 10040 - if (is_err(v)) { res = v; goto done; } 10041 - 10042 - if (label_flags & F_CONTINUE_LABEL) { 10043 - if (is_this_loop_continue_target(marker_index)) { 10044 - clear_continue_label(); 10045 - js->flags &= ~(F_BREAK | F_NOEXEC); 10046 - } else { js->flags |= F_BREAK; break; } 10047 - } 10048 - 10049 - if (js->flags & F_BREAK) break; 10050 - if (js->flags & F_RETURN) { res = v; goto done; } 10051 - } 10052 - 10053 - prop_off = loadoff(js, prop_off) & ~(3U | FLAGMASK); 10054 - } 10001 + for_iter_ctx_t ctx = { 10002 + body_start, body_end, 10003 + var_name_off, var_name_len, 10004 + is_const_var, flags, 10005 + has_destructure, destructure_off, 10006 + destructure_len, marker_index 10007 + }; 10008 + res = for_in_iter_object(js, &ctx, obj); 10009 + if (is_err(res)) goto done; 10010 + if (js->flags & F_RETURN) goto done; 10055 10011 } 10056 10012 10057 10013 js->pos = body_end; ··· 10074 10030 if (exe) { 10075 10031 jsval_t iterable = resolveprop(js, iter_expr); 10076 10032 uint8_t itype = vtype(iterable); 10077 - for_iter_ctx_t ctx = { body_start, body_end, var_name_off, var_name_len, is_const_var, flags, has_destructure, destructure_off, destructure_len }; 10033 + for_iter_ctx_t ctx = { 10034 + body_start, body_end, 10035 + var_name_off, var_name_len, 10036 + is_const_var, flags, 10037 + has_destructure, destructure_off, 10038 + destructure_len, marker_index 10039 + }; 10078 10040 10079 10041 if (itype == T_ARR) res = for_of_iter_array(js, &ctx, iterable); 10080 10042 else if (itype == T_STR) res = for_of_iter_string(js, &ctx, iterable);
+179
tests/test_labeled_loops.cjs
··· 1 + // Test labeled break and continue in loops 2 + console.log('=== Labeled Loop Tests ==='); 3 + 4 + // Test 1: Labeled break in nested for loops 5 + console.log('\nTest 1: Labeled break in nested for loops'); 6 + let result1 = ''; 7 + outer1: for (let i = 0; i < 3; i++) { 8 + for (let j = 0; j < 3; j++) { 9 + if (i === 1 && j === 1) { 10 + break outer1; 11 + } 12 + result1 += `(${i},${j})`; 13 + } 14 + } 15 + console.log('Result: ' + result1); 16 + console.log('Expected: (0,0)(0,1)(0,2)(1,0)'); 17 + 18 + // Test 2: Labeled continue in nested for loops 19 + console.log('\nTest 2: Labeled continue in nested for loops'); 20 + let result2 = ''; 21 + outer2: for (let i = 0; i < 3; i++) { 22 + for (let j = 0; j < 3; j++) { 23 + if (j === 1) { 24 + continue outer2; 25 + } 26 + result2 += `(${i},${j})`; 27 + } 28 + } 29 + console.log('Result: ' + result2); 30 + console.log('Expected: (0,0)(1,0)(2,0)'); 31 + 32 + // Test 3: Labeled break in for-of loop 33 + console.log('\nTest 3: Labeled break in for-of loop'); 34 + let result3 = ''; 35 + const arr1 = [[1,2,3], [4,5,6], [7,8,9]]; 36 + outer3: for (const row of arr1) { 37 + for (const val of row) { 38 + if (val === 5) { 39 + break outer3; 40 + } 41 + result3 += val + ','; 42 + } 43 + } 44 + console.log('Result: ' + result3); 45 + console.log('Expected: 1,2,3,4,'); 46 + 47 + // Test 4: Labeled continue in for-of loop 48 + console.log('\nTest 4: Labeled continue in for-of loop'); 49 + let result4 = ''; 50 + const arr2 = [[1,2], [3,4], [5,6]]; 51 + outer4: for (const row of arr2) { 52 + for (const val of row) { 53 + if (val % 2 === 0) { 54 + continue outer4; 55 + } 56 + result4 += val + ','; 57 + } 58 + } 59 + console.log('Result: ' + result4); 60 + console.log('Expected: 1,3,5,'); 61 + 62 + // Test 5: Labeled break in for-in loop 63 + console.log('\nTest 5: Labeled break in for-in loop'); 64 + let count5 = 0; 65 + let brokeEarly5 = false; 66 + const obj1 = { a: 1, b: 2, c: 3 }; 67 + outer5: for (const key1 in obj1) { 68 + for (const key2 in obj1) { 69 + count5++; 70 + if (count5 === 4) { 71 + brokeEarly5 = true; 72 + break outer5; 73 + } 74 + } 75 + } 76 + console.log('Count: ' + count5 + ', Broke early: ' + brokeEarly5); 77 + console.log('Expected: Count: 4, Broke early: true'); 78 + 79 + // Test 6: Labeled continue in for-in loop 80 + console.log('\nTest 6: Labeled continue in for-in loop'); 81 + let outerCount6 = 0; 82 + let innerCount6 = 0; 83 + const obj2 = { a: 1, b: 2, c: 3 }; 84 + outer6: for (const key1 in obj2) { 85 + outerCount6++; 86 + for (const key2 in obj2) { 87 + innerCount6++; 88 + continue outer6; // Should skip rest of inner loop 89 + } 90 + } 91 + // Each outer iteration should only do 1 inner iteration before continuing outer 92 + console.log('Outer: ' + outerCount6 + ', Inner: ' + innerCount6); 93 + console.log('Expected: Outer: 3, Inner: 3'); 94 + 95 + // Test 7: Labeled break in while loop 96 + console.log('\nTest 7: Labeled break in while loop'); 97 + let result7 = ''; 98 + let i7 = 0; 99 + outer7: while (i7 < 3) { 100 + let j7 = 0; 101 + while (j7 < 3) { 102 + if (i7 === 1 && j7 === 1) { 103 + break outer7; 104 + } 105 + result7 += `(${i7},${j7})`; 106 + j7++; 107 + } 108 + i7++; 109 + } 110 + console.log('Result: ' + result7); 111 + console.log('Expected: (0,0)(0,1)(0,2)(1,0)'); 112 + 113 + // Test 8: Labeled continue in while loop 114 + console.log('\nTest 8: Labeled continue in while loop'); 115 + let result8 = ''; 116 + let i8 = 0; 117 + outer8: while (i8 < 3) { 118 + let j8 = 0; 119 + while (j8 < 3) { 120 + if (j8 === 1) { 121 + i8++; 122 + continue outer8; 123 + } 124 + result8 += `(${i8},${j8})`; 125 + j8++; 126 + } 127 + i8++; 128 + } 129 + console.log('Result: ' + result8); 130 + console.log('Expected: (0,0)(1,0)(2,0)'); 131 + 132 + // Test 9: Triple nested loops with labeled break 133 + console.log('\nTest 9: Triple nested with labeled break'); 134 + let result9 = ''; 135 + outer9: for (let i = 0; i < 2; i++) { 136 + middle9: for (let j = 0; j < 2; j++) { 137 + for (let k = 0; k < 2; k++) { 138 + if (i === 1 && j === 0 && k === 1) { 139 + break outer9; 140 + } 141 + result9 += `(${i},${j},${k})`; 142 + } 143 + } 144 + } 145 + console.log('Result: ' + result9); 146 + console.log('Expected: (0,0,0)(0,0,1)(0,1,0)(0,1,1)(1,0,0)'); 147 + 148 + // Test 10: Triple nested with middle label break 149 + console.log('\nTest 10: Triple nested with middle label break'); 150 + let result10 = ''; 151 + outer10: for (let i = 0; i < 2; i++) { 152 + middle10: for (let j = 0; j < 2; j++) { 153 + for (let k = 0; k < 2; k++) { 154 + if (j === 1 && k === 0) { 155 + break middle10; 156 + } 157 + result10 += `(${i},${j},${k})`; 158 + } 159 + } 160 + } 161 + console.log('Result: ' + result10); 162 + console.log('Expected: (0,0,0)(0,0,1)(1,0,0)(1,0,1)'); 163 + 164 + // Test 11: for-of with string iteration and labeled continue 165 + console.log('\nTest 11: for-of string with labeled continue'); 166 + let result11 = ''; 167 + const strings = ['ab', 'cd', 'ef']; 168 + outer11: for (const str of strings) { 169 + for (const ch of str) { 170 + if (ch === 'c' || ch === 'e') { 171 + continue outer11; 172 + } 173 + result11 += ch; 174 + } 175 + } 176 + console.log('Result: ' + result11); 177 + console.log('Expected: ab'); 178 + 179 + console.log('\n=== All labeled loop tests completed ===');