From 0de570988a0decf7676ebcc831817fc844746bac Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sat, 30 Mar 2024 17:15:25 +0100 Subject: [PATCH] Fix strict name conformity cases (#335) - reject *future strict reserved words* in `js_parse_function_check_names()`. - add tests for reserved names in tests/test_language.js - allow running tests/test_language.js with v8 - update v8.txt --- quickjs.c | 24 +++++++++++++-- tests/test_language.js | 70 ++++++++++++++++++++++++++++++++++++++---- v8.txt | 54 -------------------------------- 3 files changed, 85 insertions(+), 63 deletions(-) diff --git a/quickjs.c b/quickjs.c index cf49ffa..8698067 100644 --- a/quickjs.c +++ b/quickjs.c @@ -31466,6 +31466,25 @@ static __exception int js_parse_directives(JSParseState *s) return js_parse_seek_token(s, &pos); } +static BOOL js_invalid_strict_name(JSAtom name) { + switch (name) { + case JS_ATOM_eval: + case JS_ATOM_arguments: + case JS_ATOM_implements: // future strict reserved words + case JS_ATOM_interface: + case JS_ATOM_let: + case JS_ATOM_package: + case JS_ATOM_private: + case JS_ATOM_protected: + case JS_ATOM_public: + case JS_ATOM_static: + case JS_ATOM_yield: + return TRUE; + default: + return FALSE; + } +} + static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, JSAtom func_name) { @@ -31476,13 +31495,12 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, if (!fd->has_simple_parameter_list && fd->has_use_strict) { return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); } - if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) { + if (js_invalid_strict_name(func_name)) { return js_parse_error(s, "invalid function name in strict code"); } for (idx = 0; idx < fd->arg_count; idx++) { name = fd->args[idx].var_name; - - if (name == JS_ATOM_eval || name == JS_ATOM_arguments) { + if (js_invalid_strict_name(name)) { return js_parse_error(s, "invalid argument name in strict code"); } } diff --git a/tests/test_language.js b/tests/test_language.js index b6ce4e4..a1cc1bc 100644 --- a/tests/test_language.js +++ b/tests/test_language.js @@ -10,24 +10,25 @@ function assert(actual, expected, message) { && actual.toString() === expected.toString()) return; + var msg = message ? " (" + message + ")" : ""; throw Error("assertion failed: got |" + actual + "|" + - ", expected |" + expected + "|" + - (message ? " (" + message + ")" : "")); + ", expected |" + expected + "|" + msg); } -function assert_throws(expected_error, func) +function assert_throws(expected_error, func, message) { var err = false; + var msg = message ? " (" + message + ")" : ""; try { func(); } catch(e) { err = true; if (!(e instanceof expected_error)) { - throw Error("unexpected exception type"); + throw Error(`expected ${expected_error.name}, got ${e.name}${msg}`); } } if (!err) { - throw Error("expected exception"); + throw Error(`expected ${expected_error.name}${msg}`); } } @@ -421,7 +422,9 @@ function test_argument_scope() var c = "global"; f = function(a = eval("var arguments")) {}; - assert_throws(SyntaxError, f); + // for some reason v8 does not throw an exception here + if (typeof require === 'undefined') + assert_throws(SyntaxError, f); f = function(a = eval("1"), b = arguments[0]) { return b; }; assert(f(12), 12); @@ -536,6 +539,60 @@ function test_function_expr_name() assert_throws(TypeError, f); } +function test_expr(expr, err) { + if (err) + assert_throws(err, () => eval(expr), `for ${expr}`); + else + assert(1, eval(expr), `for ${expr}`); +} + +function test_name(name, err) +{ + test_expr(`(function() { return typeof ${name} ? 1 : 1; })()`); + test_expr(`(function() { var ${name}; ${name} = 1; return ${name}; })()`); + test_expr(`(function() { let ${name}; ${name} = 1; return ${name}; })()`, name == 'let' ? SyntaxError : undefined); + test_expr(`(function() { const ${name} = 1; return ${name}; })()`, name == 'let' ? SyntaxError : undefined); + test_expr(`(function(${name}) { ${name} = 1; return ${name}; })()`); + test_expr(`(function({${name}}) { ${name} = 1; return ${name}; })({})`); + test_expr(`(function ${name}() { return ${name} ? 1 : 0; })()`); + test_expr(`"use strict"; (function() { return typeof ${name} ? 1 : 1; })()`, err); + test_expr(`"use strict"; (function() { if (0) ${name} = 1; return 1; })()`, err); + test_expr(`"use strict"; (function() { var x; if (0) x = ${name}; return 1; })()`, err); + test_expr(`"use strict"; (function() { var ${name}; return 1; })()`, err); + test_expr(`"use strict"; (function() { let ${name}; return 1; })()`, err); + test_expr(`"use strict"; (function() { const ${name} = 1; return 1; })()`, err); + test_expr(`"use strict"; (function() { var ${name}; ${name} = 1; return 1; })()`, err); + test_expr(`"use strict"; (function() { var ${name}; ${name} = 1; return ${name}; })()`, err); + test_expr(`"use strict"; (function(${name}) { return 1; })()`, err); + test_expr(`"use strict"; (function({${name}}) { return 1; })({})`, err); + test_expr(`"use strict"; (function ${name}() { return 1; })()`, err); + test_expr(`(function() { "use strict"; return typeof ${name} ? 1 : 1; })()`, err); + test_expr(`(function() { "use strict"; if (0) ${name} = 1; return 1; })()`, err); + test_expr(`(function() { "use strict"; var x; if (0) x = ${name}; return 1; })()`, err); + test_expr(`(function() { "use strict"; var ${name}; return 1; })()`, err); + test_expr(`(function() { "use strict"; let ${name}; return 1; })()`, err); + test_expr(`(function() { "use strict"; const ${name} = 1; return 1; })()`, err); + test_expr(`(function() { "use strict"; var ${name}; ${name} = 1; return 1; })()`, err); + test_expr(`(function() { "use strict"; var ${name}; ${name} = 1; return ${name}; })()`, err); + test_expr(`(function(${name}) { "use strict"; return 1; })()`, err); + test_expr(`(function({${name}}) { "use strict"; return 1; })({})`, SyntaxError); + test_expr(`(function ${name}() { "use strict"; return 1; })()`, err); +} + +function test_reserved_names() +{ + test_name('await'); + test_name('yield', SyntaxError); + test_name('implements', SyntaxError); + test_name('interface', SyntaxError); + test_name('let', SyntaxError); + test_name('package', SyntaxError); + test_name('private', SyntaxError); + test_name('protected', SyntaxError); + test_name('public', SyntaxError); + test_name('static', SyntaxError); +} + test_op1(); test_cvt(); test_eq(); @@ -555,3 +612,4 @@ test_spread(); test_function_length(); test_argument_scope(); test_function_expr_name(); +test_reserved_names(); diff --git a/v8.txt b/v8.txt index 9a7c801..2f98e8d 100644 --- a/v8.txt +++ b/v8.txt @@ -711,60 +711,6 @@ Did not throw exception Did not throw exception Did not throw exception Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception -Did not throw exception Failure: expected found Failure: expected found