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.

fix scope management in function calls

+487 -66
+97 -64
src/ant.c
··· 2948 2948 2949 2949 no_descriptor: 2950 2950 if (existing <= 0) goto create_new; 2951 - 2952 - if (is_const_prop(js, existing)) { 2953 - if (js->flags & F_STRICT) return js_mkerr(js, "assignment to constant"); 2954 - return mkval(T_PROP, existing); 2955 - } 2951 + if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant"); 2956 2952 2957 2953 saveval(js, existing + sizeof(jsoff_t) * 2, v); 2958 2954 if (vtype(obj) != T_ARR || klen == 0 || key[0] < '0' || key[0] > '9') goto done_update; ··· 5003 4999 if (vtype(lhs) != T_PROP) { 5004 5000 if (js->flags & F_STRICT) { 5005 5001 return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side in assignment"); 5006 - } 5007 - return val; 5002 + } return val; 5008 5003 } 5009 5004 5010 5005 jsoff_t propoff = (jsoff_t) vdata(lhs); 5011 - 5012 5006 jsoff_t koff = loadoff(js, propoff + sizeof(jsoff_t)); 5013 5007 jsoff_t klen = offtolen(loadoff(js, koff)); 5008 + 5014 5009 const char *key = (char *)&js->mem[koff + sizeof(jsoff_t)]; 5015 - 5016 - if (is_const_prop(js, propoff)) { 5017 - if (js->flags & F_STRICT) return js_mkerr(js, "assignment to constant"); 5018 - return mkval(T_PROP, propoff); 5019 - } 5010 + if (is_const_prop(js, propoff)) return js_mkerr(js, "assignment to constant"); 5020 5011 5021 5012 if ((klen == 9 && memcmp(key, "undefined", 9) == 0) || 5022 5013 (klen == 3 && memcmp(key, "NaN", 3) == 0) || ··· 6087 6078 6088 6079 jsval_t saved_scope = js->scope; 6089 6080 if (global_scope_stack == NULL) utarray_new(global_scope_stack, &jsoff_icd); 6090 - utarray_push_back(global_scope_stack, &parent_scope_offset); 6091 6081 jsval_t function_scope = mkobj(js, parent_scope_offset); 6082 + jsoff_t function_scope_offset = (jsoff_t) vdata(function_scope); 6083 + utarray_push_back(global_scope_stack, &function_scope_offset); 6092 6084 js->scope = function_scope; 6093 6085 6094 6086 jsval_t slot_name = get_slot(js, func_val, SLOT_NAME); ··· 6659 6651 uint8_t lhs_type = vtype(lhs); 6660 6652 if (lhs_type != T_PROP && lhs_type != T_PROPREF) 6661 6653 return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side expression in postfix operation"); 6662 - do_assign_op(js, op == TOK_POSTINC ? TOK_PLUS_ASSIGN : TOK_MINUS_ASSIGN, lhs, tov(1)); 6654 + jsval_t assign_res = do_assign_op(js, op == TOK_POSTINC ? TOK_PLUS_ASSIGN : TOK_MINUS_ASSIGN, lhs, tov(1)); 6655 + if (is_err(assign_res)) return assign_res; 6663 6656 return l; 6664 6657 } 6665 6658 ··· 8757 8750 if (js->flags & F_NOEXEC) return operand; 8758 8751 jsval_t resolved = resolveprop(js, operand); 8759 8752 if (vtype(operand) == T_PROP || vtype(operand) == T_PROPREF) { 8760 - do_assign_op(js, op == TOK_POSTINC ? TOK_PLUS_ASSIGN : TOK_MINUS_ASSIGN, operand, tov(1)); 8761 - } else { 8762 - return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side in assignment"); 8763 - } 8753 + jsval_t assign_res = do_assign_op(js, op == TOK_POSTINC ? TOK_PLUS_ASSIGN : TOK_MINUS_ASSIGN, operand, tov(1)); 8754 + if (is_err(assign_res)) return assign_res; 8755 + } else return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side in assignment"); 8764 8756 return do_op(js, op == TOK_POSTINC ? TOK_PLUS : TOK_MINUS, resolved, tov(1)); 8765 8757 } 8766 8758 ··· 10151 10143 } else if (next(js) == TOK_OF) { 10152 10144 is_for_of = true; 10153 10145 js->consumed = 1; 10146 + } else if (next(js) == TOK_ASSIGN) { 10147 + js->pos = destructure_off; 10148 + js->consumed = 1; 10149 + if (is_const_var) v = js_const(js); 10150 + else if (is_var_decl) v = js_var_decl(js); 10151 + else v = js_let(js); 10152 + if (is_err2(&v, &res)) goto done; 10153 + has_destructure = false; 10154 10154 } else { 10155 - res = js_mkerr_typed(js, JS_ERR_SYNTAX, "expected 'in' or 'of' after destructuring pattern"); 10155 + res = js_mkerr_typed(js, JS_ERR_SYNTAX, "expected 'in', 'of', or '=' after destructuring pattern"); 10156 10156 goto done; 10157 10157 } 10158 10158 } else if (next(js) == TOK_IDENTIFIER) { ··· 10172 10172 } else { 10173 10173 js->pos = var_name_off; 10174 10174 js->consumed = 1; 10175 - if (is_const_var) { 10176 - v = js_const(js); 10177 - } else if (is_var_decl) { 10178 - v = js_var_decl(js); 10179 - } else { 10180 - v = js_let(js); 10181 - } 10175 + if (is_const_var) v = js_const(js); 10176 + else if (is_var_decl) v = js_var_decl(js); 10177 + else v = js_let(js); 10182 10178 if (is_err2(&v, &res)) goto done; 10183 10179 } 10184 10180 } ··· 11653 11649 11654 11650 js->consumed = 1; 11655 11651 for (;;) { 11656 - EXPECT_IDENT(); 11657 - js->consumed = 0; 11658 - jsoff_t noff = js->toff, nlen = js->tlen; 11659 - char *name = (char *) &js->code[noff]; 11652 + uint8_t tok = next(js); 11660 11653 11661 - if (exe && (js->flags & F_STRICT) && is_strict_restricted(name, nlen)) { 11662 - return js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 11663 - } 11664 - 11665 - if (exe && (js->flags & F_STRICT) && is_strict_reserved(name, nlen)) { 11666 - return js_mkerr_typed(js, JS_ERR_SYNTAX, "'%.*s' is reserved in strict mode", (int) nlen, name); 11667 - } 11668 - 11669 - jsval_t v = js_mkundef(); 11670 - bool has_initializer = false; 11671 - js->consumed = 1; 11672 - if (next(js) == TOK_ASSIGN) { 11654 + if (tok == TOK_LBRACKET || tok == TOK_LBRACE) { 11655 + jsoff_t pattern_start = js->toff; 11656 + uint8_t close_tok = (tok == TOK_LBRACKET) ? TOK_RBRACKET : TOK_RBRACE; 11657 + 11658 + js->consumed = 1; 11659 + int depth = 1; 11660 + while (depth > 0 && next(js) != TOK_EOF) { 11661 + if (js->tok == tok) depth++; 11662 + else if (js->tok == close_tok) depth--; 11663 + if (depth > 0) js->consumed = 1; 11664 + } 11673 11665 js->consumed = 1; 11674 - v = js_expr(js); 11666 + jsoff_t pattern_end = js->pos; 11667 + jsoff_t pattern_len = pattern_end - pattern_start; 11668 + 11669 + if (next(js) != TOK_ASSIGN) { 11670 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "destructuring requires assignment"); 11671 + } js->consumed = 1; 11672 + 11673 + jsval_t v = js_expr(js); 11675 11674 if (is_err(v)) return v; 11676 - has_initializer = true; 11677 - } 11678 - 11679 - if (exe) { 11680 - char decoded_name[256]; 11681 - size_t decoded_len = decode_ident_escapes(name, nlen, decoded_name, sizeof(decoded_name)); 11675 + 11676 + if (exe) { 11677 + jsval_t val = resolveprop(js, v); 11678 + jsval_t r = bind_destruct_pattern(js, &js->code[pattern_start], pattern_len, val, var_scope); 11679 + if (is_err(r)) return r; 11680 + } 11681 + } else { 11682 + EXPECT_IDENT(); 11683 + js->consumed = 0; 11684 + jsoff_t noff = js->toff, nlen = js->tlen; 11685 + char *name = (char *) &js->code[noff]; 11686 + 11687 + if (exe && (js->flags & F_STRICT) && is_strict_restricted(name, nlen)) { 11688 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 11689 + } 11690 + 11691 + if (exe && (js->flags & F_STRICT) && is_strict_reserved(name, nlen)) { 11692 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "'%.*s' is reserved in strict mode", (int) nlen, name); 11693 + } 11694 + 11695 + jsval_t v = js_mkundef(); 11696 + bool has_initializer = false; 11697 + js->consumed = 1; 11698 + if (next(js) == TOK_ASSIGN) { 11699 + js->consumed = 1; 11700 + v = js_expr(js); 11701 + if (is_err(v)) return v; 11702 + has_initializer = true; 11703 + } 11682 11704 11683 - jsoff_t existing_off = lkp(js, var_scope, decoded_name, decoded_len); 11684 - if (existing_off > 0) { 11685 - if (has_initializer && !is_err(v)) { 11686 - jsval_t key_val = js_mkstr(js, decoded_name, decoded_len); 11687 - setprop(js, var_scope, key_val, resolveprop(js, v)); 11705 + if (exe) { 11706 + char decoded_name[256]; 11707 + size_t decoded_len = decode_ident_escapes(name, nlen, decoded_name, sizeof(decoded_name)); 11708 + 11709 + jsoff_t existing_off = lkp(js, var_scope, decoded_name, decoded_len); 11710 + if (existing_off > 0) { 11711 + if (has_initializer && !is_err(v)) { 11712 + jsval_t key_val = js_mkstr(js, decoded_name, decoded_len); 11713 + setprop(js, var_scope, key_val, resolveprop(js, v)); 11714 + } 11715 + } else { 11716 + jsval_t x = mkprop(js, var_scope, js_mkstr(js, decoded_name, decoded_len), resolveprop(js, v), 0); 11717 + if (is_err(x)) return x; 11688 11718 } 11689 - } else { 11690 - jsval_t x = mkprop(js, var_scope, js_mkstr(js, decoded_name, decoded_len), resolveprop(js, v), 0); 11691 - if (is_err(x)) return x; 11692 11719 } 11693 11720 } 11694 11721 ··· 21189 21216 jsoff_t existing = lkp(js, obj, key, key_len); 21190 21217 if (existing > 0) { 21191 21218 if (is_const_prop(js, existing)) { 21192 - if (js->flags & F_STRICT) js_mkerr(js, "assignment to constant"); 21219 + js_mkerr(js, "assignment to constant"); 21193 21220 return; 21194 21221 } 21195 21222 saveval(js, existing + sizeof(jsoff_t) * 2, val); ··· 21202 21229 jsoff_t existing = lkp(js, func_obj, key, key_len); 21203 21230 if (existing > 0) { 21204 21231 if (is_const_prop(js, existing)) { 21205 - if (js->flags & F_STRICT) js_mkerr(js, "assignment to constant"); 21232 + js_mkerr(js, "assignment to constant"); 21206 21233 return; 21207 21234 } 21208 21235 saveval(js, existing + sizeof(jsoff_t) * 2, val); ··· 21730 21757 mkscope(js); 21731 21758 js->flags = saved_flags; 21732 21759 21760 + if (global_scope_stack == NULL) utarray_new(global_scope_stack, &jsoff_icd); 21761 + jsoff_t function_scope_offset = (jsoff_t) vdata(js->scope); 21762 + 21763 + utarray_push_back(global_scope_stack, &function_scope_offset); 21764 + hoist_var_declarations_from_slot(js, js->scope, func_obj); 21765 + 21733 21766 parsed_func_t *pf = get_or_parse_func(fn, fnlen); 21734 21767 if (!pf) { 21735 - delscope(js); 21736 - js->scope = saved_scope; 21768 + if (global_scope_stack && utarray_len(global_scope_stack) > 0) utarray_pop_back(global_scope_stack); 21769 + delscope(js); js->scope = saved_scope; 21737 21770 if (combined_args) free(combined_args); 21738 21771 js->gc_suppress = saved_gc_suppress; 21739 21772 return js_mkerr(js, "failed to parse function"); ··· 21807 21840 js->flags = caller_flags; 21808 21841 21809 21842 js->this_val = saved_this; 21810 - delscope(js); 21811 - js->scope = saved_scope; 21843 + if (global_scope_stack && utarray_len(global_scope_stack) > 0) utarray_pop_back(global_scope_stack); 21844 + delscope(js); js->scope = saved_scope; 21812 21845 if (combined_args) free(combined_args); 21813 21846 21814 21847 js->gc_suppress = saved_gc_suppress;
+2 -2
src/repl.c
··· 331 331 if (c == '\r' || c == '\n') return (key_event_t){ KEY_ENTER, 0 }; 332 332 if (c == 3) { ctrl_c_pressed++; return (key_event_t){ KEY_EOF, 0 }; } 333 333 if (c == 4 || c == 26) return (key_event_t){ KEY_EOF, 0 }; 334 - if (isprint(c)) return (key_event_t){ KEY_CHAR, c }; 334 + if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c }; 335 335 return (key_event_t){ KEY_NONE, 0 }; 336 336 } 337 337 #define TERM_INIT() ··· 356 356 } 357 357 if (c == 127 || c == 8) return (key_event_t){ KEY_BACKSPACE, 0 }; 358 358 if (c == '\n' || c == '\r') return (key_event_t){ KEY_ENTER, 0 }; 359 - if (isprint(c)) return (key_event_t){ KEY_CHAR, c }; 359 + if (isprint(c) || (unsigned char)c >= 0x80) return (key_event_t){ KEY_CHAR, c }; 360 360 return (key_event_t){ KEY_NONE, 0 }; 361 361 } 362 362 #define TERM_INIT() do { \
+388
tests/var.js
··· 1 + // ============================================ 2 + // COMPREHENSIVE VAR DECLARATION BUG TEST 3 + // ============================================ 4 + 5 + console.log('=== TEST 1: Regular IIFE (baseline) ==='); 6 + (function () { 7 + var a = 1; 8 + var b = 2, 9 + c = 3; 10 + if (true) { 11 + var d = 4; 12 + } 13 + { 14 + var e = 5; 15 + } 16 + for (var f = 6; f < 7; f++) { 17 + var g = 7; 18 + } 19 + while (false) { 20 + var h = 8; 21 + } 22 + do { 23 + var i = 9; 24 + } while (false); 25 + try { 26 + var j = 10; 27 + } catch (err) { 28 + var k = 11; 29 + } 30 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' e=' + e + ' f=' + f + ' g=' + g + ' h=' + h + ' i=' + i + ' j=' + j + ' k=' + k); 31 + })(); 32 + 33 + console.log('\n=== TEST 2: Arrow IIFE ==='); 34 + (() => { 35 + var a = 1; 36 + var b = 2, 37 + c = 3; 38 + if (true) { 39 + var d = 4; 40 + } 41 + { 42 + var e = 5; 43 + } 44 + for (var f = 6; f < 7; f++) { 45 + var g = 7; 46 + } 47 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' e=' + e + ' f=' + f + ' g=' + g); 48 + })(); 49 + 50 + console.log('\n=== TEST 3: User-defined callback ==='); 51 + function callFn(cb) { 52 + cb(100); 53 + } 54 + callFn(function (x) { 55 + var a = 1; 56 + var b = 2, 57 + c = 3; 58 + if (true) { 59 + var d = 4; 60 + } 61 + for (var f = 6; f < 7; f++) { 62 + var g = 7; 63 + } 64 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' f=' + f + ' g=' + g + ' x=' + x); 65 + }); 66 + 67 + console.log('\n=== TEST 4: User-defined callback (arrow) ==='); 68 + callFn(x => { 69 + var a = 1; 70 + var b = 2, 71 + c = 3; 72 + if (true) { 73 + var d = 4; 74 + } 75 + for (var f = 6; f < 7; f++) { 76 + var g = 7; 77 + } 78 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' f=' + f + ' g=' + g + ' x=' + x); 79 + }); 80 + 81 + console.log('\n=== TEST 5: forEach with regular function ==='); 82 + [1].forEach(function (x, idx, arr) { 83 + var a = 1; 84 + var b = 2, 85 + c = 3; 86 + if (true) { 87 + var d = 4; 88 + } 89 + { 90 + var e = 5; 91 + } 92 + for (var f = 6; f < 7; f++) { 93 + var g = 7; 94 + } 95 + while (false) { 96 + var h = 8; 97 + } 98 + do { 99 + var i = 9; 100 + } while (false); 101 + try { 102 + var j = 10; 103 + } catch (err) { 104 + var k = 11; 105 + } 106 + switch (1) { 107 + case 1: 108 + var l = 12; 109 + break; 110 + } 111 + console.log( 112 + ' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' e=' + e + ' f=' + f + ' g=' + g + ' h=' + h + ' i=' + i + ' j=' + j + ' k=' + k + ' l=' + l 113 + ); 114 + }); 115 + 116 + console.log('\n=== TEST 6: forEach with arrow function ==='); 117 + [1].forEach((x, idx, arr) => { 118 + var a = 1; 119 + var b = 2, 120 + c = 3; 121 + if (true) { 122 + var d = 4; 123 + } 124 + { 125 + var e = 5; 126 + } 127 + for (var f = 6; f < 7; f++) { 128 + var g = 7; 129 + } 130 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' e=' + e + ' f=' + f + ' g=' + g); 131 + }); 132 + 133 + console.log('\n=== TEST 7: map with regular function ==='); 134 + [1].map(function (x) { 135 + var a = 1; 136 + if (true) { 137 + var b = 2; 138 + } 139 + for (var c = 3; c < 4; c++) { 140 + var d = 4; 141 + } 142 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d); 143 + return x; 144 + }); 145 + 146 + console.log('\n=== TEST 8: filter with regular function ==='); 147 + [1].filter(function (x) { 148 + var a = 1; 149 + if (true) { 150 + var b = 2; 151 + } 152 + for (var c = 3; c < 4; c++) { 153 + var d = 4; 154 + } 155 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d); 156 + return true; 157 + }); 158 + 159 + console.log('\n=== TEST 9: reduce with regular function ==='); 160 + [1, 2].reduce(function (acc, x) { 161 + var a = 1; 162 + if (true) { 163 + var b = 2; 164 + } 165 + for (var c = 3; c < 4; c++) { 166 + var d = 4; 167 + } 168 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' acc=' + acc + ' x=' + x); 169 + return acc + x; 170 + }, 0); 171 + 172 + console.log('\n=== TEST 10: find with regular function ==='); 173 + [1].find(function (x) { 174 + var a = 1; 175 + for (var b = 2; b < 3; b++) { 176 + var c = 3; 177 + } 178 + console.log(' a=' + a + ' b=' + b + ' c=' + c); 179 + return true; 180 + }); 181 + 182 + console.log('\n=== TEST 11: some with regular function ==='); 183 + [1].some(function (x) { 184 + var a = 1; 185 + for (var b = 2; b < 3; b++) { 186 + var c = 3; 187 + } 188 + console.log(' a=' + a + ' b=' + b + ' c=' + c); 189 + return true; 190 + }); 191 + 192 + console.log('\n=== TEST 12: every with regular function ==='); 193 + [1].every(function (x) { 194 + var a = 1; 195 + for (var b = 2; b < 3; b++) { 196 + var c = 3; 197 + } 198 + console.log(' a=' + a + ' b=' + b + ' c=' + c); 199 + return true; 200 + }); 201 + 202 + console.log('\n=== TEST 13: sort with regular function ==='); 203 + [2, 1].sort(function (a, b) { 204 + var v = 1; 205 + for (var i = 0; i < 1; i++) { 206 + var w = 2; 207 + } 208 + console.log(' v=' + v + ' i=' + i + ' w=' + w); 209 + return a - b; 210 + }); 211 + 212 + console.log('\n=== TEST 14: flatMap with regular function ==='); 213 + [1].flatMap(function (x) { 214 + var a = 1; 215 + for (var b = 2; b < 3; b++) { 216 + var c = 3; 217 + } 218 + console.log(' a=' + a + ' b=' + b + ' c=' + c); 219 + return [x]; 220 + }); 221 + 222 + console.log('\n=== TEST 15: findIndex with regular function ==='); 223 + [1].findIndex(function (x) { 224 + var a = 1; 225 + for (var b = 2; b < 3; b++) { 226 + var c = 3; 227 + } 228 + console.log(' a=' + a + ' b=' + b + ' c=' + c); 229 + return true; 230 + }); 231 + 232 + console.log('\n=== TEST 16: let/const in forEach (should work) ==='); 233 + [1].forEach(function (x) { 234 + let a = 1; 235 + const b = 2; 236 + let c = 3, 237 + d = 4; 238 + if (true) { 239 + let e = 5; 240 + var f = 6; 241 + } 242 + for (let g = 7; g < 8; g++) { 243 + let h = 8; 244 + var i = 9; 245 + } 246 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' f=' + f + ' i=' + i); 247 + }); 248 + 249 + console.log('\n=== TEST 17: Nested forEach ==='); 250 + [1].forEach(function (x) { 251 + var outer = 'outer'; 252 + [2].forEach(function (y) { 253 + var inner = 'inner'; 254 + for (var z = 0; z < 1; z++) { 255 + var inFor = 'inFor'; 256 + } 257 + console.log(' outer=' + outer + ' inner=' + inner + ' z=' + z + ' inFor=' + inFor); 258 + }); 259 + }); 260 + 261 + console.log('\n=== TEST 18: setTimeout callback ==='); 262 + setTimeout(function () { 263 + var a = 1; 264 + if (true) { 265 + var b = 2; 266 + } 267 + for (var c = 3; c < 4; c++) { 268 + var d = 4; 269 + } 270 + console.log(' setTimeout: a=' + a + ' b=' + b + ' c=' + c + ' d=' + d); 271 + }, 0); 272 + 273 + console.log('\n=== TEST 19: Promise.then callback ==='); 274 + Promise.resolve(1).then(function (x) { 275 + var a = 1; 276 + if (true) { 277 + var b = 2; 278 + } 279 + for (var c = 3; c < 4; c++) { 280 + var d = 4; 281 + } 282 + console.log(' Promise.then: a=' + a + ' b=' + b + ' c=' + c + ' d=' + d); 283 + }); 284 + 285 + console.log('\n=== TEST 20: Object methods ==='); 286 + Object.keys({ a: 1 }).forEach(function (k) { 287 + var v = 'test'; 288 + for (var i = 0; i < 1; i++) { 289 + var w = 'for'; 290 + } 291 + console.log(' Object.keys.forEach: v=' + v + ' i=' + i + ' w=' + w); 292 + }); 293 + 294 + console.log('\n=== TEST 21: String methods ==='); 295 + 'abc'.split('').forEach(function (c) { 296 + var v = 'test'; 297 + for (var i = 0; i < 1; i++) { 298 + var w = 'for'; 299 + } 300 + console.log(' String.split.forEach: v=' + v + ' i=' + i + ' w=' + w + ' c=' + c); 301 + }); 302 + 303 + console.log('\n=== TEST 22: Map.forEach ==='); 304 + new Map([['a', 1]]).forEach(function (v, k) { 305 + var a = 1; 306 + for (var i = 0; i < 1; i++) { 307 + var b = 2; 308 + } 309 + console.log(' Map.forEach: a=' + a + ' i=' + i + ' b=' + b); 310 + }); 311 + 312 + console.log('\n=== TEST 23: Set.forEach ==='); 313 + new Set([1]).forEach(function (v) { 314 + var a = 1; 315 + for (var i = 0; i < 1; i++) { 316 + var b = 2; 317 + } 318 + console.log(' Set.forEach: a=' + a + ' i=' + i + ' b=' + b); 319 + }); 320 + 321 + console.log('\n=== TEST 24: for...of loop ==='); 322 + for (var item of [1, 2]) { 323 + var a = 'test'; 324 + console.log(' for...of: item=' + item + ' a=' + a); 325 + } 326 + 327 + console.log('\n=== TEST 25: for...in loop ==='); 328 + for (var key in { a: 1, b: 2 }) { 329 + var v = 'test'; 330 + console.log(' for...in: key=' + key + ' v=' + v); 331 + } 332 + 333 + console.log('\n=== TEST 26: Nested functions in forEach ==='); 334 + [1].forEach(function (x) { 335 + var outer = 'outer'; 336 + function inner() { 337 + var innerVar = 'inner'; 338 + console.log(' nested fn: outer=' + outer + ' innerVar=' + innerVar); 339 + } 340 + inner(); 341 + console.log(' forEach body: outer=' + outer); 342 + }); 343 + 344 + console.log('\n=== TEST 27: var in different for variants ==='); 345 + [1].forEach(function (x) { 346 + for (var a = 0; a < 1; a++) {} 347 + for (var b in { x: 1 }) { 348 + } 349 + for (var c of [1]) { 350 + } 351 + var d = 'plain'; 352 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d); 353 + }); 354 + 355 + console.log('\n=== TEST 28: Hoisting test ==='); 356 + [1].forEach(function (x) { 357 + console.log(' before: a=' + a + ' b=' + b); 358 + var a = 1; 359 + for (var b = 2; b < 3; b++) {} 360 + console.log(' after: a=' + a + ' b=' + b); 361 + }); 362 + 363 + console.log('\n=== TEST 29: Function expression assignment ==='); 364 + [1].forEach(function (x) { 365 + var fn = function () { 366 + return 'test'; 367 + }; 368 + for (var i = 0; i < 1; i++) { 369 + var fn2 = function () { 370 + return 'for'; 371 + }; 372 + } 373 + console.log(' fn=' + fn + ' fn()=' + (fn ? fn() : 'N/A') + ' fn2()=' + (fn2 ? fn2() : 'N/A')); 374 + }); 375 + 376 + console.log('\n=== TEST 30: Destructuring var ==='); 377 + try { 378 + [1].forEach(function (x) { 379 + var [a, b] = [1, 2]; 380 + var { c, d } = { c: 3, d: 4 }; 381 + for (var [e] = [5]; e < 6; e++) {} 382 + console.log(' a=' + a + ' b=' + b + ' c=' + c + ' d=' + d + ' e=' + e); 383 + }); 384 + } catch (e) { 385 + console.log(' destructuring error: ' + e.message); 386 + } 387 + 388 + console.log('\n=== ASYNC TESTS (will print after) ===');