From 531f1e96878bce26d0b59765e172fb2f698f1d42 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 10 Dec 2019 14:32:08 +0100 Subject: [PATCH] Support let identifier in non-strict mode. (#3427) This code adds a lot of checks and complexity to the code. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/parser/js/js-lexer.c | 77 +++++++++- jerry-core/parser/js/js-lexer.h | 5 - jerry-core/parser/js/js-parser-expr.c | 11 +- jerry-core/parser/js/js-parser-internal.h | 16 +- jerry-core/parser/js/js-parser-module.c | 8 +- jerry-core/parser/js/js-parser-statm.c | 171 +++++++++++++++++----- jerry-core/parser/js/js-parser-util.c | 4 + jerry-core/parser/js/js-parser.c | 2 +- jerry-core/parser/js/js-parser.h | 3 +- jerry-core/parser/js/js-scanner-util.c | 1 + jerry-core/parser/js/js-scanner.c | 121 ++++++++++++--- jerry-core/parser/js/js-scanner.h | 1 + tests/jerry/es2015/let13.js | 110 ++++++++++++++ 13 files changed, 449 insertions(+), 81 deletions(-) create mode 100644 tests/jerry/es2015/let13.js diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index c6132acb1..f54bb1d26 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -643,6 +643,15 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ context_p->token.type = (uint8_t) LEXER_KEYW_YIELD; break; } + + if (keyword_p->type == LEXER_KEYW_LET && !context_p->token.lit_location.has_escape) + { + if (context_p->status_flags & PARSER_IS_STRICT) + { + context_p->token.type = (uint8_t) LEXER_KEYW_LET; + } + break; + } #endif /* ENABLED (JERRY_ES2015) */ if (context_p->status_flags & PARSER_IS_STRICT) @@ -2799,9 +2808,12 @@ lexer_compare_identifiers (const uint8_t *left_p, /**< left identifier */ * @return true if the input identifiers are the same */ bool -lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context */ - const lexer_lit_location_t *right_ident_p) /**< identifier */ +lexer_current_is_literal (parser_context_t *context_p, /**< context */ + const lexer_lit_location_t *right_ident_p) /**< identifier */ { + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + lexer_lit_location_t *left_ident_p = &context_p->token.lit_location; JERRY_ASSERT (left_ident_p->length > 0 && right_ident_p->length > 0); @@ -2817,10 +2829,12 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * } return lexer_compare_identifiers (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length); -} /* lexer_compare_identifier_to_current */ +} /* lexer_current_is_literal */ + +#if ENABLED (JERRY_ES2015) /** - * Compares the current identifier to an expected identifier. + * Compares the current token to an expected identifier. * * Note: * Escape sequences are not allowed. @@ -2828,16 +2842,63 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * * @return true if they are the same, false otherwise */ inline bool JERRY_ATTR_ALWAYS_INLINE -lexer_compare_literal_to_identifier (parser_context_t *context_p, /**< context */ - const char *identifier_p, /**< identifier */ - size_t identifier_length) /**< identifier length */ +lexer_token_is_identifier (parser_context_t *context_p, /**< context */ + const char *identifier_p, /**< identifier */ + size_t identifier_length) /**< identifier length */ { /* Checking has_escape is unnecessary because memcmp will fail if escape sequences are present. */ return (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL && context_p->token.lit_location.length == identifier_length && memcmp (context_p->token.lit_location.char_p, identifier_p, identifier_length) == 0); -} /* lexer_compare_literal_to_identifier */ +} /* lexer_token_is_identifier */ + +/** + * Compares the current identifier token to "let". + * + * Note: + * Escape sequences are not allowed. + * + * @return true if "let" is found, false otherwise + */ +inline bool JERRY_ATTR_ALWAYS_INLINE +lexer_token_is_let (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL); + + const uint8_t *char_p = context_p->token.lit_location.char_p; + + return (!(context_p->status_flags & PARSER_IS_STRICT) + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL + && context_p->token.lit_location.length == 3 + && char_p[0] == LIT_CHAR_LOWERCASE_L + && char_p[1] == LIT_CHAR_LOWERCASE_E + && char_p[2] == LIT_CHAR_LOWERCASE_T); +} /* lexer_token_is_let */ + +/** + * Compares the current literal object to an expected identifier + * + * Note: + * Escape sequences are allowed. + * + * @return true if the input identifiers are the same + */ +bool +lexer_literal_object_is_identifier (parser_context_t *context_p, /**< context */ + const char *identifier_p, /**< identifier */ + size_t identifier_length) /**< identifier length */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + + lexer_literal_t *literal_p = context_p->lit_object.literal_p; + + return (literal_p->prop.length == identifier_length + && memcmp (literal_p->u.char_p, identifier_p, identifier_length) == 0); +} /* lexer_literal_object_is_identifier */ + +#endif /* ENABLED (JERRY_ES2015) */ /** * Compares the current identifier or string to an expected string. diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index fec1b2f08..2b3f81353 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -161,9 +161,6 @@ typedef enum LEXER_KEYW_CLASS, /**< class */ LEXER_KEYW_EXTENDS, /**< extends */ LEXER_KEYW_SUPER, /**< super */ -#if ENABLED (JERRY_ES2015) - LEXER_KEYW_LET, /**< let */ -#endif /* ENABLED (JERRY_ES2015) */ LEXER_KEYW_CONST, /**< const */ LEXER_KEYW_EXPORT, /**< export */ LEXER_KEYW_IMPORT, /**< import */ @@ -192,9 +189,7 @@ typedef enum /* Context dependent future strict reserved words: * See also: ECMA-262 v6, 11.6.2.1 */ -#if !ENABLED (JERRY_ES2015) LEXER_KEYW_LET, /**< let */ -#endif /* !ENABLED (JERRY_ES2015) */ LEXER_KEYW_YIELD, /**< yield */ LEXER_KEYW_STATIC, /**< static */ } lexer_token_type_t; diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 8d484286a..61fe519eb 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -2348,7 +2348,7 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ uint8_t assign_opcode = CBC_ASSIGN_SET_IDENT; - if (flags & PARSER_PATTERN_LEXICAL + if (flags & (PARSER_PATTERN_LEXICAL | PARSER_PATTERN_LOCAL) && context_p->lit_object.index < PARSER_REGISTER_START) { assign_opcode = CBC_ASSIGN_LET_CONST; @@ -2410,6 +2410,7 @@ parser_pattern_process_nested_pattern (parser_context_t *context_p, /**< context | PARSER_PATTERN_TARGET_ON_STACK | (flags & (PARSER_PATTERN_BINDING | PARSER_PATTERN_LEXICAL + | PARSER_PATTERN_LOCAL | PARSER_PATTERN_REST_ELEMENT))); if (context_p->next_scanner_info_p->source_p == context_p->source_p) @@ -2458,6 +2459,13 @@ parser_pattern_process_assignment (parser_context_t *context_p, /**< context */ lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + if (flags & PARSER_PATTERN_LEXICAL + && !(context_p->status_flags & PARSER_IS_STRICT) + && lexer_literal_object_is_identifier (context_p, "let", 3)) + { + parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); + } + 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); @@ -2472,7 +2480,6 @@ parser_pattern_process_assignment (parser_context_t *context_p, /**< context */ } context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } - #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) parser_module_append_export_name (context_p); #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 99254f91d..00346df07 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -112,8 +112,9 @@ typedef enum PARSER_PATTERN_TARGET_DEFAULT = (1u << 2), /**< perform default value comparison for assignment target */ PARSER_PATTERN_NESTED_PATTERN = (1u << 3), /**< parse patter inside a pattern */ PARSER_PATTERN_LEXICAL = (1u << 4), /**< pattern is a lexical (let/const) declaration */ - PARSER_PATTERN_REST_ELEMENT = (1u << 5), /**< parse rest array initializer */ - PARSER_PATTERN_ARGUMENTS = (1u << 6), /**< parse arguments binding */ + PARSER_PATTERN_LOCAL = (1u << 5), /**< pattern is a local (catch parameter) declaration */ + PARSER_PATTERN_REST_ELEMENT = (1u << 6), /**< parse rest array initializer */ + PARSER_PATTERN_ARGUMENTS = (1u << 7), /**< parse arguments binding */ } parser_pattern_flags_t; /** @@ -646,9 +647,14 @@ void lexer_convert_push_number_to_push_literal (parser_context_t *context_p); uint16_t lexer_construct_function_object (parser_context_t *context_p, uint32_t extra_status_flags); void lexer_construct_regexp_object (parser_context_t *context_p, bool parse_only); bool lexer_compare_identifiers (const uint8_t *left_p, const uint8_t *right_p, size_t size); -bool lexer_compare_identifier_to_current (parser_context_t *context_p, const lexer_lit_location_t *right_ident_p); -bool lexer_compare_literal_to_identifier (parser_context_t *context_p, const char *identifier_p, - size_t identifier_length); +bool lexer_current_is_literal (parser_context_t *context_p, const lexer_lit_location_t *right_ident_p); +#if ENABLED (JERRY_ES2015) +bool lexer_token_is_identifier (parser_context_t *context_p, const char *identifier_p, + size_t identifier_length); +bool lexer_token_is_let (parser_context_t *context_p); +bool lexer_literal_object_is_identifier (parser_context_t *context_p, const char *identifier_p, + size_t identifier_length); +#endif /* ENABLED (JERRY_ES2015) */ bool lexer_compare_literal_to_string (parser_context_t *context_p, const char *string_p, size_t string_length); uint8_t lexer_convert_binary_lvalue_token_to_binary (uint8_t token); diff --git a/jerry-core/parser/js/js-parser-module.c b/jerry-core/parser/js/js-parser-module.c index e8336ce9c..3541d21ca 100644 --- a/jerry-core/parser/js/js-parser-module.c +++ b/jerry-core/parser/js/js-parser-module.c @@ -381,7 +381,7 @@ parser_module_parse_export_clause (parser_context_t *context_p) /**< parser cont uint16_t export_name_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; lexer_next_token (context_p); - if (lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); @@ -433,7 +433,7 @@ parser_module_parse_export_clause (parser_context_t *context_p) /**< parser cont lexer_next_token (context_p); } - if (lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } @@ -480,7 +480,7 @@ parser_module_parse_import_clause (parser_context_t *context_p) /**< parser cont uint16_t local_name_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; lexer_next_token (context_p); - if (lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); @@ -540,7 +540,7 @@ parser_module_parse_import_clause (parser_context_t *context_p) /**< parser cont lexer_next_token (context_p); } - if (lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 1d70133a0..5eaa3ec8a 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -542,6 +542,13 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ #if ENABLED (JERRY_ES2015) + if (declaration_type != LEXER_KEYW_VAR + && !(context_p->status_flags & PARSER_IS_STRICT) + && lexer_literal_object_is_identifier (context_p, "let", 3)) + { + parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); + } + 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); @@ -1169,32 +1176,65 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ || context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_OF); bool is_for_in = (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN); + end_location = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location; + + scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_get_location (&start_location, context_p); lexer_next_token (context_p); - bool is_let_const = (context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST); - const uint8_t *source_p = context_p->source_p; + uint8_t token_type = LEXER_EOS; + bool has_context = false; + + if (context_p->token.type == LEXER_KEYW_VAR + || context_p->token.type == LEXER_KEYW_LET + || context_p->token.type == LEXER_KEYW_CONST) + { + token_type = context_p->token.type; + has_context = (context_p->token.type != LEXER_KEYW_VAR); + scanner_get_location (&start_location, context_p); + + /* TODO: remove this after the pre-scanner supports strict mode detection. */ + if (context_p->next_scanner_info_p->source_p == context_p->source_p + && context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) + { + scanner_release_next (context_p, sizeof (scanner_info_t)); + } + } + else if (context_p->token.type == LEXER_LITERAL && lexer_token_is_let (context_p)) + { + if (context_p->next_scanner_info_p->source_p == context_p->source_p + && context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) + { + scanner_release_next (context_p, sizeof (scanner_info_t)); + } + else + { + token_type = LEXER_KEYW_LET; + has_context = true; + scanner_get_location (&start_location, context_p); + } + } + + if (has_context && (context_p->next_scanner_info_p->source_p == context_p->source_p)) + { + has_context = parser_push_block_context (context_p, true); + } + + scanner_set_location (context_p, &end_location); #else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN); bool is_for_in = true; scanner_get_location (&start_location, context_p); -#endif /* ENABLED (JERRY_ES2015) */ 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)); +#endif /* ENABLED (JERRY_ES2015) */ + /* The length of both 'in' and 'of' is two. */ const uint8_t *source_end_p = context_p->source_p - 2; - scanner_release_next (context_p, sizeof (scanner_location_info_t)); - -#if ENABLED (JERRY_ES2015) - if (is_let_const && (context_p->next_scanner_info_p->source_p == source_p)) - { - is_let_const = parser_push_block_context (context_p, true); - } -#endif /* ENABLED (JERRY_ES2015) */ - scanner_seek (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); @@ -1219,7 +1259,7 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ for_in_of_statement.start_offset = context_p->byte_code_size; #if ENABLED (JERRY_ES2015) - if (is_let_const) + if (has_context) { parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_CONTEXT); } @@ -1232,9 +1272,19 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ const uint8_t *original_source_end_p = context_p->source_end_p; context_p->source_end_p = source_end_p; scanner_seek (context_p); + +#if ENABLED (JERRY_ES2015) + if (token_type == LEXER_EOS) + { + lexer_next_token (context_p); + } +#else /* !ENABLED (JERRY_ES2015) */ lexer_next_token (context_p); - switch (context_p->token.type) + uint8_t token_type = context_p->token.type; +#endif /* ENABLED (JERRY_ES2015) */ + + switch (token_type) { #if ENABLED (JERRY_ES2015) case LEXER_KEYW_LET: @@ -1245,7 +1295,7 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ #if ENABLED (JERRY_ES2015) if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)) { - bool is_lexical = (context_p->token.type != LEXER_KEYW_VAR); + bool is_var = (token_type == LEXER_KEYW_VAR); parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); @@ -1259,7 +1309,7 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ parser_pattern_flags_t flags = (PARSER_PATTERN_BINDING | PARSER_PATTERN_TARGET_ON_STACK); - if (is_lexical) + if (!is_var) { flags |= PARSER_PATTERN_LEXICAL; } @@ -1290,13 +1340,10 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT : CBC_EXT_FOR_OF_GET_NEXT); #if ENABLED (JERRY_ES2015) - if (literal_index >= PARSER_REGISTER_START) - { - is_let_const = false; - } + JERRY_ASSERT (literal_index < PARSER_REGISTER_START || !has_context); parser_emit_cbc_literal (context_p, - is_let_const ? CBC_ASSIGN_LET_CONST : CBC_ASSIGN_SET_IDENT, + has_context ? CBC_ASSIGN_LET_CONST : CBC_ASSIGN_SET_IDENT, literal_index); #else /* !ENABLED (JERRY_ES2015) */ parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index); @@ -1358,13 +1405,40 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ if (context_p->token.type != LEXER_SEMICOLON) { +#if ENABLED (JERRY_ES2015) + const uint8_t *source_p = context_p->source_p; +#endif /* ENABLED (JERRY_ES2015) */ + switch (context_p->token.type) { #if ENABLED (JERRY_ES2015) + case LEXER_LITERAL: + { + if (!lexer_token_is_let (context_p)) + { + parser_parse_expression_statement (context_p, PARSE_EXPR); + break; + } + + if (context_p->next_scanner_info_p->source_p == context_p->source_p + && context_p->next_scanner_info_p->type != SCANNER_TYPE_BLOCK) + { + if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) + { + scanner_release_next (context_p, sizeof (scanner_info_t)); + } + + parser_parse_expression_statement (context_p, PARSE_EXPR); + break; + } + + context_p->token.type = LEXER_KEYW_LET; + /* FALLTHRU */ + } case LEXER_KEYW_LET: case LEXER_KEYW_CONST: { - if (context_p->next_scanner_info_p->source_p == context_p->source_p) + if (context_p->next_scanner_info_p->source_p == source_p) { parser_push_block_context (context_p, true); } @@ -1849,7 +1923,7 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ { parser_pattern_flags_t flags = (PARSER_PATTERN_BINDING | PARSER_PATTERN_TARGET_ON_STACK - | PARSER_PATTERN_LEXICAL); + | PARSER_PATTERN_LOCAL); parser_parse_initializer_by_next_char (context_p, flags); } @@ -2039,7 +2113,7 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */ parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); - if (lexer_compare_identifier_to_current (context_p, &label_statement.label_ident)) + if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { label_statement.break_list_p = parser_emit_cbc_forward_branch_item (context_p, (uint16_t) opcode, @@ -2126,7 +2200,7 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */ parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); - if (lexer_compare_identifier_to_current (context_p, &label_statement.label_ident)) + if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { parser_loop_statement_t loop; @@ -2253,7 +2327,7 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_EXPECTED); } } - else if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + else if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_COMMA_EXPECTED); } @@ -2263,7 +2337,7 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context { /* NameSpaceImport*/ lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (!lexer_token_is_identifier (context_p, "as", 2)) { parser_raise_error (context_p, PARSER_ERR_AS_EXPECTED); } @@ -2299,7 +2373,7 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context parser_module_parse_import_clause (context_p); } - if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); } @@ -2386,7 +2460,7 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ case LEXER_MULTIPLY: { lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); } @@ -2419,7 +2493,7 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ { parser_module_parse_export_clause (context_p); - if (lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (lexer_token_is_identifier (context_p, "from", 4)) { lexer_next_token (context_p); parser_module_handle_module_specifier (context_p); @@ -2464,7 +2538,7 @@ parser_parse_label (parser_context_t *context_p) /**< context */ parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t)); parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t)); - if (lexer_compare_identifier_to_current (context_p, &label_statement.label_ident)) + if (lexer_current_is_literal (context_p, &label_statement.label_ident)) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_LABEL); } @@ -2929,16 +3003,37 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); break; } + case LEXER_LITERAL: { - if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL - && lexer_check_next_character (context_p, LIT_CHAR_COLON)) + if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { - parser_parse_label (context_p); - lexer_next_token (context_p); - JERRY_ASSERT (context_p->token.type == LEXER_COLON); - lexer_next_token (context_p); - continue; + if (lexer_check_next_character (context_p, LIT_CHAR_COLON)) + { + parser_parse_label (context_p); + lexer_consume_next_character (context_p); + lexer_next_token (context_p); + continue; + } +#if ENABLED (JERRY_ES2015) + if (lexer_token_is_let (context_p)) + { + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION) + { + scanner_release_next (context_p, sizeof (scanner_info_t)); + } + + parser_parse_expression_statement (context_p, PARSE_EXPR); + break; + } + + context_p->token.type = LEXER_KEYW_LET; + parser_parse_var_statement (context_p); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ } /* FALLTHRU */ } diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index ee7361a6a..1036d5138 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1107,6 +1107,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Lexical declaration cannot appear in a single-statement context."; } + case PARSER_ERR_LEXICAL_LET_BINDING: + { + return "Let binding cannot appear in let/const declarations."; + } case PARSER_ERR_MISSING_ASSIGN_AFTER_CONST: { return "Value assignment is expected after a const declaration."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 37a6f3730..ffee4613b 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1686,7 +1686,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ uint32_t flags = (PARSER_PATTERN_BINDING | PARSER_PATTERN_TARGET_ON_STACK - | PARSER_PATTERN_LEXICAL + | PARSER_PATTERN_LOCAL | PARSER_PATTERN_ARGUMENTS); if (context_p->next_scanner_info_p->source_p == context_p->source_p) diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 95f812bc0..f35a6fae7 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -120,7 +120,8 @@ typedef enum PARSER_ERR_OBJECT_PROPERTY_REDEFINED, /**< property of object literal redefined */ #if ENABLED (JERRY_ES2015) PARSER_ERR_VARIABLE_REDECLARED, /**< a variable redeclared */ - PARSER_ERR_LEXICAL_SINGLE_STATEMENT, /**< lexical variable in single statement context */ + PARSER_ERR_LEXICAL_SINGLE_STATEMENT, /**< lexical declaration in single statement context */ + PARSER_ERR_LEXICAL_LET_BINDING, /**< let binding cannot be declared in let/const */ PARSER_ERR_MISSING_ASSIGN_AFTER_CONST, /**< an assignment is required after a const declaration */ PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, /**< multiple class constructor */ diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index cf0c19d01..731b310c5 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -1467,6 +1467,7 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ { #if ENABLED (JERRY_ES2015) JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS + || scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION || scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); #else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 437e04367..0b77aa134 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -133,7 +133,7 @@ typedef enum * Checks whether token type is "of". */ #if ENABLED (JERRY_ES2015) -#define SCANNER_IDENTIFIER_IS_OF() (lexer_compare_literal_to_identifier (context_p, "of", 2)) +#define SCANNER_IDENTIFIER_IS_OF() (lexer_token_is_identifier (context_p, "of", 2)) #else #define SCANNER_IDENTIFIER_IS_OF() (false) #endif /* ENABLED (JERRY_ES2015) */ @@ -1554,10 +1554,15 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_for_statement_t for_statement; for_statement.u.source_p = context_p->source_p; uint8_t stack_mode = SCAN_STACK_FOR_START; + scan_return_types_t return_type = SCAN_KEEP_TOKEN; lexer_next_token (context_p); scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; +#if ENABLED (JERRY_ES2015) + const uint8_t *source_p = context_p->source_p; +#endif /* ENABLED (JERRY_ES2015) */ + switch (context_p->token.type) { case LEXER_SEMICOLON: @@ -1569,20 +1574,62 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; stack_mode = SCAN_STACK_FOR_VAR_START; + return_type = SCAN_NEXT_TOKEN; break; } #if ENABLED (JERRY_ES2015) + case LEXER_LITERAL: + { + if (!lexer_token_is_let (context_p)) + { + break; + } + + parser_line_counter_t line = context_p->line; + parser_line_counter_t column = context_p->column; + + if (lexer_check_arrow (context_p)) + { + context_p->source_p = source_p; + context_p->line = line; + context_p->column = column; + context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; + break; + } + + lexer_next_token (context_p); + + type = (lexer_token_type_t) context_p->token.type; + + if (type != LEXER_LEFT_SQUARE + && type != LEXER_LEFT_BRACE + && (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)) + { + scanner_info_t *info_p = scanner_insert_info (context_p, source_p, sizeof (scanner_info_t)); + info_p->type = SCANNER_TYPE_LET_EXPRESSION; + + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + break; + } + + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + /* FALLTHRU */ + } case LEXER_KEYW_LET: case LEXER_KEYW_CONST: { scanner_literal_pool_t *literal_pool_p; literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_BLOCK); - literal_pool_p->source_p = context_p->source_p; + literal_pool_p->source_p = source_p; - scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + if (scanner_context_p->mode == SCAN_MODE_PRIMARY_EXPRESSION) + { + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + return_type = SCAN_NEXT_TOKEN; + } - stack_mode = ((context_p->token.type == LEXER_KEYW_LET) ? SCAN_STACK_FOR_LET_START - : SCAN_STACK_FOR_CONST_START); + stack_mode = ((context_p->token.type == LEXER_KEYW_CONST) ? SCAN_STACK_FOR_CONST_START + : SCAN_STACK_FOR_LET_START); break; } #endif /* ENABLED (JERRY_ES2015) */ @@ -1590,7 +1637,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); parser_stack_push_uint8 (context_p, stack_mode); - return (stack_mode == SCAN_STACK_FOR_START) ? SCAN_KEEP_TOKEN : SCAN_NEXT_TOKEN; + return return_type; } case LEXER_KEYW_VAR: { @@ -1817,7 +1864,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (!lexer_token_is_identifier (context_p, "as", 2)) { scanner_raise_error (context_p); } @@ -1861,7 +1908,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ { lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (!lexer_token_is_identifier (context_p, "as", 2)) { scanner_raise_error (context_p); } @@ -1921,7 +1968,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ } } - if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (!lexer_token_is_identifier (context_p, "from", 4)) { scanner_raise_error (context_p); } @@ -2042,7 +2089,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (!lexer_token_is_identifier (context_p, "from", 4)) { scanner_raise_error (context_p); } @@ -2072,7 +2119,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); - if (lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (lexer_token_is_identifier (context_p, "as", 2)) { lexer_next_token (context_p); @@ -2098,7 +2145,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); - if (!lexer_compare_literal_to_identifier (context_p, "from", 4)) + if (!lexer_token_is_identifier (context_p, "from", 4)) { return SCAN_KEEP_TOKEN; } @@ -2145,8 +2192,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ { if (JERRY_UNLIKELY (lexer_check_next_character (context_p, LIT_CHAR_COLON))) { - lexer_next_token (context_p); - JERRY_ASSERT (context_p->token.type == LEXER_COLON); + lexer_consume_next_character (context_p); scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } @@ -2161,6 +2207,41 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_process_simple_arrow (context_p, scanner_context_p, context_p->source_p); return SCAN_KEEP_TOKEN; } + + if (JERRY_UNLIKELY (lexer_token_is_let (context_p))) + { + lexer_lit_location_t let_literal = context_p->token.lit_location; + const uint8_t *source_p = context_p->source_p; + + lexer_next_token (context_p); + + type = (lexer_token_type_t) context_p->token.type; + + if (type == LEXER_LEFT_SQUARE + || type == LEXER_LEFT_BRACE + || (type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL)) + { + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + parser_stack_push_uint8 (context_p, SCAN_STACK_LET); + return SCAN_KEEP_TOKEN; + } + + scanner_info_t *info_p = scanner_insert_info (context_p, source_p, sizeof (scanner_info_t)); + info_p->type = SCANNER_TYPE_LET_EXPRESSION; + + lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &let_literal); + lit_location_p->type |= SCANNER_LITERAL_IS_USED; + + if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + lit_location_p->type |= SCANNER_LITERAL_NO_REG; + } + + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return SCAN_KEEP_TOKEN; + } #endif /* ENABLED (JERRY_ES2015) */ scanner_add_reference (context_p, scanner_context_p); @@ -2639,7 +2720,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ break; } - if (lexer_compare_literal_to_identifier (context_p, "static", 6)) + if (lexer_token_is_identifier (context_p, "static", 6)) { lexer_scan_identifier (context_p, LEXER_SCAN_IDENT_NO_OPTS); } @@ -2649,8 +2730,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION; - if (lexer_compare_literal_to_identifier (context_p, "get", 3) - || lexer_compare_literal_to_identifier (context_p, "set", 3)) + if (lexer_token_is_identifier (context_p, "get", 3) + || lexer_token_is_identifier (context_p, "set", 3)) { lexer_scan_identifier (context_p, LEXER_SCAN_IDENT_NO_OPTS); @@ -3560,6 +3641,12 @@ scan_completed: print_location = true; break; } + case SCANNER_TYPE_LET_EXPRESSION: + { + JERRY_DEBUG_MSG (" LET_EXPRESSION: source:%d\n", + (int) (info_p->source_p - source_start_p)); + break; + } case SCANNER_TYPE_ERR_REDECLARED: { JERRY_DEBUG_MSG (" ERR_REDECLARED: source:%d\n", diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index 166929794..3146520d5 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -46,6 +46,7 @@ typedef enum SCANNER_TYPE_CASE, /**< case statement */ #if ENABLED (JERRY_ES2015) SCANNER_TYPE_INITIALIZER, /**< destructuring binding or assignment pattern with initializer */ + SCANNER_TYPE_LET_EXPRESSION, /**< let expression */ SCANNER_TYPE_ERR_REDECLARED, /**< syntax error: a variable is redeclared */ #endif /* ENABLED (JERRY_ES2015) */ } scanner_info_type_t; diff --git a/tests/jerry/es2015/let13.js b/tests/jerry/es2015/let13.js new file mode 100644 index 000000000..862296d09 --- /dev/null +++ b/tests/jerry/es2015/let13.js @@ -0,0 +1,110 @@ +// 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. + +/* Declaring let */ + +/* Note: l\u0065t is let */ + +function check_syntax_error (code) { + try { + eval(code) + assert (false) + } catch (e) { + assert (e instanceof SyntaxError) + } +} + +function check_strict_syntax_error (code) { + "use strict" + + try { + eval(code) + assert (false) + } catch (e) { + assert (e instanceof SyntaxError) + } +} + +check_syntax_error("let let = 5") +check_syntax_error("const [let] = [1]") +check_syntax_error("const l\u0065t = 6") +check_syntax_error("let [l\u0065t] = [2]") +check_syntax_error("l\\u0065t a") +check_strict_syntax_error("var let = 5") +check_strict_syntax_error("function let() {}") +check_strict_syntax_error("for (let in []) ;") +check_strict_syntax_error("l\\u0065t;") + +var let = 1 +assert(let === 1) + +var [let] = [2] +assert(let === 2) + +var x = 0; +let = [ () => x = 1 ] + +l\u0065t[0]() +assert(x === 1) + +function f1() +{ + var a = 0 + + function let(l\u0065t) { + a = let + } + + let(3) + + assert(a === 3) +} +f1() + +function f2() +{ + var let = [1] + + /* First: destructuring pattern definition */ + let + [a] = [2] + + assert(a === 2) + + a = 0 + + /* Second: property access */ + l\u0065t + [a] = [3] + + assert(let[0][0] === 3) +} +f2() + +var arr = [] + +for (let in ["x","y"]) + arr.push(let) + +assert(arr[0] === "0") +assert(arr[1] === "1") + +/* Let and arrow */ + +for (let => 4; false ; ) ; + +let => 5 + +/* Let label */ +let: break let