From 5d8c5f3e92a64d52d43d534ec24fb8775273dfdf Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Wed, 24 Jun 2020 10:41:03 +0200 Subject: [PATCH] Support comma after last argument for function declarations (#3910) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/parser/js/js-parser.c | 33 ++++++---- jerry-core/parser/js/js-scanner-ops.c | 89 +++++++++++++++------------ jerry-core/parser/js/js-scanner.c | 40 +++++++----- tests/jerry/es.next/arrow-function.js | 2 - tests/jerry/es.next/function-decl.js | 48 +++++++++++++++ 5 files changed, 146 insertions(+), 66 deletions(-) create mode 100644 tests/jerry/es.next/function-decl.js diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 65bcdafbb..e3533ac42 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1802,11 +1802,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ while (true) { #if ENABLED (JERRY_ESNEXT) - if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) - { - parser_raise_error (context_p, PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER); - } - else if (context_p->token.type == LEXER_THREE_DOTS) + if (context_p->token.type == LEXER_THREE_DOTS) { if (context_p->status_flags & PARSER_IS_PROPERTY_SETTER) { @@ -1974,18 +1970,31 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ if (context_p->token.type != LEXER_COMMA) { + if (context_p->token.type != end_type) + { + parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED + : PARSER_ERR_IDENTIFIER_EXPECTED); + + parser_raise_error (context_p, error); + } break; } +#if ENABLED (JERRY_ESNEXT) + if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) + { + parser_raise_error (context_p, PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + lexer_next_token (context_p); - } - if (context_p->token.type != end_type) - { - parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED - : PARSER_ERR_IDENTIFIER_EXPECTED); - - parser_raise_error (context_p, error); +#if ENABLED (JERRY_ESNEXT) + if (context_p->token.type == end_type) + { + break; + } +#endif /* ENABLED (JERRY_ESNEXT) */ } scanner_revert_active (context_p); diff --git a/jerry-core/parser/js/js-scanner-ops.c b/jerry-core/parser/js/js-scanner-ops.c index 6fa8bd171..685c78074 100644 --- a/jerry-core/parser/js/js-scanner-ops.c +++ b/jerry-core/parser/js/js-scanner-ops.c @@ -182,65 +182,78 @@ scanner_check_arrow_arg (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); } - if (context_p->token.type == LEXER_LITERAL - && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + switch (context_p->token.type) { - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - - if (lexer_check_arrow (context_p)) + case LEXER_RIGHT_PAREN: { - process_arrow = true; + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return; } - else + case LEXER_LITERAL: { + if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + break; + } + + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + + if (lexer_check_arrow (context_p)) + { + process_arrow = true; + break; + } + lexer_lit_location_t *argument_literal_p = scanner_append_argument (context_p, scanner_context_p); scanner_detect_eval_call (context_p, scanner_context_p); lexer_next_token (context_p); - if (context_p->token.type == LEXER_ASSIGN) - { - if (argument_literal_p->type & SCANNER_LITERAL_IS_USED) - { - JERRY_ASSERT (argument_literal_p->type & SCANNER_LITERAL_EARLY_CREATE); - return; - } - - scanner_binding_literal_t binding_literal; - binding_literal.literal_p = argument_literal_p; - - parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); - parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); - return; - } - if (context_p->token.type == LEXER_COMMA || context_p->token.type == LEXER_RIGHT_PAREN) { return; } - } - } - else if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE) - { - scanner_append_hole (context_p, scanner_context_p); - scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_ARROW_ARG, false); - if (context_p->token.type == LEXER_LEFT_BRACE) - { - parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); - scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; + if (context_p->token.type != LEXER_ASSIGN) + { + break; + } + + if (argument_literal_p->type & SCANNER_LITERAL_IS_USED) + { + JERRY_ASSERT (argument_literal_p->type & SCANNER_LITERAL_EARLY_CREATE); + return; + } + + scanner_binding_literal_t binding_literal; + binding_literal.literal_p = argument_literal_p; + + parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); + parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); return; } + case LEXER_LEFT_SQUARE: + case LEXER_LEFT_BRACE: + { + scanner_append_hole (context_p, scanner_context_p); + scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_ARROW_ARG, false); - parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); - scanner_context_p->mode = SCAN_MODE_BINDING; - lexer_next_token (context_p); - return; + if (context_p->token.type == LEXER_LEFT_BRACE) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); + scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; + return; + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); + scanner_context_p->mode = SCAN_MODE_BINDING; + lexer_next_token (context_p); + return; + } } scanner_pop_literal_pool (context_p, scanner_context_p); - parser_stack_pop_uint8 (context_p); if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC) diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 3197a0376..50c0d8ba6 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -2738,20 +2738,15 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } case SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS: { -#endif /* ENABLED (JERRY_ESNEXT) */ if (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS) { -#if ENABLED (JERRY_ESNEXT) lexer_lit_location_t *argument_literal_p; -#endif /* ENABLED (JERRY_ESNEXT) */ - while (true) + do { -#if ENABLED (JERRY_ESNEXT) if (context_p->token.type == LEXER_THREE_DOTS) { scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED; - lexer_next_token (context_p); } @@ -2760,7 +2755,6 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ argument_literal_p = NULL; break; } -#endif /* ENABLED (JERRY_ESNEXT) */ if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) @@ -2768,22 +2762,18 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } -#if ENABLED (JERRY_ESNEXT) argument_literal_p = scanner_append_argument (context_p, &scanner_context); -#else /* !ENABLED (JERRY_ESNEXT) */ - scanner_append_argument (context_p, &scanner_context); -#endif /* ENABLED (JERRY_ESNEXT) */ - lexer_next_token (context_p); if (context_p->token.type != LEXER_COMMA) { break; } + lexer_next_token (context_p); } + while (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS); -#if ENABLED (JERRY_ESNEXT) if (argument_literal_p == NULL) { scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED; @@ -2824,8 +2814,30 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); break; } -#endif /* ENABLED (JERRY_ESNEXT) */ } +#else /* !ENABLED (JERRY_ESNEXT) */ + if (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS) + { + while (true) + { + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + scanner_raise_error (context_p); + } + + scanner_append_argument (context_p, &scanner_context); + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + + lexer_next_token (context_p); + } + } +#endif /* ENABLED (JERRY_ESNEXT) */ if (context_p->token.type == LEXER_EOS && stack_top == SCAN_STACK_SCRIPT_FUNCTION) { diff --git a/tests/jerry/es.next/arrow-function.js b/tests/jerry/es.next/arrow-function.js index 0d11ec439..2092955b1 100644 --- a/tests/jerry/es.next/arrow-function.js +++ b/tests/jerry/es.next/arrow-function.js @@ -138,10 +138,8 @@ must_throw ("var x => x;"); must_throw ("(()) => 0"); must_throw ("((x)) => 0"); must_throw ("(((x))) => 0"); -must_throw ("(x,) => 0"); must_throw ("(x==6) => 0"); must_throw ("(x y) => 0"); -must_throw ("(x,y,) => 0"); must_throw ("x\n => 0"); must_throw ("this => 0"); must_throw ("(true) => 0"); diff --git a/tests/jerry/es.next/function-decl.js b/tests/jerry/es.next/function-decl.js new file mode 100644 index 000000000..c54ad373e --- /dev/null +++ b/tests/jerry/es.next/function-decl.js @@ -0,0 +1,48 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +function check_syntax_error (code) +{ + try { + eval (code) + assert (false) + } catch (e) { + assert (e instanceof SyntaxError) + } +} + +check_syntax_error ("function f(,) {}") +check_syntax_error ("function f(...a,) {}") +check_syntax_error ("function f(a = 1 + 1,,) {}") +check_syntax_error ("function f(a,,b) {}") +check_syntax_error ("function f(,a) {}") + +function f1(a,) {} +assert(f1.length === 1) + +function f2(a = 1,) {} +assert(f2.length === 1) + +function f3(a = 1, b = 1 + 1, c,) {} +assert(f3.length === 3) + +var f4 = async(a,) => {} +assert(f4.length === 1) + +var f5 = async(a = 1,) => {} +assert(f5.length === 1) + +assert(((a = 1, b = 1 + 1, c,) => {}).length === 3) + +assert(((a = 1, b, c = 1 + 1,) => {}).length === 3)