diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 443360ce0..e8d456807 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (28u) +#define JERRY_SNAPSHOT_VERSION (29u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index e38294662..853dbb248 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -536,6 +536,10 @@ VM_OC_CLASS_EXPR_CONTEXT_END) \ CBC_FORWARD_BRANCH (CBC_EXT_SUPER_CLASS_CREATE_CONTEXT, \ -1 + PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION, VM_OC_CLASS_HERITAGE) \ + CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_PROP, CBC_NO_FLAG, 0, \ + VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_STACK) \ + CBC_FORWARD_BRANCH (CBC_EXT_DEFAULT_INITIALIZER, 0, \ + VM_OC_DEFAULT_INITIALIZER) \ \ /* Basic opcodes. */ \ CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_0, CBC_HAS_LITERAL_ARG, 2, \ @@ -616,6 +620,18 @@ VM_OC_CREATE_SPREAD_OBJECT | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ VM_OC_APPEND_ARRAY) \ + CBC_OPCODE (CBC_EXT_GET_ITERATOR, CBC_NO_FLAG, 1, \ + VM_OC_GET_ITERATOR) \ + CBC_OPCODE (CBC_EXT_ITERATOR_STEP, CBC_NO_FLAG, 1, \ + VM_OC_ITERATOR_STEP) \ + CBC_OPCODE (CBC_EXT_ITERATOR_STEP_PROP, CBC_NO_FLAG, 1, \ + VM_OC_ITERATOR_STEP) \ + CBC_OPCODE (CBC_EXT_REST_INITIALIZER, CBC_NO_FLAG, 1, \ + VM_OC_REST_INITIALIZER) \ + CBC_OPCODE (CBC_EXT_REST_INITIALIZER_PROP, CBC_NO_FLAG, 1, \ + VM_OC_REST_INITIALIZER) \ + CBC_OPCODE (CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ + VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_LITERAL) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index df4b810dd..ba979d5c3 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -2369,7 +2369,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ { lexer_parse_identifier (context_p, false); - if (!(ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS) + if (!(ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN)) && context_p->token.lit_location.length == 3) { lexer_skip_spaces (context_p); diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 017a76e4a..267b6c511 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -151,6 +151,7 @@ typedef enum LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ + LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */ #if !ENABLED (JERRY_ES2015) /* Future reserved words: these keywords @@ -238,6 +239,7 @@ typedef enum LEXER_OBJ_IDENT_NO_OPTS = (1u << 0), /**< no options */ LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 1), /**< only identifiers are accepted */ LEXER_OBJ_IDENT_CLASS_METHOD = (1u << 2), /**< expect identifier inside a class body */ + LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ } lexer_obj_ident_opts_t; /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 21bf7395c..e3c677bd1 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -382,6 +382,18 @@ parser_append_object_literal_item (parser_context_t *context_p, /**< context */ #endif /* !ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015) +/** + * Definition of parse object initializer. + */ +static void +parser_parse_object_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); + +/** + * Definition of parse array initializer. + */ +static void +parser_parse_array_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); + /** * Description of "get" literal string. */ @@ -630,11 +642,8 @@ parser_parse_class (parser_context_t *context_p, /**< context */ class_ident_index = context_p->lit_object.index; #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) - { - context_p->module_identifier_lit_p = context_p->lit_object.literal_p; - context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); - } + parser_module_append_export_name (context_p); + context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ lexer_next_token (context_p); @@ -729,6 +738,32 @@ parser_parse_object_method (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); } /* parser_parse_object_method */ + +/** + * Reparse the current literal as a common identifier. + */ +static void +parser_reparse_as_common_identifier (parser_context_t *context_p, /**< context */ + parser_line_counter_t start_line, /**< start line */ + parser_line_counter_t start_column) /**< start column */ +{ + context_p->source_p = context_p->token.lit_location.char_p; + context_p->line = start_line; + context_p->column = start_column; + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_IDENT_LITERAL); + +} /* parser_reparse_as_common_identifier */ #endif /* ENABLED (JERRY_ES2015) */ /** @@ -880,23 +915,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ if (context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_COMMA) { - /* Re-parse the literal as common identifier. */ - context_p->source_p = context_p->token.lit_location.char_p; - context_p->line = start_line; - context_p->column = start_column; - - lexer_next_token (context_p); - - if (context_p->token.type != LEXER_LITERAL - || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) - { - parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); - } - - lexer_construct_literal_object (context_p, - &context_p->token.lit_location, - context_p->token.lit_location.type); - + parser_reparse_as_common_identifier (context_p, start_line, start_column); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; @@ -1327,11 +1346,29 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } case LEXER_LEFT_BRACE: { +#if ENABLED (JERRY_ES2015) + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); + parser_parse_object_initializer (context_p, PARSER_PATTERN_NO_OPTS); + return; + } +#endif /* ENABLED (JERRY_ES2015) */ + parser_parse_object_literal (context_p); break; } case LEXER_LEFT_SQUARE: { +#if ENABLED (JERRY_ES2015) + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); + parser_parse_array_initializer (context_p, PARSER_PATTERN_NO_OPTS); + return; + } +#endif /* ENABLED (JERRY_ES2015) */ + parser_parse_array_literal (context_p); break; } @@ -2020,6 +2057,440 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ } } /* parser_process_binary_opcodes */ +#if ENABLED (JERRY_ES2015) +/** + * End position marker of a pattern. + */ +typedef struct +{ + scanner_location_t location; /**< end position of the pattern */ + lexer_token_t token; /**< token at the end position */ +} parser_pattern_end_marker_t; + +/** + * Process the target of an initializer pattern. + */ +static parser_pattern_end_marker_t +parser_pattern_get_target (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags) /**< flags */ +{ + parser_pattern_end_marker_t end_marker; + end_marker.token.type = LEXER_INVALID_PATTERN; + parser_branch_t skip_init; + + if (flags & PARSER_PATTERN_TARGET_DEFAULT) + { + JERRY_ASSERT (flags & PARSER_PATTERN_TARGET_ON_STACK); + + parser_emit_cbc_forward_branch (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_DEFAULT_INITIALIZER), &skip_init); + parser_emit_cbc (context_p, CBC_POP); + } + + if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) + { + scanner_location_t start_location; + + if (context_p->next_scanner_info_p->source_p != context_p->source_p + || context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED) + { + /* Found invalid pattern, push null value to fake the rhs target. */ + parser_emit_cbc (context_p, CBC_PUSH_NULL); + } + else + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); + scanner_get_location (&start_location, context_p); + + scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); + scanner_release_next (context_p, sizeof (scanner_location_info_t)); + scanner_seek (context_p); + lexer_next_token (context_p); + + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + scanner_get_location (&(end_marker.location), context_p); + end_marker.token = context_p->token; + + scanner_set_location (context_p, &start_location); + scanner_seek (context_p); + parser_flush_cbc (context_p); + } + } + + if (flags & PARSER_PATTERN_TARGET_DEFAULT) + { + parser_set_branch_to_current_position (context_p, &skip_init); + } + + return end_marker; +} /* parser_pattern_get_target */ + +/** + * Finalize an assignment/binding pattern. + */ +static void +parser_pattern_finalize (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags, /**< flags */ + parser_pattern_end_marker_t *end_marker_p) /**< pattern end position */ +{ + if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) + { + if (end_marker_p->token.type == LEXER_INVALID_PATTERN) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); + } + + scanner_set_location (context_p, &(end_marker_p->location)); + context_p->token = end_marker_p->token; + } + + if ((flags & PARSER_PATTERN_BINDING) + && !(flags & PARSER_PATTERN_INNER_PATTERN)) + { + /* Pop the result of the expression. */ + parser_emit_cbc (context_p, CBC_POP); + } + + parser_flush_cbc (context_p); +} /* parser_pattern_finalize */ + +/** + * Emit right-hand-side target value. + */ +static void +parser_pattern_emit_rhs (parser_context_t *context_p, /**< context */ + uint16_t rhs_opcode, /**< opcode to process the rhs value */ + uint16_t literal_index) /**< literal index for object pattern */ +{ + if (literal_index != UINT16_MAX) + { + parser_emit_cbc_ext_literal (context_p, rhs_opcode, literal_index); + } + else + { + parser_emit_cbc_ext (context_p, rhs_opcode); + } +} /* parser_pattern_emit_rhs */ + +/** + * Form an assignment from a pattern. + */ +static void +parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ + uint16_t rhs_opcode, /**< opcode to process the rhs value */ + uint16_t literal_index, /**< literal index for object pattern */ + parser_line_counter_t ident_line_counter) /**< identifier line counter */ +{ + JERRY_UNUSED (ident_line_counter); + + parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); + lexer_token_type_t current_token_type = context_p->token.type; + + context_p->token.type = LEXER_ASSIGN; + parser_append_binary_token (context_p); + + parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); + + if (current_token_type == LEXER_ASSIGN) + { + parser_branch_t skip_init; + lexer_next_token (context_p); + parser_emit_cbc_forward_branch (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_DEFAULT_INITIALIZER), &skip_init); + parser_emit_cbc (context_p, CBC_POP); + + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_LEFT_HAND_SIDE); + parser_set_branch_to_current_position (context_p, &skip_init); + } + else + { + context_p->token.type = current_token_type; + } + + parser_process_binary_opcodes (context_p, 0); + + JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); + parser_stack_pop_uint8 (context_p); + +#if ENABLED (JERRY_DEBUGGER) + if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + && ident_line_counter != context_p->last_breakpoint_line) + { + 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_breakpoint_line = ident_line_counter; + } +#endif /* ENABLED (JERRY_DEBUGGER) */ + +#if ENABLED (JERRY_LINE_INFO) + if (ident_line_counter != context_p->last_line_info_line) + { + parser_emit_line_info (context_p, ident_line_counter, false); + } +#endif /* ENABLED (JERRY_LINE_INFO) */ +} /* parser_pattern_form_assignment */ + +/** + * Parse pattern inside a pattern. + */ +static void +parser_pattern_process_inner_pattern (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags, /**< flags */ + uint16_t rhs_opcode, /**< opcode to process the rhs value */ + uint16_t literal_index) /**< literal index for object pattern */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE); + + parser_pattern_flags_t options = (PARSER_PATTERN_INNER_PATTERN + | PARSER_PATTERN_TARGET_ON_STACK + | (flags & PARSER_PATTERN_BINDING)); + + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + if (rhs_opcode == CBC_EXT_REST_INITIALIZER) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); + } + options |= PARSER_PATTERN_TARGET_DEFAULT; + } + + parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); + + if (context_p->token.type == LEXER_LEFT_BRACE) + { + parser_parse_object_initializer (context_p, options); + } + else + { + parser_parse_array_initializer (context_p, options); + } + + if (!(options & PARSER_PATTERN_TARGET_DEFAULT)) + { + lexer_next_token (context_p); + } + + parser_emit_cbc (context_p, CBC_POP); +} /* parser_pattern_process_inner_pattern */ + +/** + * Parse array initializer. + */ +static void +parser_parse_array_initializer (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags) /**< flags */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); + parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); + + lexer_next_token (context_p); + parser_emit_cbc_ext (context_p, CBC_EXT_GET_ITERATOR); + + while (context_p->token.type != LEXER_RIGHT_SQUARE) + { + uint16_t step_opcode = CBC_EXT_ITERATOR_STEP; + + if (context_p->token.type == LEXER_COMMA) + { + parser_emit_cbc_ext (context_p, step_opcode); + parser_emit_cbc (context_p, CBC_POP); + lexer_next_token (context_p); + continue; + } + + if (context_p->token.type == LEXER_THREE_DOTS) + { + lexer_next_token (context_p); + step_opcode = CBC_EXT_REST_INITIALIZER; + } + + if (context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE) + { + parser_pattern_process_inner_pattern (context_p, flags, step_opcode, UINT16_MAX); + + if (step_opcode == CBC_EXT_REST_INITIALIZER) + { + break; + } + } + else + { + parser_line_counter_t ident_line_counter = context_p->token.line; + + if (flags & PARSER_PATTERN_BINDING) + { + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); + parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); + } + +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + parser_module_append_export_name (context_p); +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + lexer_next_token (context_p); + } + else + { + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_LEFT_HAND_SIDE); + + if (!PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode)) + { + if (!PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode) + && context_p->last_cbc_opcode != CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); + } + PARSER_PLUS_EQUAL_U16 (step_opcode, 1); + } + } + + if (step_opcode == CBC_EXT_REST_INITIALIZER + && context_p->token.type != LEXER_RIGHT_SQUARE) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); + } + + parser_pattern_form_assignment (context_p, step_opcode, UINT16_MAX, ident_line_counter); + } + + if (context_p->token.type == LEXER_COMMA) + { + lexer_next_token (context_p); + } + else if (context_p->token.type != LEXER_RIGHT_SQUARE) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); + } + } + + /* pop the iterator */ + parser_emit_cbc (context_p, CBC_POP); + + parser_pattern_finalize (context_p, flags, &end_pos); +} /* parser_parse_array_initializer */ + +/** + * Parse object initializer. + */ +static void +parser_parse_object_initializer (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags) /**< flags */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); + parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); + + while (true) + { + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJECT_PATTERN); + + uint16_t prop_index = context_p->lit_object.index; + parser_line_counter_t start_line = context_p->token.line; + parser_line_counter_t start_column = context_p->token.column; + uint16_t push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL; + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + break; + } + else if (context_p->token.type == LEXER_RIGHT_SQUARE) + { + prop_index = UINT16_MAX; + push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_PROP; + } + + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); + parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); + } + + lexer_next_token (context_p); + + if (push_prop_opcode != CBC_EXT_INITIALIZER_PUSH_PROP + && (context_p->token.type == LEXER_RIGHT_BRACE + || context_p->token.type == LEXER_ASSIGN + || context_p->token.type == LEXER_COMMA)) + { + parser_reparse_as_common_identifier (context_p, start_line, start_column); + lexer_next_token (context_p); + } + + bool is_inner_pattern = false; + if (context_p->token.type == LEXER_COLON) + { + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE) + { + parser_pattern_process_inner_pattern (context_p, flags, push_prop_opcode, prop_index); + is_inner_pattern = true; + } + else + { + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + lexer_next_token (context_p); + } + } + + if (!is_inner_pattern) + { +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + parser_module_append_export_name (context_p); +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + parser_pattern_form_assignment (context_p, push_prop_opcode, prop_index, start_line); + } + + if (context_p->token.type == LEXER_RIGHT_BRACE) + { + break; + } + else if (context_p->token.type != LEXER_COMMA) + { + parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); + } + } + + parser_pattern_finalize (context_p, flags, &end_pos); +} /* parser_parse_object_initializer */ + +/** + * Parse an initializer. + * + */ +void +parser_parse_initializer (parser_context_t *context_p, /**< context */ + parser_pattern_flags_t flags) /**< flags */ +{ + if (context_p->token.type == LEXER_LEFT_BRACE) + { + parser_parse_object_initializer (context_p, flags); + } + else + { + JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); + parser_parse_array_initializer (context_p, flags); + } +} /* parser_parse_initializer */ +#endif /* ENABLED (JERRY_ES2015) */ + /** * Process ternary expression. */ diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index e9f3f2d7e..46f36f924 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -99,6 +99,18 @@ typedef enum * CBC_PUSH_LITERAL instruction */ } parser_expression_flags_t; +/** + * Pattern parsing flags. + */ +typedef enum +{ + PARSER_PATTERN_NO_OPTS = 0, /**< parse the expression after '=' */ + PARSER_PATTERN_BINDING = (1u << 0), /**< parse BindingPattern */ + PARSER_PATTERN_TARGET_ON_STACK = (1u << 1), /**< assignment target is the topmost element on the stack */ + PARSER_PATTERN_TARGET_DEFAULT = (1u << 2), /**< perform default value comparison for assignment target */ + PARSER_PATTERN_INNER_PATTERN = (1u << 3), /**< parse patter inside a pattern */ +} parser_pattern_flags_t; + /** * Mask for strict mode code */ @@ -623,6 +635,7 @@ void parser_parse_expression (parser_context_t *context_p, int options); void parser_parse_class (parser_context_t *context_p, bool is_statement); void parser_parse_super_class_context_start (parser_context_t *context_p); void parser_parse_super_class_context_end (parser_context_t *context_p); +void parser_parse_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); #endif /* ENABLED (JERRY_ES2015) */ /** @@ -684,6 +697,7 @@ void parser_module_set_default (parser_context_t *context_p); ecma_module_node_t *parser_module_create_module_node (parser_context_t *context_p); bool parser_module_check_duplicate_import (parser_context_t *context_p, ecma_string_t *local_name_p); bool parser_module_check_duplicate_export (parser_context_t *context_p, ecma_string_t *export_name_p); +void parser_module_append_export_name (parser_context_t *context_p); void parser_module_add_names_to_node (parser_context_t *context_p, ecma_string_t *imex_name_p, ecma_string_t *local_name_p); diff --git a/jerry-core/parser/js/js-parser-module.c b/jerry-core/parser/js/js-parser-module.c index 74d296650..dbefff399 100644 --- a/jerry-core/parser/js/js-parser-module.c +++ b/jerry-core/parser/js/js-parser-module.c @@ -76,6 +76,34 @@ parser_module_check_duplicate_import (parser_context_t *context_p, /**< parser c return false; } /* parser_module_check_duplicate_import */ +/** + * Append an identifier to the exported bindings. + */ +void +parser_module_append_export_name (parser_context_t *context_p) /**< parser context */ +{ + if (!(context_p->status_flags & PARSER_MODULE_STORE_IDENT)) + { + return; + } + + context_p->module_identifier_lit_p = context_p->lit_object.literal_p; + + ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->lit_object.literal_p->u.char_p, + context_p->lit_object.literal_p->prop.length); + + if (parser_module_check_duplicate_export (context_p, name_p)) + { + ecma_deref_ecma_string (name_p); + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); + } + + parser_module_add_names_to_node (context_p, + name_p, + name_p); + ecma_deref_ecma_string (name_p); +} /* parser_module_append_export_name */ + /** * Check for duplicated exported bindings. * @return - true - if the exported name is a duplicate diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 5eefe6428..528d06ba1 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -446,86 +446,93 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ while (true) { - lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); - JERRY_ASSERT (context_p->token.type == LEXER_LITERAL - && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); +#if ENABLED (JERRY_ES2015) + if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_BRACE) + || lexer_check_next_character (context_p, LIT_CHAR_LEFT_SQUARE)) + { + lexer_next_token (context_p); + parser_parse_initializer (context_p, PARSER_PATTERN_BINDING); + } + else + { +#endif /* ENABLED (JERRY_ES2015) */ + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); #if ENABLED (JERRY_DEBUGGER) || ENABLED (JERRY_LINE_INFO) - parser_line_counter_t ident_line_counter = context_p->token.line; + parser_line_counter_t ident_line_counter = context_p->token.line; #endif /* ENABLED (JERRY_DEBUGGER) || ENABLED (JERRY_LINE_INFO) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) - { - context_p->module_identifier_lit_p = context_p->lit_object.literal_p; - context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); - } + parser_module_append_export_name (context_p); #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ #if ENABLED (JERRY_ES2015) - if (context_p->next_scanner_info_p->source_p == context_p->source_p) - { - JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); - parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); - } + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); + parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); + } #endif /* ENABLED (JERRY_ES2015) */ - lexer_next_token (context_p); + lexer_next_token (context_p); - if (context_p->token.type == LEXER_ASSIGN) - { -#if ENABLED (JERRY_DEBUGGER) - if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - && ident_line_counter != context_p->last_breakpoint_line) + if (context_p->token.type == LEXER_ASSIGN) { - parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED); - parser_flush_cbc (context_p); +#if ENABLED (JERRY_DEBUGGER) + if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + && ident_line_counter != context_p->last_breakpoint_line) + { + 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); + parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, ident_line_counter); - context_p->last_breakpoint_line = ident_line_counter; - } + context_p->last_breakpoint_line = ident_line_counter; + } #endif /* ENABLED (JERRY_DEBUGGER) */ #if ENABLED (JERRY_LINE_INFO) - if (ident_line_counter != context_p->last_line_info_line) - { - parser_emit_line_info (context_p, ident_line_counter, false); - } + if (ident_line_counter != context_p->last_line_info_line) + { + parser_emit_line_info (context_p, ident_line_counter, false); + } #endif /* ENABLED (JERRY_LINE_INFO) */ #if ENABLED (JERRY_ES2015) - if (declaration_type != LEXER_KEYW_VAR - && context_p->lit_object.index < PARSER_REGISTER_START) + if (declaration_type != LEXER_KEYW_VAR + && context_p->lit_object.index < PARSER_REGISTER_START) + { + uint16_t index = context_p->lit_object.index; + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_emit_cbc_literal (context_p, CBC_ASSIGN_LET_CONST, index); + } + else + { +#endif /* ENABLED (JERRY_ES2015) */ + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); +#if ENABLED (JERRY_ES2015) + } +#endif /* ENABLED (JERRY_ES2015) */ + } +#if ENABLED (JERRY_ES2015) + else if (declaration_type == LEXER_KEYW_LET) { + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + uint16_t index = context_p->lit_object.index; - - lexer_next_token (context_p); - parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); - parser_emit_cbc_literal (context_p, CBC_ASSIGN_LET_CONST, index); + parser_emit_cbc_literal (context_p, + index >= PARSER_REGISTER_START ? CBC_MOV_IDENT : CBC_ASSIGN_LET_CONST, + index); } - else + else if (declaration_type == LEXER_KEYW_CONST) { -#endif /* ENABLED (JERRY_ES2015) */ - parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); - parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); -#if ENABLED (JERRY_ES2015) + parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST); } -#endif /* ENABLED (JERRY_ES2015) */ - } -#if ENABLED (JERRY_ES2015) - else if (declaration_type == LEXER_KEYW_LET) - { - parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); - - uint16_t index = context_p->lit_object.index; - parser_emit_cbc_literal (context_p, - index >= PARSER_REGISTER_START ? CBC_MOV_IDENT : CBC_ASSIGN_LET_CONST, - index); - } - else if (declaration_type == LEXER_KEYW_CONST) - { - parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST); } #endif /* ENABLED (JERRY_ES2015) */ @@ -534,6 +541,10 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ break; } } + +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ } /* parser_parse_var_statement */ /** @@ -565,11 +576,8 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) - { - context_p->module_identifier_lit_p = context_p->lit_object.literal_p; - context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); - } + parser_module_append_export_name (context_p); + context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; @@ -2303,64 +2311,23 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ break; } case LEXER_KEYW_VAR: -#if ENABLED (JERRY_ES2015) case LEXER_KEYW_LET: case LEXER_KEYW_CONST: -#endif /* ENABLED (JERRY_ES2015) */ { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_var_statement (context_p); - ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->module_identifier_lit_p->u.char_p, - context_p->module_identifier_lit_p->prop.length); - - if (parser_module_check_duplicate_export (context_p, name_p)) - { - ecma_deref_ecma_string (name_p); - parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); - } - - parser_module_add_names_to_node (context_p, - name_p, - name_p); - ecma_deref_ecma_string (name_p); break; } case LEXER_KEYW_CLASS: { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_class (context_p, true); - ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->module_identifier_lit_p->u.char_p, - context_p->module_identifier_lit_p->prop.length); - - if (parser_module_check_duplicate_export (context_p, name_p)) - { - ecma_deref_ecma_string (name_p); - parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); - } - - parser_module_add_names_to_node (context_p, - name_p, - name_p); - ecma_deref_ecma_string (name_p); break; } case LEXER_KEYW_FUNCTION: { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_function_statement (context_p); - ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->module_identifier_lit_p->u.char_p, - context_p->module_identifier_lit_p->prop.length); - - if (parser_module_check_duplicate_export (context_p, name_p)) - { - ecma_deref_ecma_string (name_p); - parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); - } - - parser_module_add_names_to_node (context_p, - name_p, - name_p); - ecma_deref_ecma_string (name_p); break; } case LEXER_LEFT_BRACE: diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 449f381d2..d41bb671b 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 "Duplicated function argument names are not allowed here."; } + case PARSER_ERR_INVALID_DESTRUCTURING_PATTERN: + { + return "Invalid destructuring assignment target."; + } case PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER: { return "Rest parameter must be the last formal parameter."; diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index d900a8084..2787c2a6a 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -128,6 +128,7 @@ typedef enum PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER, /**< formal parameter after rest parameter */ PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER, /**< rest parameter default initializer */ PARSER_ERR_DUPLICATED_ARGUMENT_NAMES, /**< duplicated argument names */ + PARSER_ERR_INVALID_DESTRUCTURING_PATTERN, /**< invalid destructuring pattern */ #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) PARSER_ERR_FILE_NOT_FOUND, /**< file not found*/ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index c42292698..67ff54726 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -35,12 +35,12 @@ typedef struct } scanner_source_start_t; /** - * Descriptor for storing a let/const literal on stack. + * Descriptor for storing a binding literal on stack. */ typedef struct { - lexer_lit_location_t *literal_p; /**< let/const literal */ -} scanner_let_const_literal_t; + lexer_lit_location_t *literal_p; /**< binding literal */ +} scanner_binding_literal_t; /** * Flags for type member of lexer_lit_location_t structure in the literal pool. @@ -92,6 +92,46 @@ typedef struct scanner_case_info_t **last_case_p; /**< last case info */ } scanner_switch_statement_t; +#if ENABLED (JERRY_ES2015) + +/** + * Types of scanner destructuring bindings. + */ +typedef enum +{ + /* Update SCANNER_NEEDS_BINDING_LIST after changing these values. */ + SCANNER_BINDING_NONE, /**< not a destructuring binding expression */ + SCANNER_BINDING_VAR, /**< destructuring var binding */ + SCANNER_BINDING_LET, /**< destructuring let binding */ + SCANNER_BINDING_CONST, /**< destructuring const binding */ +} scanner_binding_type_t; + +/** + * Check whether a binding list is needed for the binding pattern. + */ +#define SCANNER_NEEDS_BINDING_LIST(type) ((type) >= SCANNER_BINDING_LET) + +/** + * Scanner binding items for destructuring binding patterns. + */ +typedef struct scanner_binding_item_t +{ + struct scanner_binding_item_t *next_p; /**< next binding in the list */ + lexer_lit_location_t *literal_p; /**< binding literal */ +} scanner_binding_item_t; + +/** + * Scanner binding lists for destructuring binding patterns. + */ +typedef struct scanner_binding_list_t +{ + struct scanner_binding_list_t *prev_p; /**< prev list */ + scanner_binding_item_t *items_p; /**< list of bindings */ + bool is_nested; /**< is nested binding declaration */ +} scanner_binding_list_t; + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Flags for scanner_literal_pool_t structure. */ @@ -137,6 +177,10 @@ struct scanner_context_t #if ENABLED (JERRY_DEBUGGER) uint8_t debugger_enabled; /**< debugger is enabled */ #endif /* ENABLED (JERRY_DEBUGGER) */ +#if ENABLED (JERRY_ES2015) + uint8_t binding_type; /**< current destructuring binding type */ + scanner_binding_list_t *active_binding_list_p; /**< currently active binding list */ +#endif /* ENABLED (JERRY_ES2015) */ scanner_literal_pool_t *active_literal_pool_p; /**< currently active literal pool */ scanner_switch_statement_t active_switch_statement; /**< currently active switch statement */ scanner_info_t *end_arguments_p; /**< position of end arguments */ @@ -174,6 +218,11 @@ void scanner_detect_invalid_let (parser_context_t *context_p, lexer_lit_location #endif /* ENABLED (JERRY_ES2015) */ void scanner_detect_eval_call (parser_context_t *context_p, scanner_context_t *scanner_context_p); +#if ENABLED (JERRY_ES2015) +void scanner_push_destructuring_pattern (parser_context_t *context_p, scanner_context_t *scanner_context_p, + uint8_t binding_type, bool is_nested); +void scanner_pop_binding_list (scanner_context_t *scanner_context_p); +#endif /* ENABLED (JERRY_ES2015) */ /** * @} diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index a8bfe924f..ad36e1b23 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -1033,7 +1033,6 @@ scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */ lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } -#if ENABLED (JERRY_ES2015) if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); @@ -1044,7 +1043,6 @@ scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */ return true; } } -#endif /* ENABLED (JERRY_ES2015) */ ecma_deref_ecma_string (name_p); return false; @@ -1144,6 +1142,80 @@ scanner_detect_invalid_let (parser_context_t *context_p, /**< context */ } } /* scanner_detect_invalid_let */ +/** + * Push the values required for destructuring assignment or binding parsing. + */ +void +scanner_push_destructuring_pattern (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p, /**< scanner context */ + uint8_t binding_type, /**< type of destructuring binding pattern */ + bool is_nested) /**< nested declaration */ +{ + JERRY_ASSERT (binding_type != SCANNER_BINDING_NONE || !is_nested); + + scanner_source_start_t source_start; + source_start.source_p = context_p->source_p; + + parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); + parser_stack_push_uint8 (context_p, scanner_context_p->binding_type); + scanner_context_p->binding_type = binding_type; + + if (SCANNER_NEEDS_BINDING_LIST (binding_type)) + { + scanner_binding_list_t *binding_list_p; + binding_list_p = (scanner_binding_list_t *) scanner_malloc (context_p, sizeof (scanner_binding_list_t)); + + binding_list_p->prev_p = scanner_context_p->active_binding_list_p; + binding_list_p->items_p = NULL; + binding_list_p->is_nested = is_nested; + + scanner_context_p->active_binding_list_p = binding_list_p; + } +} /* scanner_push_destructuring_pattern */ + +/** + * Pop binding list. + */ +void +scanner_pop_binding_list (scanner_context_t *scanner_context_p) /**< scanner context */ +{ + scanner_binding_list_t *binding_list_p = scanner_context_p->active_binding_list_p; + scanner_binding_item_t *item_p = binding_list_p->items_p; + scanner_binding_list_t *prev_binding_list_p = binding_list_p->prev_p; + bool is_nested = binding_list_p->is_nested; + + scanner_free (binding_list_p, sizeof (scanner_binding_list_t)); + scanner_context_p->active_binding_list_p = prev_binding_list_p; + + JERRY_ASSERT (binding_list_p != NULL); + + if (!is_nested) + { + while (item_p != NULL) + { + scanner_binding_item_t *next_p = item_p->next_p; + + JERRY_ASSERT (item_p->literal_p->type & SCANNER_LITERAL_IS_LOCAL); + + scanner_free (item_p, sizeof (scanner_binding_item_t)); + item_p = next_p; + } + return; + } + + JERRY_ASSERT (prev_binding_list_p != NULL); + + while (item_p != NULL) + { + scanner_binding_item_t *next_p = item_p->next_p; + + item_p->next_p = prev_binding_list_p->items_p; + prev_binding_list_p->items_p = item_p; + + item_p = next_p; + } +} /* scanner_pop_binding_list */ + #endif /* ENABLED (JERRY_ES2015) */ /** @@ -1215,6 +1287,9 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ case SCANNER_TYPE_FOR_OF: #endif /* ENABLED (JERRY_ES2015) */ case SCANNER_TYPE_CASE: +#if ENABLED (JERRY_ES2015) + case SCANNER_TYPE_INITIALIZER: +#endif /* ENABLED (JERRY_ES2015) */ { size = sizeof (scanner_location_info_t); break; diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index de48ba13c..1a98d9a5d 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -43,10 +43,11 @@ typedef enum SCAN_MODE_STATEMENT_OR_TERMINATOR, /**< scanning statement or statement end */ SCAN_MODE_STATEMENT_END, /**< scanning statement end */ SCAN_MODE_VAR_STATEMENT, /**< scanning var statement */ - SCAN_MODE_FUNCTION_ARGUMENTS, /**< scanning function arguments */ SCAN_MODE_PROPERTY_NAME, /**< scanning property name */ + SCAN_MODE_FUNCTION_ARGUMENTS, /**< scanning function arguments */ #if ENABLED (JERRY_ES2015) SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS, /**< continue scanning function arguments */ + SCAN_MODE_BINDING, /**< array or object binding */ SCAN_MODE_CLASS_DECLARATION, /**< scanning class declaration */ SCAN_MODE_CLASS_METHOD, /**< scanning class method */ #endif /* ENABLED (JERRY_ES2015) */ @@ -73,9 +74,10 @@ typedef enum SCAN_STACK_PAREN_EXPRESSION, /**< expression in brackets */ SCAN_STACK_STATEMENT_WITH_EXPR, /**< statement which starts with expression enclosed in brackets */ #if ENABLED (JERRY_ES2015) + SCAN_STACK_BINDING_INIT, /**< post processing after a single initializer */ + SCAN_STACK_BINDING_LIST_INIT, /**< post processing after an initializer list */ SCAN_STACK_LET, /**< let statement */ SCAN_STACK_CONST, /**< const statement */ - SCAN_STACK_LET_CONST_INIT, /**< let/const statement initializer */ #endif /* ENABLED (JERRY_ES2015) */ /* The SCANNER_IS_FOR_START macro needs to be updated when the following constants are reordered. */ SCAN_STACK_VAR, /**< var statement */ @@ -92,8 +94,9 @@ typedef enum SCAN_STACK_COLON_EXPRESSION, /**< expression between a question mark and colon */ SCAN_STACK_TRY_STATEMENT, /**< try statement */ SCAN_STACK_CATCH_STATEMENT, /**< catch statement */ - SCAN_STACK_SQUARE_BRACKETED_EXPRESSION, /**< square bracketed expression group */ + SCAN_STACK_ARRAY_LITERAL, /**< array literal or destructuring assignment or binding */ SCAN_STACK_OBJECT_LITERAL, /**< object literal group */ + SCAN_STACK_PROPERTY_ACCESSOR, /**< property accessor in squarey brackets */ #if ENABLED (JERRY_ES2015) SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */ SCAN_STACK_TEMPLATE_STRING, /**< template string */ @@ -333,12 +336,20 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ } case LEXER_LEFT_SQUARE: { - parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); +#if ENABLED (JERRY_ES2015) + scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_NONE, false); +#endif /* ENABLED (JERRY_ES2015) */ + + parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_LEFT_BRACE: { +#if ENABLED (JERRY_ES2015) + scanner_push_destructuring_pattern (context_p, scanner_context_p, SCANNER_BINDING_NONE, false); +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; return SCAN_KEEP_TOKEN; @@ -402,20 +413,21 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_ES2015) */ case LEXER_RIGHT_SQUARE: { - if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) + if (stack_top != SCAN_STACK_ARRAY_LITERAL) { scanner_raise_error (context_p); } - parser_stack_pop_uint8 (context_p); - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return SCAN_KEEP_TOKEN; } #if ENABLED (JERRY_ES2015) case LEXER_THREE_DOTS: #endif /* ENABLED (JERRY_ES2015) */ case LEXER_COMMA: { - if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) + /* Elision. */ + if (stack_top != SCAN_STACK_ARRAY_LITERAL) { scanner_raise_error (context_p); } @@ -465,7 +477,7 @@ scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context } case LEXER_LEFT_SQUARE: { - parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); + parser_stack_push_uint8 (context_p, SCAN_STACK_PROPERTY_ACCESSOR); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } @@ -511,11 +523,6 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * { switch (stack_top) { - case SCAN_STACK_OBJECT_LITERAL: - { - scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; - return SCAN_KEEP_TOKEN; - } case SCAN_STACK_VAR: #if ENABLED (JERRY_ES2015) case SCAN_STACK_LET: @@ -536,16 +543,23 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } #if ENABLED (JERRY_ES2015) - case SCAN_STACK_LET_CONST_INIT: + case SCAN_STACK_BINDING_INIT: { - scanner_let_const_literal_t let_const_literal; + scanner_binding_literal_t binding_literal; parser_stack_pop_uint8 (context_p); - parser_stack_pop (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); + parser_stack_pop (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); - if (let_const_literal.literal_p->type & SCANNER_LITERAL_IS_USED) + if (binding_literal.literal_p->type & SCANNER_LITERAL_IS_USED) { - let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + binding_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + } + + if (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL + || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL) + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return SCAN_KEEP_TOKEN; } JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET @@ -572,7 +586,23 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } + case SCAN_STACK_ARRAY_LITERAL: + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (scanner_context_p->binding_type != SCANNER_BINDING_NONE) + { + scanner_context_p->mode = SCAN_MODE_BINDING; + } + + return SCAN_NEXT_TOKEN; + } #endif /* ENABLED (JERRY_ES2015) */ + case SCAN_STACK_OBJECT_LITERAL: + { + scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME; + return SCAN_KEEP_TOKEN; + } default: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; @@ -669,16 +699,47 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * return SCAN_NEXT_TOKEN; } #if ENABLED (JERRY_ES2015) - case SCAN_STACK_LET_CONST_INIT: + case SCAN_STACK_BINDING_LIST_INIT: { - scanner_let_const_literal_t let_const_literal; + parser_stack_pop_uint8 (context_p); + + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL + || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL + || context_p->stack_top_uint8 == SCAN_STACK_LET + || context_p->stack_top_uint8 == SCAN_STACK_CONST); + + scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p; + + while (item_p != NULL) + { + if (item_p->literal_p->type & SCANNER_LITERAL_IS_USED) + { + item_p->literal_p->type |= SCANNER_LITERAL_NO_REG; + } + item_p = item_p->next_p; + } + + scanner_pop_binding_list (scanner_context_p); + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return SCAN_KEEP_TOKEN; + } + case SCAN_STACK_BINDING_INIT: + { + scanner_binding_literal_t binding_literal; parser_stack_pop_uint8 (context_p); - parser_stack_pop (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); + parser_stack_pop (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); - if (let_const_literal.literal_p->type & SCANNER_LITERAL_IS_USED) + if (binding_literal.literal_p->type & SCANNER_LITERAL_IS_USED) { - let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + binding_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + } + + if (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL + || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL) + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return SCAN_KEEP_TOKEN; } if (context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START @@ -908,26 +969,86 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } - case SCAN_STACK_SQUARE_BRACKETED_EXPRESSION: +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_ARRAY_LITERAL: + case SCAN_STACK_OBJECT_LITERAL: + { + if (((stack_top == SCAN_STACK_ARRAY_LITERAL) && (type != LEXER_RIGHT_SQUARE)) + || ((stack_top == SCAN_STACK_OBJECT_LITERAL) && (type != LEXER_RIGHT_BRACE))) + { + break; + } + + scanner_source_start_t source_start; + uint8_t binding_type = scanner_context_p->binding_type; + + parser_stack_pop_uint8 (context_p); + scanner_context_p->binding_type = context_p->stack_top_uint8; + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_ASSIGN) + { + if (SCANNER_NEEDS_BINDING_LIST (binding_type)) + { + scanner_pop_binding_list (scanner_context_p); + } + + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return SCAN_KEEP_TOKEN; + } + + scanner_location_info_t *location_info_p; + location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p, + source_start.source_p, + sizeof (scanner_location_info_t)); + location_info_p->info.type = SCANNER_TYPE_INITIALIZER; + scanner_get_location (&location_info_p->location, context_p); + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (SCANNER_NEEDS_BINDING_LIST (binding_type)) + { + scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p; + + while (item_p != NULL) + { + item_p->literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_USED; + item_p = item_p->next_p; + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_LIST_INIT); + } + return SCAN_NEXT_TOKEN; + } +#else /* !ENABLED (JERRY_ES2015) */ + case SCAN_STACK_ARRAY_LITERAL: +#endif /* ENABLED (JERRY_ES2015) */ + case SCAN_STACK_PROPERTY_ACCESSOR: { if (type != LEXER_RIGHT_SQUARE) { break; } + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } +#if !ENABLED (JERRY_ES2015) case SCAN_STACK_OBJECT_LITERAL: { if (type != LEXER_RIGHT_BRACE) { break; } + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } +#endif /* !ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015) case SCAN_STACK_COMPUTED_PROPERTY: { @@ -966,6 +1087,11 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * } scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (scanner_context_p->binding_type != SCANNER_BINDING_NONE) + { + scanner_context_p->mode = SCAN_MODE_BINDING; + } return SCAN_NEXT_TOKEN; } case SCAN_STACK_TEMPLATE_STRING: @@ -2094,6 +2220,10 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_DEBUGGER) scanner_context.debugger_enabled = (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) ? 1 : 0; #endif /* ENABLED (JERRY_DEBUGGER) */ +#if ENABLED (JERRY_ES2015) + scanner_context.binding_type = SCANNER_BINDING_NONE; + scanner_context.active_binding_list_p = NULL; +#endif /* ENABLED (JERRY_ES2015) */ scanner_context.active_literal_pool_p = NULL; scanner_context.active_switch_statement.last_case_p = NULL; scanner_context.end_arguments_p = NULL; @@ -2284,6 +2414,35 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } case SCAN_MODE_VAR_STATEMENT: { +#if ENABLED (JERRY_ES2015) + if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE) + { + uint8_t binding_type = SCANNER_BINDING_VAR; + + if (stack_top == SCAN_STACK_LET) + { + binding_type = SCANNER_BINDING_LET; + } + else if (stack_top == SCAN_STACK_CONST) + { + binding_type = SCANNER_BINDING_CONST; + } + + scanner_push_destructuring_pattern (context_p, &scanner_context, binding_type, false); + + if (type == LEXER_LEFT_SQUARE) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); + scanner_context.mode = SCAN_MODE_BINDING; + break; + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); + scanner_context.mode = SCAN_MODE_PROPERTY_NAME; + continue; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { @@ -2315,11 +2474,11 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } else if (context_p->token.type == LEXER_ASSIGN) { - scanner_let_const_literal_t let_const_literal; - let_const_literal.literal_p = literal_p; + scanner_binding_literal_t binding_literal; + binding_literal.literal_p = literal_p; - parser_stack_push (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); - parser_stack_push_uint8 (context_p, SCAN_STACK_LET_CONST_INIT); + parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); + parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); } } else @@ -2516,9 +2675,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_RIGHT_BRACE) { - parser_stack_pop_uint8 (context_p); - scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + continue; } if (context_p->token.type == LEXER_PROPERTY_GETTER @@ -2568,7 +2726,9 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } if (is_ident - && (context_p->token.type == LEXER_COMMA || context_p->token.type == LEXER_RIGHT_BRACE)) + && (context_p->token.type == LEXER_COMMA + || context_p->token.type == LEXER_RIGHT_BRACE + || context_p->token.type == LEXER_ASSIGN)) { context_p->source_p = context_p->token.lit_location.char_p; context_p->line = start_line; @@ -2584,20 +2744,24 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } - scanner_add_literal (context_p, &scanner_context); - - lexer_next_token (context_p); - - if (context_p->token.type == LEXER_COMMA) + if (scanner_context.binding_type != SCANNER_BINDING_NONE) { + scanner_context.mode = SCAN_MODE_BINDING; continue; } - JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE); + scanner_add_reference (context_p, &scanner_context); - parser_stack_pop_uint8 (context_p); - scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - break; + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_ASSIGN) + { + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + continue; } #endif /* ENABLED (JERRY_ES2015) */ @@ -2607,8 +2771,112 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; + +#if ENABLED (JERRY_ES2015) + if (scanner_context.binding_type != SCANNER_BINDING_NONE) + { + scanner_context.mode = SCAN_MODE_BINDING; + } +#endif /* ENABLED (JERRY_ES2015) */ break; } +#if ENABLED (JERRY_ES2015) + case SCAN_MODE_BINDING: + { + JERRY_ASSERT (scanner_context.binding_type == SCANNER_BINDING_VAR + || scanner_context.binding_type == SCANNER_BINDING_LET + || scanner_context.binding_type == SCANNER_BINDING_CONST); + + if (type == LEXER_THREE_DOTS) + { + lexer_next_token (context_p); + type = (lexer_token_type_t) context_p->token.type; + } + + if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE) + { + scanner_push_destructuring_pattern (context_p, &scanner_context, scanner_context.binding_type, true); + + if (type == LEXER_LEFT_SQUARE) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL); + break; + } + + parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); + scanner_context.mode = SCAN_MODE_PROPERTY_NAME; + continue; + } + + if (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; + continue; + } + + lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context); + + scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + + if (scanner_context.binding_type == SCANNER_BINDING_VAR) + { + if (!(literal_p->type & SCANNER_LITERAL_IS_VAR)) + { + scanner_detect_invalid_var (context_p, &scanner_context, literal_p); + literal_p->type |= SCANNER_LITERAL_IS_VAR; + + if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + literal_p->type |= SCANNER_LITERAL_NO_REG; + } + } + } + else + { + scanner_detect_invalid_let (context_p, literal_p); + + if (scanner_context.binding_type == SCANNER_BINDING_LET) + { + literal_p->type |= SCANNER_LITERAL_IS_LET; + } + else + { + literal_p->type |= SCANNER_LITERAL_IS_CONST; + } + + if (literal_p->type & SCANNER_LITERAL_IS_USED) + { + literal_p->type |= SCANNER_LITERAL_NO_REG; + } + else + { + scanner_binding_item_t *binding_item_p; + binding_item_p = (scanner_binding_item_t *) scanner_malloc (context_p, sizeof (scanner_binding_item_t)); + + binding_item_p->next_p = scanner_context.active_binding_list_p->items_p; + binding_item_p->literal_p = literal_p; + + scanner_context.active_binding_list_p->items_p = binding_item_p; + + lexer_next_token (context_p); + if (context_p->token.type != LEXER_ASSIGN) + { + continue; + } + + scanner_binding_literal_t binding_literal; + binding_literal.literal_p = literal_p; + + parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t)); + parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT); + + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; + } + } + + break; + } +#endif /* ENABLED (JERRY_ES2015) */ } lexer_next_token (context_p); @@ -2623,6 +2891,11 @@ scan_completed: scanner_pop_literal_pool (context_p, &scanner_context); +#if ENABLED (JERRY_ES2015) + JERRY_ASSERT (scanner_context.active_binding_list_p == NULL); +#endif /* ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (scanner_context.active_literal_pool_p == NULL); + #ifndef JERRY_NDEBUG context_p->status_flags |= PARSER_SCANNING_SUCCESSFUL; #endif /* !JERRY_NDEBUG */ @@ -2635,6 +2908,13 @@ scan_completed: context_p->error = PARSER_ERR_NO_ERROR; } +#if ENABLED (JERRY_ES2015) + while (scanner_context.active_binding_list_p != NULL) + { + scanner_pop_binding_list (&scanner_context); + } +#endif /* ENABLED (JERRY_ES2015) */ + /* The following loop may allocate memory, so it is enclosed in a try/catch. */ PARSER_TRY (context_p->try_buffer) { @@ -2860,6 +3140,20 @@ scan_completed: print_location = true; break; } +#if ENABLED (JERRY_ES2015) + case SCANNER_TYPE_INITIALIZER: + { + name_p = "INITIALIZER"; + print_location = true; + break; + } + case SCANNER_TYPE_ERR_REDECLARED: + { + JERRY_DEBUG_MSG (" ERR_REDECLARED: source:%d\n", + (int) (info_p->source_p - source_start_p)); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ } if (print_location) diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index 2058e6df1..048dac7b7 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -45,6 +45,7 @@ typedef enum SCANNER_TYPE_SWITCH, /**< switch statement */ SCANNER_TYPE_CASE, /**< case statement */ #if ENABLED (JERRY_ES2015) + SCANNER_TYPE_INITIALIZER, /**< destructuring binding or assignment pattern with initializer */ SCANNER_TYPE_ERR_REDECLARED, /**< syntax error: a variable is redeclared */ #endif /* ENABLED (JERRY_ES2015) */ } scanner_info_type_t; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 95ac3ea60..0a5f7ada6 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1791,7 +1791,104 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = ecma_op_create_spread_object (left_value); goto free_left_value; } -#endif /* ENABLED (JERRY_ES2015) */ + case VM_OC_GET_ITERATOR: + { + result = ecma_op_get_iterator (stack_top_p[-1], ECMA_VALUE_EMPTY); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + *stack_top_p++ = result; + continue; + } + case VM_OC_ITERATOR_STEP: + { + const int8_t index = (opcode == CBC_EXT_ITERATOR_STEP) ? -1 : -3; + result = ecma_op_iterator_step (stack_top_p[index]); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + ecma_value_t value = ECMA_VALUE_UNDEFINED; + + if (!ecma_is_value_false (result)) + { + value = ecma_op_iterator_value (result); + ecma_free_value (result); + + if (ECMA_IS_VALUE_ERROR (value)) + { + result = value; + goto error; + } + } + + *stack_top_p++ = value; + continue; + } + case VM_OC_DEFAULT_INITIALIZER: + { + if (stack_top_p[-1] != ECMA_VALUE_UNDEFINED) + { + byte_code_p = byte_code_start_p + branch_offset; + } + continue; + } + case VM_OC_REST_INITIALIZER: + { + const int8_t iterator_index = (opcode == CBC_EXT_REST_INITIALIZER) ? -1 : -3; + ecma_object_t *array_p = ecma_op_new_fast_array_object (0); + ecma_value_t iterator = stack_top_p[iterator_index]; + uint32_t index = 0; + + while (true) + { + result = ecma_op_iterator_step (iterator); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + if (ecma_is_value_false (result)) + { + break; + } + + ecma_value_t value = ecma_op_iterator_value (result); + ecma_free_value (result); + + if (ECMA_IS_VALUE_ERROR (value)) + { + result = value; + goto error; + } + + bool set_result = ecma_fast_array_set_property (array_p, index++, value); + JERRY_ASSERT (set_result); + ecma_free_value (value); + } + + *stack_top_p++ = ecma_make_object_value (array_p); + continue; + } + case VM_OC_INITIALIZER_PUSH_PROP: + { + result = vm_op_get_value (stack_top_p[-1], left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + *stack_top_p++ = result; + goto free_left_value; + } + #endif /* ENABLED (JERRY_ES2015) */ case VM_OC_PUSH_ELISON: { *stack_top_p++ = ECMA_VALUE_ARRAY_HOLE; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index c0cef3231..08a7b9bb2 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -248,6 +248,11 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_THIS, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET, /**< explicit return from a class constructor */ VM_OC_CREATE_SPREAD_OBJECT, /**< create spread object */ + VM_OC_GET_ITERATOR, /**< GetIterator abstract operation */ + VM_OC_ITERATOR_STEP, /**< IteratorStep abstract operation */ + VM_OC_DEFAULT_INITIALIZER, /**< default initializer inside a pattern */ + VM_OC_REST_INITIALIZER, /**< create rest object inside an array pattern */ + VM_OC_INITIALIZER_PUSH_PROP, /**< push property for object initializer */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -291,8 +296,12 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_THIS = VM_OC_NONE, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET = VM_OC_NONE, /**< explicit return from a class constructor */ VM_OC_CREATE_SPREAD_OBJECT = VM_OC_NONE, /**< create spread object */ -#endif /* !ENABLED (JERRY_ES2015S) */ - + VM_OC_GET_ITERATOR = VM_OC_NONE, /**< GetIterator abstract operation */ + VM_OC_ITERATOR_STEP = VM_OC_NONE, /**< IteratorStep abstract operation */ + VM_OC_DEFAULT_INITIALIZER = VM_OC_NONE, /**< default initializer inside a pattern */ + VM_OC_REST_INITIALIZER = VM_OC_NONE, /**< create rest object inside an array pattern */ + VM_OC_INITIALIZER_PUSH_PROP = VM_OC_NONE, /**< push property for object initializer */ +#endif /* !ENABLED (JERRY_ES2015) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ } vm_oc_unused_types; diff --git a/tests/jerry/es2015/array-pattern.js b/tests/jerry/es2015/array-pattern.js new file mode 100644 index 000000000..be0400049 --- /dev/null +++ b/tests/jerry/es2015/array-pattern.js @@ -0,0 +1,224 @@ +// 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 checkSyntax (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } +} + +function assertArrayEqual (actual, expected) { + assert (actual.length === expected.length); + + for (var i = 0; i < actual.length; i++) { + assert (actual[i] === expected[i]); + } +} + +function mustThrow (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +} + +checkSyntax ("var [a]"); +checkSyntax ("var [a, o.a]"); +checkSyntax ("var [a, ...b,]"); +checkSyntax ("var [a, ...b = 4]"); +checkSyntax ("var [a, ...[b] = 4]"); +checkSyntax ("var [let]"); +checkSyntax ("var [get = []"); +checkSyntax ("var [get : 5]"); +checkSyntax ("var [[a = {},]"); +checkSyntax ("let [a,a] = []"); +checkSyntax ("let [a, ...a] = []"); +checkSyntax ("const [a,a] = []"); +checkSyntax ("const [a, ...a] = []"); +checkSyntax ("[new Object()] = []"); +checkSyntax ("[Object()] = []"); +checkSyntax ("[(a, b, d, c)] = []"); +checkSyntax ("[super] = []"); +checkSyntax ("[this] = []"); +checkSyntax ("[()] = []"); +checkSyntax ("try { let [$] = $;"); + +mustThrow ("var [a] = 4"); +mustThrow ("var [a] = 5"); +mustThrow ("var [a] = {}"); +mustThrow ("var [a] = { get [Symbol.iterator] () { throw new TypeError } }"); +mustThrow ("var [a] = { [Symbol.iterator] () {} }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return {} } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next: 5 } } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next: 5 } } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { get next() { throw new TypeError } } } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { } } } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { } } } }"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { return { get value () { throw new TypeError }}}}}}"); +mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { return { get done () { throw new TypeError }}}}}}"); + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment + +// Basic variable assignment +(function () { + var foo = ["one", "two", "three"]; + + var [red, yellow, green] = foo; + assert (red === "one"); + assert (yellow === "two"); + assert (green === "three"); +}) (); + +// Assignment separate from declaration +(function () { + var a, b; + + [a, b] = [1, 2]; + assert (a === 1); + assert (b === 2); +}) (); + +// Default values +(function () { + var a, b; + [a = 5, b = 7] = [1]; + + assert (a === 1); + assert (b === 7); +}) (); + +// Swapping variables +(function () { + var a = 1; + var b = 3; + + [a, b] = [b, a]; + assert (a === 3); + assert (b === 1); + + var arr = [1,2,3]; + [arr[2], arr[1]] = [arr[1], arr[2]]; + assertArrayEqual (arr, [1, 3, 2]); +}) (); + +// Parsing an array returned from a function +(function () { + function f() { + return [1, 2]; + } + + var a, b; + [a, b] = f(); + assert (a === 1); + assert (b === 2); +}) (); + +// Ignoring some returned values +(function () { + function f() { + return [1, 2, 3]; + } + + var a, b; + [a, ,b] = f(); + assert (a === 1); + assert (b === 3); +}) (); + +// Ignoring some returned values +(function () { + var [a, ...b] = [1, 2, 3]; + assert (a === 1); + assertArrayEqual (b, [2, 3]); +}) (); + +// Unpacking values from a regular expression match +(function () { + function parseProtocol(url) { + var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url); + if (!parsedURL) { + return false; + } + + var [, protocol, fullhost, fullpath] = parsedURL; + return protocol; + } + + assert (parseProtocol("https://developer.mozilla.org/en-US/Web/JavaScript") === "https"); +}) (); + +// Test inner patterns I. +(function () { + let [a, [b, [c = 4, d = 5]], [e] = [6]] = [1, [2, [3,undefined]]]; + + assert (a === 1); + assert (b === 2); + assert (c === 3); + assert (d === 5); + assert (e === 6); +}) (); + +// Test inner patterns II. +(function () { + var o = {}; + [a, b, c, o.a = 4, o.b, o.c = 3] = ["1", "2", "3", undefined, "8", "6"]; + + assert (a === "1"); + assert (b === "2"); + assert (c === "3"); + assert (o.a === 4); + assert (o.b === "8"); + assert (o.c === "6"); +}) (); + +// Test rest element I. +(function () { + var o = {}; + [...o.a] = ["1", "2", "3"]; + + assertArrayEqual (o.a, ["1", "2", "3"]); +}) (); + +// Test rest element II. +(function () { + [...[a,b,c]] = ["1", "2", "3"]; + + assert (a === "1"); + assert (b === "2"); + assert (c === "3"); +}) (); + +// Test inner object pattern I. +(function () { + [{f : a, g : b}, , , ...[c, d, e]] = [{ f : "1", g : "2"}, 3, 4, 5, 6, 7]; + + assert (a === "1"); + assert (b === "2"); + assert (c === 5); + assert (d === 6); + assert (e === 7); +}) (); + +// Multiple declaration +(function () { + var [a] = [1], [b] = [2]; + + assert (a === 1); + assert (b === 2); +}) (); diff --git a/tests/jerry/es2015/object-pattern.js b/tests/jerry/es2015/object-pattern.js new file mode 100644 index 000000000..f0f71a459 --- /dev/null +++ b/tests/jerry/es2015/object-pattern.js @@ -0,0 +1,199 @@ +// 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 checkSyntax (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } +} + +function mustThrow (str) { + try { + eval (str); + assert (false); + } catch (e) { + assert (e instanceof TypeError); + } +} + +checkSyntax ("var {a}"); +checkSyntax ("var {a, o.a}"); +checkSyntax ("var {a, ...b}"); +checkSyntax ("var {a, ...b} = 4"); +checkSyntax ("var {a, ...[b] = 4}"); +checkSyntax ("var {a,,} = 4"); +checkSyntax ("var {a :} = 4"); +checkSyntax ("var {a : ,} = 4"); +checkSyntax ("var {a : ['foobar']} = 4"); +checkSyntax ("var {let}"); +checkSyntax ("var {get = []"); +checkSyntax ("var {get : 5}"); +checkSyntax ("var {[a = {},}"); +checkSyntax ("let {a,a} = []"); +checkSyntax ("let {a : b, b} = []"); +checkSyntax ("const {a,a} = []"); +checkSyntax ("const {a : b, b} = []"); +checkSyntax ("try { let {$} = $;"); + +mustThrow ("var {a} = null"); +mustThrow ("var {a} = undefined"); + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment + +// Basic assignment +(function () { + var o = {p: 42, q: true}; + var {p, q} = o; + + assert (p === 42); + assert (q === true); +}) (); + +// Assignment without declaration +(function () { + var a, b; + ({a, b} = {a: 1, b: 2}); + + assert (a === 1); + assert (b === 2); +}) (); + +// Assigning to new variable names +(function () { + var o = {p: 42, q: true}; + var {p: foo, q: bar} = o; + + assert (foo === 42); + assert (bar === true); +}) (); + +// Default values +(function () { + var {a = 10, b = 5} = {a: 3}; + + assert (a === 3); + assert (b === 5); +}) (); + + +// Assigning to new variables names and providing default values +(function () { + var {a: aa = 10, b: bb = 5} = {a: 3}; + + assert (aa === 3); + assert (bb === 5); +}) (); + +// Nested object and array destructuring +(function () { + const metadata = { + title: 'Scratchpad', + translations: [ + { + locale: 'de', + localization_tags: [], + last_edit: '2014-04-14T08:43:37', + url: '/de/docs/Tools/Scratchpad', + title: 'JavaScript-Umgebung' + } + ], + url: '/en-US/docs/Tools/Scratchpad' + }; + + let { + title: englishTitle, // rename + translations: [ + { + title: localeTitle, // rename + }, + ], + } = metadata; + + assert (englishTitle === "Scratchpad"); + assert (localeTitle === "JavaScript-Umgebung"); +}) (); + +// Computed object property names and destructuring +(function () { + let key = 'z'; + let {[key]: foo} = {z: 'bar'}; + + assert (foo === "bar"); +}) (); + +// Invalid JavaScript identifier as a property name +(function () { + const foo = { 'fizz-buzz': true }; + const { 'fizz-buzz': fizzBuzz } = foo; + + assert (fizzBuzz === true); +}) (); + +// Combined Array and Object Destructuring +(function () { + const props = [ + { id: 1, name: 'Fizz'}, + { id: 2, name: 'Buzz'}, + { id: 3, name: 'FizzBuzz'} + ]; + + const [,, { name }] = props; + + assert (name === "FizzBuzz"); +}) (); + +// The prototype chain is looked up when the object is deconstructed +(function () { + var obj = {self: '123'}; + Object.getPrototypeOf(obj).prot = '456'; + const {self, prot} = obj; + assert (self === '123'); + assert (prot === '456'); +}) (); + +// Test inner patterns I. +(function () { + var a,b,c,d,e; + var o = { a : { b: 2 }, c: 1, d: { e: undefined } }; + var { e: { b : a } = { b : 2, a : 1}, d: { e: { b : e = 2} = { b } } } = o; + assert (a === 2); + assert (b === undefined); + assert (c === undefined); + assert (d === undefined); + assert (e === 2); +}) (); + +// Test inner patterns II. +(function () { + var a,b,c,d,e; + var o = { a : [{ b : 2 ,}, d], e : 5 }; + + var { a: [{b, c = 3}, d = 4], e } = o; + assert (a === undefined); + assert (b === 2); + assert (c === 3); + assert (d === 4); + assert (e === 5); +}) (); + +// Multiple declaration +(function () { + var {a} = {a : 1}, {b} = {b : 2}; + + assert (a === 1); + assert (b === 2); +}) (); diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 0af657b3e..2928a6cf0 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x1C, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x01, 0x00,