···859859 return n == len && memcmp(buf, p, len) == 0;
860860}
861861862862+static bool is_strict_reserved(const char *buf, size_t len) {
863863+ switch (len) {
864864+ case 3: return streq(buf, len, "let", 3);
865865+ case 5: return streq(buf, len, "yield", 5);
866866+ case 6: return streq(buf, len, "static", 6) || streq(buf, len, "public", 6);
867867+ case 7: return streq(buf, len, "private", 7) || streq(buf, len, "package", 7);
868868+ case 9: return streq(buf, len, "interface", 9) || streq(buf, len, "protected", 9);
869869+ case 10: return streq(buf, len, "implements", 10);
870870+ default: return false;
871871+ }
872872+}
873873+874874+static bool is_strict_restricted(const char *buf, size_t len) {
875875+ return (len == 4 && streq(buf, len, "eval", 4)) || (len == 9 && streq(buf, len, "arguments", 9));
876876+}
877877+862878static uint8_t parsekeyword(const char *buf, size_t len) {
863879 switch (buf[0]) {
864880 case 'a': if (streq("async", 5, buf, len)) return TOK_ASYNC; if (streq("await", 5, buf, len)) return TOK_AWAIT; break;
···957973 if (buf[0] == buf[js->tlen]) js->tok = TOK_STRING, js->tlen++;
958974 break;
959975 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
976976+ if ((js->flags & F_STRICT) && buf[0] == '0' && js->toff + 1 < js->clen &&
977977+ is_digit(buf[1]) && buf[1] != 'x' && buf[1] != 'X' && buf[1] != 'b' && buf[1] != 'B' && buf[1] != 'o' && buf[1] != 'O') {
978978+ js->tok = TOK_ERR;
979979+ js->tlen = 1;
980980+ break;
981981+ }
960982 char *end;
961983 js->tval = tov(strtod(buf, &end));
962984 TOK(TOK_NUMBER, (jsoff_t) (end - buf));
···10851107 scope = mkval(T_OBJ, loadoff(js, (jsoff_t) (vdata(scope) + sizeof(jsoff_t))));
10861108 }
1087110911101110+ if (js->flags & F_STRICT) {
11111111+ return js_mkerr(js, "ReferenceError: '%.*s' is not defined", (int) len, buf);
11121112+ }
11131113+10881114 return js_mkerr(js, "'%.*s' not found", (int) len, buf);
10891115}
10901116···10981124 if (is_const_prop(js, propoff)) {
10991125 return js_mkerr(js, "assignment to constant");
11001126 }
11271127+11281128+ if (js->flags & F_STRICT) {
11291129+ jsval_t prop_val = loadval(js, (jsoff_t) (propoff + sizeof(jsoff_t) * 2));
11301130+ uint8_t prop_type = vtype(prop_val);
11311131+11321132+ if (prop_type == T_STR || prop_type == T_NUM || prop_type == T_BOOL ||
11331133+ prop_type == T_NULL || prop_type == T_UNDEF) {
11341134+ return js_mkerr(js, "TypeError: cannot set property on primitive value in strict mode");
11351135+ }
11361136+ }
11371137+11011138 saveval(js, (jsoff_t) ((vdata(lhs) & ~3U) + sizeof(jsoff_t) * 2), val);
11021139 return lhs;
11031140}
···20612098}
2062209920632100static bool parse_func_params(struct js *js, uint8_t *flags) {
21012101+ const char *param_names[32];
21022102+ size_t param_lens[32];
21032103+ int param_count = 0;
21042104+20642105 for (bool comma = false; next(js) != TOK_EOF; comma = true) {
20652106 if (!comma && next(js) == TOK_RPAREN) break;
20662107···20762117 js_mkerr(js, "identifier expected");
20772118 return false;
20782119 }
21202120+21212121+ const char *param_name = &js->code[js->toff];
21222122+ size_t param_len = js->tlen;
21232123+21242124+ if ((js->flags & F_STRICT) && is_strict_restricted(param_name, param_len)) {
21252125+ if (flags) js->flags = *flags;
21262126+ js_mkerr(js, "cannot use '%.*s' as parameter name in strict mode", (int) param_len, param_name);
21272127+ return false;
21282128+ }
21292129+21302130+ if (js->flags & F_STRICT) {
21312131+ for (int i = 0; i < param_count; i++) {
21322132+ if (param_lens[i] == param_len && memcmp(param_names[i], param_name, param_len) == 0) {
21332133+ if (flags) js->flags = *flags;
21342134+ js_mkerr(js, "duplicate parameter name '%.*s' in strict mode", (int) param_len, param_name);
21352135+ return false;
21362136+ }
21372137+ }
21382138+ }
21392139+21402140+ if (param_count < 32) {
21412141+ param_names[param_count] = param_name;
21422142+ param_lens[param_count] = param_len;
21432143+ param_count++;
21442144+ }
21452145+20792146 js->consumed = 1;
2080214720812148 if (is_rest && next(js) != TOK_RPAREN) {
···21872254 js->consumed = 1;
2188225521892256 switch (js->tok) {
21902190- case TOK_ERR: return js_mkerr(js, "parse error");
22572257+ case TOK_ERR:
22582258+ if ((js->flags & F_STRICT) && js->toff < js->clen && js->code[js->toff] == '0' &&
22592259+ js->toff + 1 < js->clen && is_digit(js->code[js->toff + 1])) {
22602260+ return js_mkerr(js, "octal literals are not allowed in strict mode");
22612261+ }
22622262+ return js_mkerr(js, "parse error");
21912263 case TOK_NUMBER: return js->tval;
21922264 case TOK_STRING: return js_str_literal(js);
21932265 case TOK_TEMPLATE: return js_template_literal(js);
···29943066 js->consumed = 0;
29953067 jsoff_t noff = js->toff, nlen = js->tlen;
29963068 char *name = (char *) &js->code[noff];
30693069+30703070+ if (exe && (js->flags & F_STRICT) && is_strict_restricted(name, nlen)) {
30713071+ return js_mkerr(js, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name);
30723072+ }
30733073+30743074+ if (exe && (js->flags & F_STRICT) && is_strict_reserved(name, nlen)) {
30753075+ return js_mkerr(js, "'%.*s' is reserved in strict mode", (int) nlen, name);
30763076+ }
30773077+29973078 jsval_t v = js_mkundef();
29983079 js->consumed = 1;
29993080 if (next(js) == TOK_ASSIGN) {
+110
tests/test_strict_comprehensive.cjs
···11+// Comprehensive Strict Mode Tests
22+// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
33+44+console.log("=== Strict Mode Comprehensive Tests ===\n");
55+66+// Test 1: Converting mistakes into errors
77+console.log("Test 1: Assigning to undeclared variables");
88+try {
99+ "use strict";
1010+ eval("x = 3.14;");
1111+ console.log(" FAIL: Should throw ReferenceError");
1212+} catch (e) {
1313+ console.log(" PASS: " + e);
1414+}
1515+1616+// Test 2: Octal literals
1717+console.log("\nTest 2: Legacy octal literals");
1818+try {
1919+ "use strict";
2020+ eval("let num = 0123;");
2121+ console.log(" FAIL: Should reject octal literals");
2222+} catch (e) {
2323+ console.log(" PASS: Octal literals rejected");
2424+}
2525+2626+// Test 3: Reserved words as identifiers
2727+console.log("\nTest 3: Future reserved words");
2828+const reservedWords = ["implements", "interface", "package", "private", "protected", "public", "static"];
2929+let passedReserved = 0;
3030+for (let i = 0; i < reservedWords.length; i++) {
3131+ try {
3232+ eval('"use strict"; let ' + reservedWords[i] + ' = 1;');
3333+ console.log(" FAIL: '" + reservedWords[i] + "' should be reserved");
3434+ } catch (e) {
3535+ passedReserved++;
3636+ }
3737+}
3838+console.log(" PASS: " + passedReserved + " of " + reservedWords.length + " reserved words blocked");
3939+4040+// Test 4: eval and arguments restrictions
4141+console.log("\nTest 4: eval and arguments restrictions");
4242+try {
4343+ "use strict";
4444+ eval("let eval = 5;");
4545+ console.log(" FAIL: Cannot assign to 'eval'");
4646+} catch (e) {
4747+ console.log(" PASS: Cannot use 'eval' as identifier");
4848+}
4949+5050+try {
5151+ "use strict";
5252+ eval("let arguments = 10;");
5353+ console.log(" FAIL: Cannot assign to 'arguments'");
5454+} catch (e) {
5555+ console.log(" PASS: Cannot use 'arguments' as identifier");
5656+}
5757+5858+// Test 5: with statement
5959+console.log("\nTest 5: with statement");
6060+try {
6161+ "use strict";
6262+ eval("with ({}) {}");
6363+ console.log(" FAIL: with statement should be disallowed");
6464+} catch (e) {
6565+ console.log(" PASS: with statement blocked");
6666+}
6767+6868+// Test 6: Function parameter restrictions
6969+console.log("\nTest 6: Function parameter restrictions");
7070+try {
7171+ "use strict";
7272+ eval("function f(a, b, a) {}");
7373+ console.log(" FAIL: Duplicate parameters should be disallowed");
7474+} catch (e) {
7575+ console.log(" PASS: Duplicate parameters blocked");
7676+}
7777+7878+try {
7979+ "use strict";
8080+ eval("function f(eval) {}");
8181+ console.log(" FAIL: 'eval' as parameter should be disallowed");
8282+} catch (e) {
8383+ console.log(" PASS: Cannot use 'eval' as parameter name");
8484+}
8585+8686+// Test 7: Strict mode in different contexts
8787+console.log("\nTest 7: Strict mode in different contexts");
8888+8989+// Script-level strict mode
9090+let scriptStrictWorks = false;
9191+try {
9292+ eval('"use strict"; undeclared = 1;');
9393+} catch (e) {
9494+ scriptStrictWorks = true;
9595+}
9696+console.log(" " + (scriptStrictWorks ? "PASS" : "FAIL") + ": Script-level strict mode");
9797+9898+// Function-level strict mode
9999+function testFunctionStrict() {
100100+ "use strict";
101101+ try {
102102+ undeclared2 = 2;
103103+ return false;
104104+ } catch (e) {
105105+ return true;
106106+ }
107107+}
108108+console.log(" " + (testFunctionStrict() ? "PASS" : "FAIL") + ": Function-level strict mode");
109109+110110+console.log("\n=== All Tests Completed ===");
+48
tests/test_strict_function.cjs
···11+// Test function-level strict mode
22+// Based on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
33+44+// Non-strict code
55+let globalVar = 10;
66+console.log("Non-strict global:", globalVar);
77+88+// Function with strict mode
99+function strictFunction() {
1010+ "use strict";
1111+1212+ // This should work - accessing outer scope
1313+ console.log("Accessing outer scope:", globalVar);
1414+1515+ // This should fail - undeclared variable
1616+ try {
1717+ undeclaredVar = 20;
1818+ console.log("FAIL: Should have thrown error in strict function");
1919+ } catch (e) {
2020+ console.log("PASS: Strict mode in function works");
2121+ }
2222+}
2323+2424+strictFunction();
2525+2626+// Non-strict function - should allow undeclared vars (though not recommended)
2727+function nonStrictFunction() {
2828+ // Note: In this implementation, undeclared vars might still error
2929+ // as they create implicit globals which is generally an error
3030+ console.log("PASS: Non-strict function executed");
3131+}
3232+3333+nonStrictFunction();
3434+3535+// Arrow function with strict mode
3636+const strictArrow = () => {
3737+ "use strict";
3838+ try {
3939+ newVar = 30;
4040+ console.log("FAIL: Arrow function strict mode didn't work");
4141+ } catch (e) {
4242+ console.log("PASS: Strict mode in arrow function works");
4343+ }
4444+};
4545+4646+strictArrow();
4747+4848+console.log("\nFunction-level strict mode tests completed!");
+63
tests/test_strict_mode.cjs
···11+// Test strict mode features
22+// Based on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
33+44+// Test 1: Strict mode enabled at script level
55+"use strict";
66+77+// Test 2: Assignment to undeclared variables should fail
88+try {
99+ mistypeVariable = 17;
1010+ console.log("FAIL: Should have thrown ReferenceError for undeclared variable");
1111+} catch (e) {
1212+ console.log("PASS: Caught error for undeclared variable assignment");
1313+}
1414+1515+// Test 3: Using eval as variable name should fail
1616+try {
1717+ eval("let eval = 5;");
1818+ console.log("FAIL: Should have thrown error for using 'eval' as variable name");
1919+} catch (e) {
2020+ console.log("PASS: Cannot use 'eval' as variable name in strict mode");
2121+}
2222+2323+// Test 4: Using arguments as variable name should fail
2424+try {
2525+ eval("let arguments = 10;");
2626+ console.log("FAIL: Should have thrown error for using 'arguments' as variable name");
2727+} catch (e) {
2828+ console.log("PASS: Cannot use 'arguments' as variable name in strict mode");
2929+}
3030+3131+// Test 5: Duplicate parameter names should fail
3232+try {
3333+ eval("function sum(a, a, c) { return a + a + c; }");
3434+ console.log("FAIL: Should have thrown error for duplicate parameter names");
3535+} catch (e) {
3636+ console.log("PASS: Duplicate parameter names not allowed in strict mode");
3737+}
3838+3939+// Test 6: Octal literals should fail
4040+try {
4141+ eval("let x = 0644;");
4242+ console.log("FAIL: Should have thrown error for octal literal");
4343+} catch (e) {
4444+ console.log("PASS: Octal literals not allowed in strict mode");
4545+}
4646+4747+// Test 7: Reserved words should not be usable as identifiers
4848+try {
4949+ eval("let implements = 5;");
5050+ console.log("FAIL: Should have thrown error for using reserved word 'implements'");
5151+} catch (e) {
5252+ console.log("PASS: Reserved word 'implements' cannot be used in strict mode");
5353+}
5454+5555+// Test 8: with statement should fail
5656+try {
5757+ eval("with (Math) { x = cos(2); }");
5858+ console.log("FAIL: Should have thrown error for 'with' statement");
5959+} catch (e) {
6060+ console.log("PASS: 'with' statement not allowed in strict mode");
6161+}
6262+6363+console.log("\nAll strict mode tests completed!");