From: Fabrice Bellard Date: Mon, 28 Sep 2026 14:56:47 +0000 (+0200) Subject: simplified and fixed yield/await identifier parsing X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=cfc9464f28a8b8e3d95617c2b71ac0da695ee1c6;p=quickjs.git simplified and fixed yield/await identifier parsing --- diff --git a/quickjs.c b/quickjs.c index d209b00..4d3803d 100644 --- a/quickjs.c +++ b/quickjs.c @@ -22890,25 +22890,37 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } +/* Walk up through arrow parameter contexts to find an ancestor with + the given func_kind flags (JS_FUNC_ASYNC or JS_FUNC_GENERATOR) */ +static BOOL func_has_ancestor_kind(JSFunctionDef *fd, int forced_func_kind, int kind_flags) +{ + if (forced_func_kind >= 0) + return (forced_func_kind & kind_flags) != 0; + for(;;) { + if (fd->func_kind & kind_flags) + return TRUE; + if (fd->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT && kind_flags == JS_FUNC_ASYNC) + return TRUE; + /* Arrow function parameters inherit binding from parent context */ + if (fd->func_type == JS_PARSE_FUNC_ARROW && !fd->in_function_body && fd->parent) + fd = fd->parent; + else + break; + } + return FALSE; +} + /* convert a TOK_IDENT to a keyword when needed */ -static void update_token_ident(JSParseState *s) +static void update_token_ident(JSParseState *s, int forced_func_kind) { if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && (s->cur_func->js_mode & JS_MODE_STRICT)) || (s->token.u.ident.atom == JS_ATOM_yield && - ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (func_has_ancestor_kind(s->cur_func, forced_func_kind, JS_FUNC_GENERATOR))) || (s->token.u.ident.atom == JS_ATOM_await && (s->is_module || - (s->cur_func->func_kind & JS_FUNC_ASYNC) || - s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || - s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + func_has_ancestor_kind(s->cur_func, forced_func_kind, JS_FUNC_ASYNC)))) { if (s->token.u.ident.has_escape) { s->token.u.ident.is_reserved = TRUE; s->token.val = TOK_IDENT; @@ -22921,14 +22933,14 @@ static void update_token_ident(JSParseState *s) /* if the current token is an identifier or keyword, reparse it according to the current function type */ -static void reparse_ident_token(JSParseState *s) +static void reparse_ident_token(JSParseState *s, int forced_func_kind) { if (s->token.val == TOK_IDENT || (s->token.val >= TOK_FIRST_KEYWORD && s->token.val <= TOK_LAST_KEYWORD)) { s->token.val = TOK_IDENT; s->token.u.ident.is_reserved = FALSE; - update_token_ident(s); + update_token_ident(s, forced_func_kind); } } @@ -23132,7 +23144,7 @@ static __exception int next_token(JSParseState *s) s->token.u.ident.has_escape = ident_has_escape; s->token.u.ident.is_reserved = FALSE; s->token.val = TOK_IDENT; - update_token_ident(s); + update_token_ident(s, -1); break; case '#': /* private name */ @@ -36689,22 +36701,15 @@ static __exception int js_parse_function_decl2(JSParseState *s, func_kind |= JS_FUNC_GENERATOR; } + if (func_type == JS_PARSE_FUNC_EXPR) { + /* in function expressions, the function name must be + parsed in the function context */ + reparse_ident_token(s, func_kind); + } + if (s->token.val == TOK_IDENT) { - if (s->token.u.ident.is_reserved || - (s->token.u.ident.atom == JS_ATOM_yield && - func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_GENERATOR)) || - (s->token.u.ident.atom == JS_ATOM_await && - ((func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_ASYNC)) || - func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { + if (s->token.u.ident.is_reserved) return js_parse_error_reserved_identifier(s); - } - } - if (s->token.val == TOK_IDENT || - (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) || - (s->token.val == TOK_AWAIT && !s->is_module)) && - func_type == JS_PARSE_FUNC_EXPR)) { func_name = JS_DupAtom(ctx, s->token.u.ident.atom); if (next_token(s)) { JS_FreeAtom(ctx, func_name); @@ -36833,6 +36838,12 @@ static __exception int js_parse_function_decl2(JSParseState *s, fd->has_simple_parameter_list = TRUE; fd->has_parameter_expressions = FALSE; has_opt_arg = FALSE; + + if (func_type == JS_PARSE_FUNC_ARROW) { + /* reparse the potential arrow first parameter in the function context */ + reparse_ident_token(s, -1); + } + if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { JSAtom name; if (s->token.u.ident.is_reserved) { @@ -36901,10 +36912,6 @@ static __exception int js_parse_function_decl2(JSParseState *s, goto fail; } name = s->token.u.ident.atom; - if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { - js_parse_error_reserved_identifier(s); - goto fail; - } if (fd->has_parameter_expressions) { if (js_parse_check_duplicate_parameter(s, name)) goto fail; @@ -37109,7 +37116,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, the token is parsed in the englobing function. It could be done by just using next_token() here for normal functions, but it is necessary for arrow functions with an expression body. */ - reparse_ident_token(s); + reparse_ident_token(s, -1); /* create the function object */ { diff --git a/test262_errors.txt b/test262_errors.txt index be91001..afeec03 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -32,12 +32,9 @@ test262/test/staging/sm/Function/function-bind.js:24: Test262Error: Conforms to test262/test/staging/sm/Function/function-name-for.js:13: Test262Error: Expected SameValue(«""», «"forInHead"») to be true test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:12: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true test262/test/staging/sm/Function/invalid-parameter-list.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all -test262/test/staging/sm/async-functions/await-in-arrow-parameters.js:10: Test262Error: AsyncFunction:(a = (b = await/r/g) => {}) => {} Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/staging/sm/class/boundFunctionSubclassing.js:9: Test262Error: Expected SameValue(«false», «true») to be true test262/test/staging/sm/class/strictExecution.js:13: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/sm/class/superPropOrdering.js:17: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all -test262/test/staging/sm/generators/syntax.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-arguments.js:13: Test262Error: Expected SameValue(«"object"», «"function"») to be true test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-eval.js:11: Test262Error: Expected SameValue(«"outer-gouter-geval-gtruefalseq"», «"outer-geval-gwith-gtruefalseq"») to be true test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-if.js:19: TypeError: not a function