From 5562a2a67162e6d1f0b6fc189ef79609694a4291 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 29 Oct 2018 07:29:08 -0700 Subject: [PATCH] Add support for function argument initializers. EcmaScript 2015 14.1. Note: arrow functions with default arguments are not supported yet. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/config.h | 1 + jerry-core/parser/js/js-parser-scanner.c | 28 ++++++++ jerry-core/parser/js/js-parser-statm.c | 18 +---- jerry-core/parser/js/js-parser-util.c | 6 ++ jerry-core/parser/js/js-parser.c | 36 ++++++++++ jerry-core/parser/js/js-parser.h | 3 + jerry-core/profiles/README.md | 9 ++- tests/jerry/es2015/function-param-init.js | 81 +++++++++++++++++++++++ 8 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 tests/jerry/es2015/function-param-init.js diff --git a/jerry-core/config.h b/jerry-core/config.h index 013992468a..f74a31eccd 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -39,6 +39,7 @@ # define CONFIG_DISABLE_ES2015_ARROW_FUNCTION # define CONFIG_DISABLE_ES2015_BUILTIN # define CONFIG_DISABLE_ES2015_CLASS +# define CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER # define CONFIG_DISABLE_ES2015_MAP_BUILTIN # define CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER # define CONFIG_DISABLE_ES2015_PROMISE_BUILTIN diff --git a/jerry-core/parser/js/js-parser-scanner.c b/jerry-core/parser/js/js-parser-scanner.c index 9d9a70460c..3404b1ac18 100644 --- a/jerry-core/parser/js/js-parser-scanner.c +++ b/jerry-core/parser/js/js-parser-scanner.c @@ -76,6 +76,9 @@ typedef enum #ifndef CONFIG_DISABLE_ES2015_CLASS SCAN_STACK_CLASS, /**< class language element */ #endif /* !CONFIG_DISABLE_ES2015_CLASS */ +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + SCAN_STACK_FUNCTION_PARAMETERS, /**< function parameter initializer */ +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ } scan_stack_modes_t; /** @@ -406,6 +409,22 @@ parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ } #endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */ +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + if (context_p->token.type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_FUNCTION_PARAMETERS) + { + lexer_next_token (context_p); + + parser_stack_pop_uint8 (context_p); + + if (context_p->token.type != LEXER_LEFT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); + } + *mode = SCAN_MODE_STATEMENT; + return false; + } +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ + /* Check whether we can enter to statement mode. */ if (stack_top != SCAN_STACK_BLOCK_STATEMENT && stack_top != SCAN_STACK_BLOCK_EXPRESSION @@ -842,6 +861,15 @@ parser_scan_until (parser_context_t *context_p, /**< context */ } } +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + if (context_p->token.type == LEXER_ASSIGN) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PARAMETERS); + mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ + if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 52feef3467..237ae3bbec 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -326,8 +326,6 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; - parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); - lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) @@ -336,19 +334,11 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) && ident_line_counter != context_p->last_breakpoint_line) { - JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); - - cbc_argument_t last_cbc = context_p->last_cbc; - context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; - parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED); parser_flush_cbc (context_p); parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, ident_line_counter); - context_p->last_cbc_opcode = CBC_PUSH_LITERAL; - context_p->last_cbc = last_cbc; - context_p->last_breakpoint_line = ident_line_counter; } #endif /* JERRY_DEBUGGER */ @@ -360,16 +350,10 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ } #endif /* JERRY_ENABLE_LINE_INFO */ + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); parser_parse_expression (context_p, PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); } - else - { - JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL - && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); - /* We don't need to assign anything to this variable. */ - context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; - } if (context_p->token.type != LEXER_COMMA) { diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 15939edaf3..b91b913e3c 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1034,6 +1034,12 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Duplicated label."; } +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + case PARSER_ERR_DUPLICATED_ARGUMENT_NAMES: + { + return "Duplicated function argument names are not allowed here."; + } +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ case PARSER_ERR_OBJECT_PROPERTY_REDEFINED: { return "Property of object literal redefined."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 9fdde53d82..0c56206877 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2155,6 +2155,11 @@ static void parser_parse_function_arguments (parser_context_t *context_p, /**< context */ lexer_token_type_t end_type) /**< expected end type */ { +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + bool duplicated_argument_names = false; + bool initializer_found = false; +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ + if (context_p->token.type == end_type) { return; @@ -2196,6 +2201,14 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ { lexer_literal_t *literal_p; +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + if (initializer_found) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); + } + duplicated_argument_names = true; +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) { parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); @@ -2232,6 +2245,29 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + if (context_p->token.type == LEXER_ASSIGN) + { + parser_branch_t skip_init; + + if (duplicated_argument_names) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); + } + initializer_found = true; + + /* LEXER_ASSIGN does not overwrite lit_object. */ + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + parser_emit_cbc_literal (context_p, CBC_STRICT_EQUAL_RIGHT_LITERAL, context_p->lit_object.index); + parser_emit_cbc_forward_branch (context_p, CBC_BRANCH_IF_FALSE_FORWARD, &skip_init); + + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + parser_parse_expression (context_p, PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); + + parser_set_branch_to_current_position (context_p, &skip_init); + } +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ + if (context_p->token.type != LEXER_COMMA) { break; diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index bc1bd56e59..e5c0ea4835 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -118,6 +118,9 @@ typedef enum PARSER_ERR_INVALID_RETURN, /**< return must be inside a function */ PARSER_ERR_INVALID_RIGHT_SQUARE, /**< right square must terminate a block */ PARSER_ERR_DUPLICATED_LABEL, /**< duplicated label */ +#ifndef CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER + PARSER_ERR_DUPLICATED_ARGUMENT_NAMES, /**< duplicated argument names */ +#endif /* !CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER */ PARSER_ERR_OBJECT_PROPERTY_REDEFINED, /**< property of object literal redefined */ PARSER_ERR_NON_STRICT_ARG_DEFINITION /**< non-strict argument definition */ } parser_error_t; diff --git a/jerry-core/profiles/README.md b/jerry-core/profiles/README.md index f6723f0c85..2904206db4 100644 --- a/jerry-core/profiles/README.md +++ b/jerry-core/profiles/README.md @@ -32,6 +32,9 @@ Alternatively, if you want to use a custom profile at # Turn off every ES2015 feature EXCEPT the arrow functions CONFIG_DISABLE_ES2015_BUILTIN CONFIG_DISABLE_ES2015_CLASS +CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER +CONFIG_DISABLE_ES2015_MAP_BUILTIN +CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER CONFIG_DISABLE_ES2015_PROMISE_BUILTIN CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN @@ -87,6 +90,8 @@ In JerryScript all of the features are enabled by default, so an empty profile f Disable the built-in updates of the 5.1 standard. There are some differences in those built-ins which available in both [5.1](http://www.ecma-international.org/ecma-262/5.1/) and [2015](http://www.ecma-international.org/ecma-262/6.0/) versions of the standard. JerryScript uses the latest definition by default. * `CONFIG_DISABLE_ES2015_CLASS`: Disable the [class](https://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions) language element. +* `CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER`: + Disable the [default value](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions) for formal parameters. * `CONFIG_DISABLE_ES2015_MAP_BUILTIN`: Disable the [Map](http://www.ecma-international.org/ecma-262/6.0/#sec-keyed-collection) built-ins. * `CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER`: @@ -98,4 +103,6 @@ In JerryScript all of the features are enabled by default, so an empty profile f * `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`: Disable the [ArrayBuffer](http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects) and [TypedArray](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects) built-ins. * `CONFIG_DISABLE_ES2015`: Disable all of the implemented [ECMAScript2015 features](http://www.ecma-international.org/ecma-262/6.0/). - (equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, and `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`). + (equivalent to `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, + `CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER`, `CONFIG_DISABLE_ES2015_MAP_BUILTIN`, `CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER`, + `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, and `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`). diff --git a/tests/jerry/es2015/function-param-init.js b/tests/jerry/es2015/function-param-init.js new file mode 100644 index 0000000000..37d788654a --- /dev/null +++ b/tests/jerry/es2015/function-param-init.js @@ -0,0 +1,81 @@ +// 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. + +var local = 0; + +switch(0) { /* This switch forces a pre-scanner run. */ +default: + + function f(a = (5, local = 6), + b = ((5 + function(a = 6) { return a }() * 3)), + c, + d = true ? 1 : 2) + { + return "" + a + ", " + b + ", " + c + ", " + d; + } + + assert(f() === "6, 23, undefined, 1"); + assert(local === 6); + + var obj = { + f: function(a = [10,,20], + b, + c = Math.cos(0), + d) + { + return "" + a + ", " + b + ", " + c + ", " + d; + } + }; + + assert(obj.f() === "10,,20, undefined, 1, undefined"); + + function g(a, b = (local = 7)) { } + + local = 0; + g(); + assert(local === 7); + + local = 0; + g(0); + assert(local === 7); + + local = 0; + g(0, undefined); + assert(local === 7); + + local = 0; + g(0, null); + assert(local === 0); + + local = 0; + g(0, false); + assert(local === 0); + break; +} + +function CheckSyntaxError(str) +{ + try { + eval(str); + assert(false); + } catch (e) { + assert(e instanceof SyntaxError); + } +} + +CheckSyntaxError('function x(a += 5) {}'); +CheckSyntaxError('function x(a =, b) {}'); +CheckSyntaxError('function x(a = (b) {}'); +CheckSyntaxError('function x(a, a = 5) {}'); +CheckSyntaxError('function x(a = 5, a) {}');