fixed next token parsing after a function definition (github issue #77)

This commit is contained in:
Fabrice Bellard 2024-01-08 18:40:35 +01:00
parent 5935a26eae
commit c06c399f4f
2 changed files with 74 additions and 27 deletions

View File

@ -20485,6 +20485,48 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
return 0; return 0;
} }
/* convert a TOK_IDENT to a keyword when needed */
static void update_token_ident(JSParseState *s)
{
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)))) ||
(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))))) {
if (s->token.u.ident.has_escape) {
s->token.u.ident.is_reserved = TRUE;
s->token.val = TOK_IDENT;
} else {
/* The keywords atoms are pre allocated */
s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
}
}
}
/* if the current token is an identifier or keyword, reparse it
according to the current function type */
static void reparse_ident_token(JSParseState *s)
{
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);
}
}
/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
BOOL *pident_has_escape, int c, BOOL is_private) BOOL *pident_has_escape, int c, BOOL is_private)
@ -20691,32 +20733,8 @@ static __exception int next_token(JSParseState *s)
s->token.u.ident.atom = atom; s->token.u.ident.atom = atom;
s->token.u.ident.has_escape = ident_has_escape; s->token.u.ident.has_escape = ident_has_escape;
s->token.u.ident.is_reserved = FALSE; s->token.u.ident.is_reserved = FALSE;
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)))) ||
(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))))) {
if (ident_has_escape) {
s->token.u.ident.is_reserved = TRUE;
s->token.val = TOK_IDENT; s->token.val = TOK_IDENT;
} else { update_token_ident(s);
/* The keywords atoms are pre allocated */
s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
}
} else {
s->token.val = TOK_IDENT;
}
break; break;
case '#': case '#':
/* private name */ /* private name */
@ -33893,9 +33911,15 @@ static __exception int js_parse_function_decl2(JSParseState *s,
if (js_is_live_code(s)) { if (js_is_live_code(s)) {
emit_return(s, FALSE); emit_return(s, FALSE);
} }
done: done:
s->cur_func = fd->parent; s->cur_func = fd->parent;
/* Reparse identifiers after the function is terminated so that
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);
/* create the function object */ /* create the function object */
{ {
int idx; int idx;

View File

@ -536,6 +536,28 @@ function test_function_expr_name()
assert_throws(TypeError, f); assert_throws(TypeError, f);
} }
function test_parse_semicolon()
{
/* 'yield' or 'await' may not be considered as a token if the
previous ';' is missing */
function *f()
{
function func() {
}
yield 1;
var h = x => x + 1
yield 2;
}
async function g()
{
function func() {
}
await 1;
var h = x => x + 1
await 2;
}
}
test_op1(); test_op1();
test_cvt(); test_cvt();
test_eq(); test_eq();
@ -555,3 +577,4 @@ test_spread();
test_function_length(); test_function_length();
test_argument_scope(); test_argument_scope();
test_function_expr_name(); test_function_expr_name();
test_parse_semicolon();