From 6b43ef86057700e5907fa373a857dc135928e62d Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 21 Nov 2019 11:32:33 +0100 Subject: [PATCH] Arrow functions should be parsed as assignment expressions. (#3336) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/parser/js/js-parser-expr.c | 48 ++++++++++++++++--- jerry-core/parser/js/js-parser-util.c | 4 ++ jerry-core/parser/js/js-parser.h | 1 + tests/jerry/es2015/arrow-function.js | 29 ++++++++++- .../es2015/regression-test-issue-2825.js | 4 +- 5 files changed, 75 insertions(+), 11 deletions(-) diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 97760911e..2be123705 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1177,12 +1177,39 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */ return; } /* parser_parse_template_literal */ +/** + * Throws an error if the current expression is not an assignment expression. + */ +static inline void JERRY_ATTR_ALWAYS_INLINE +parser_check_assignment_expr (parser_context_t *context_p) +{ + if (context_p->stack_top_uint8 != LEXER_EXPRESSION_START + && context_p->stack_top_uint8 != LEXER_LEFT_PAREN + && context_p->stack_top_uint8 != LEXER_COMMA_SEP_LIST + && !LEXER_IS_BINARY_LVALUE_TOKEN (context_p->stack_top_uint8)) + { + parser_raise_error (context_p, PARSER_ERR_ASSIGNMENT_EXPECTED); + } +} /* parser_check_assignment_expr */ + +/** + * Checks whether the next token is a valid continuation token after an arrow function. + */ +static inline bool JERRY_ATTR_ALWAYS_INLINE +parser_abort_parsing_after_arrow (parser_context_t *context_p) +{ + return (context_p->token.type != LEXER_RIGHT_PAREN + && context_p->token.type != LEXER_COMMA); +} /* parser_abort_parsing_after_arrow */ + #endif /* ENABLED (JERRY_ES2015) */ /** * Parse and record unary operators, and parse the primary literal. + * + * @return true if parsing should be aborted, true otherwise */ -static void +static bool parser_parse_unary_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { @@ -1255,9 +1282,10 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); + parser_check_assignment_expr (context_p); parser_parse_function_expression (context_p, PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION); - return; + return parser_abort_parsing_after_arrow (context_p); } #endif /* ENABLED (JERRY_ES2015) */ @@ -1346,7 +1374,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); parser_parse_object_initializer (context_p, PARSER_PATTERN_NO_OPTS); - return; + return false; } #endif /* ENABLED (JERRY_ES2015) */ @@ -1360,7 +1388,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); parser_parse_array_initializer (context_p, PARSER_PATTERN_NO_OPTS); - return; + return false; } #endif /* ENABLED (JERRY_ES2015) */ @@ -1435,7 +1463,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ case LEXER_KEYW_CLASS: { parser_parse_class (context_p, false); - return; + return false; } case LEXER_KEYW_SUPER: { @@ -1479,10 +1507,11 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); + parser_check_assignment_expr (context_p); lexer_next_token (context_p); parser_parse_function_expression (context_p, PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_ARROW_PARSE_ARGS); - return; + return parser_abort_parsing_after_arrow (context_p); } #endif /* ENABLED (JERRY_ES2015) */ default: @@ -1494,6 +1523,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } } lexer_next_token (context_p); + return false; } /* parser_parse_unary_expression */ /** @@ -2730,7 +2760,11 @@ parser_parse_expression (parser_context_t *context_p, /**< context */ while (true) { - parser_parse_unary_expression (context_p, &grouping_level); + if (parser_parse_unary_expression (context_p, &grouping_level)) + { + parser_process_binary_opcodes (context_p, 0); + break; + } while (true) { diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 83cafa335..7f7536c23 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1121,6 +1121,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Expected 'of' token."; } + case PARSER_ERR_ASSIGNMENT_EXPECTED: + { + return "Unexpected arrow function or yield expression (parentheses around the expression may help)."; + } case PARSER_ERR_DUPLICATED_ARGUMENT_NAMES: { return "Duplicated function argument names are not allowed here."; diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index de8d4c372..fb4dab118 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -126,6 +126,7 @@ typedef enum PARSER_ERR_RIGHT_BRACE_EXPECTED, /**< right brace expected */ PARSER_ERR_OF_EXPECTED, /**< of keyword expected */ + PARSER_ERR_ASSIGNMENT_EXPECTED, /**< assignment expression expected */ PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER, /**< formal parameter after rest parameter */ PARSER_ERR_SETTER_REST_PARAMETER, /**< setter rest parameter */ PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER, /**< rest parameter default initializer */ diff --git a/tests/jerry/es2015/arrow-function.js b/tests/jerry/es2015/arrow-function.js index ebc3f4305..a2bdbd133 100644 --- a/tests/jerry/es2015/arrow-function.js +++ b/tests/jerry/es2015/arrow-function.js @@ -100,13 +100,13 @@ default: var global_var = 7; - assert ( + assert (( ( static , package ) => { global_var = 5; return static + package } - (4, 5) == 9); + )(4, 5) == 9); assert (global_var == 5); @@ -146,6 +146,13 @@ must_throw ("x\n => 0"); must_throw ("this => 0"); must_throw ("(true) => 0"); must_throw ("()\n=>5"); +must_throw ("3 + x => 3"); +must_throw ("3 || x => 3"); +must_throw ("a = 3 || (x,y) => 3"); +must_throw ("x => {} (4)"); +must_throw ("!x => 4"); +must_throw ("x => {} = 1"); +must_throw ("x => {} a = 1"); must_throw_strict ("(package) => 0"); must_throw_strict ("(package) => { return 5 }"); must_throw_strict ("(x,x,x) => 0"); @@ -174,3 +181,21 @@ assert(f()()() === 7); var f = (((a=1,b=2) => ((x => (((a) => 8)))))); assert(f()()() === 8); + +var s; +// This is not a function call +assert(eval("s = x => { return 1 }\n(3)") === 3); +assert(typeof s === "function") + +// This is a function call +assert(eval("s = function () { return 1 }\n(3)") === 1); +assert(s === 1) + +var f = 5 ? x => 1 : x => 2 +assert(f() === 1) + +var f = [x => 2][0] +assert(f() === 2) + +var f = 123; f += x => y; +assert(typeof f === "string"); diff --git a/tests/jerry/es2015/regression-test-issue-2825.js b/tests/jerry/es2015/regression-test-issue-2825.js index 9b9ff6df2..31530f0e6 100644 --- a/tests/jerry/es2015/regression-test-issue-2825.js +++ b/tests/jerry/es2015/regression-test-issue-2825.js @@ -25,9 +25,9 @@ var B = class extends f { } C = class extends B { g() { - () => { + (() => { called = true; - }() + })() } } D = class extends C {