From dfe5675f25d6535892f26dc477b3fac306157ce0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 29 Oct 2024 22:55:22 +0100 Subject: [PATCH] Allow 'undefined' in let or const declaration (#639) Except at the global scope of a classic script because... who knows, that's just how it is. Fixes: https://github.com/quickjs-ng/quickjs/issues/633 --- quickjs.c | 14 +++++++++++--- run-test262.c | 6 +++++- tests/bug633/0.js | 7 +++++++ tests/bug633/1.js | 4 ++++ tests/bug633/2.js | 4 ++++ tests/bug633/3.js | 4 ++++ 6 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/bug633/0.js create mode 100644 tests/bug633/1.js create mode 100644 tests/bug633/2.js create mode 100644 tests/bug633/3.js diff --git a/quickjs.c b/quickjs.c index 40f1842..e597a06 100644 --- a/quickjs.c +++ b/quickjs.c @@ -22709,9 +22709,17 @@ static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) && fd->is_strict_mode) { return js_parse_error(s, "invalid variable name in strict mode"); } - if ((name == JS_ATOM_let || name == JS_ATOM_undefined) - && (tok == TOK_LET || tok == TOK_CONST)) { - return js_parse_error(s, "invalid lexical variable name"); + if (tok == TOK_LET || tok == TOK_CONST) { + if (name == JS_ATOM_let) + return js_parse_error(s, "invalid lexical variable name 'let'"); + // |undefined| is allowed as an identifier except at the global + // scope of a classic script; sloppy or strict doesn't matter + if (name == JS_ATOM_undefined + && fd->scope_level == 1 + && fd->is_global_var + && !fd->module) { + return js_parse_error(s, "'undefined' already declared"); + } } switch(tok) { case TOK_LET: diff --git a/run-test262.c b/run-test262.c index 4620c77..f87cbf1 100644 --- a/run-test262.c +++ b/run-test262.c @@ -1783,6 +1783,7 @@ int run_test(const char *filename, int *msec) char *error_type; int ret, eval_flags, use_strict, use_nostrict; BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; + BOOL detect_module = TRUE; BOOL can_block; namelist_t include_list = { 0 }, *ip = &include_list; @@ -1838,6 +1839,9 @@ int run_test(const char *filename, int *msec) is_async = TRUE; skip |= skip_async; } + else if (str_equal(option, "qjs:no-detect-module")) { + detect_module = FALSE; + } else if (str_equal(option, "module")) { is_module = TRUE; skip |= skip_module; @@ -1920,7 +1924,7 @@ int run_test(const char *filename, int *msec) atomic_inc(&test_skipped); ret = -2; } else { - if (local) { + if (local && detect_module) { is_module = JS_DetectModule(buf, buf_len); } if (is_module) { diff --git a/tests/bug633/0.js b/tests/bug633/0.js new file mode 100644 index 0000000..155d3a4 --- /dev/null +++ b/tests/bug633/0.js @@ -0,0 +1,7 @@ +/*--- +flags: [qjs:no-detect-module] +negative: + phase: parse + type: SyntaxError +---*/ +const undefined = 42 // SyntaxError at global scope diff --git a/tests/bug633/1.js b/tests/bug633/1.js new file mode 100644 index 0000000..1aa83ba --- /dev/null +++ b/tests/bug633/1.js @@ -0,0 +1,4 @@ +/*--- +flags: [qjs:no-detect-module, module] +---*/ +const undefined = 42 // not a SyntaxError at toplevel module scope diff --git a/tests/bug633/2.js b/tests/bug633/2.js new file mode 100644 index 0000000..70a3301 --- /dev/null +++ b/tests/bug633/2.js @@ -0,0 +1,4 @@ +/*--- +flags: [qjs:no-detect-module] +---*/ +{ const undefined = 42 } // not a SyntaxError, not at global scope diff --git a/tests/bug633/3.js b/tests/bug633/3.js new file mode 100644 index 0000000..2bfa1ce --- /dev/null +++ b/tests/bug633/3.js @@ -0,0 +1,4 @@ +/*--- +flags: [qjs:no-detect-module] +---*/ +;(function() { const undefined = 42 })() // not a SyntaxError, not at global scope