diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index 10c59ca4d..90d42b34d 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -319,6 +319,76 @@ ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-boun return ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->u1.bound_object_cp); } /* ecma_get_lex_env_binding_object */ +/** + * Create a new lexical environment with the same property list as the passed lexical environment + * + * @return pointer to the newly created lexical environment + */ +ecma_object_t * +ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, /**< declarative lexical environment */ + bool copy_values) /**< copy property values as well */ +{ + JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + + ecma_object_t *outer_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + ecma_object_t *new_lex_env_p = ecma_create_decl_lex_env (outer_lex_env_p); + + jmem_cpointer_t prop_iter_cp = lex_env_p->u1.property_list_cp; + JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL); + + ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, + prop_iter_cp); + if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) + { + prop_iter_cp = prop_iter_p->next_property_cp; + } + + JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL); + + do + { + prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); + + JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); + + ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED) + { + JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (prop_iter_p->types[i]) == ECMA_PROPERTY_TYPE_NAMEDDATA); + + uint8_t prop_attributes = (uint8_t) (prop_iter_p->types[i] & ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); + ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]); + + ecma_property_value_t *property_value_p; + property_value_p = ecma_create_named_data_property (new_lex_env_p, name_p, prop_attributes, NULL); + + ecma_deref_ecma_string (name_p); + + JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); + + if (copy_values) + { + property_value_p->value = ecma_copy_value_if_not_object (prop_pair_p->values[i].value); + } + else + { + property_value_p->value = ECMA_VALUE_UNINITIALIZED; + } + } + } + + prop_iter_cp = prop_iter_p->next_property_cp; + } + while (prop_iter_cp != JMEM_CP_NULL); + + ecma_deref_object (lex_env_p); + return new_lex_env_p; +} /* ecma_clone_decl_lexical_environment */ + /** * Create a property in an object and link it into * the object's properties' linked-list (at start of the list). diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 3b87521fb..6cc334910 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -371,6 +371,7 @@ uint8_t ecma_get_object_builtin_id (ecma_object_t *object_p); ecma_lexical_environment_type_t JERRY_ATTR_PURE ecma_get_lex_env_type (const ecma_object_t *object_p); ecma_object_t JERRY_ATTR_PURE *ecma_get_lex_env_outer_reference (const ecma_object_t *object_p); ecma_object_t JERRY_ATTR_PURE *ecma_get_lex_env_binding_object (const ecma_object_t *object_p); +ecma_object_t *ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, bool copy_values); ecma_property_value_t * ecma_create_named_data_property (ecma_object_t *object_p, ecma_string_t *name_p, uint8_t prop_attributes, diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 828a68d95..95ebb2c05 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -544,6 +544,10 @@ VM_OC_PUSH_LIT_POS_BYTE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_NEG_BYTE, CBC_HAS_LITERAL_ARG | CBC_HAS_BYTE_ARG, 2, \ VM_OC_PUSH_LIT_NEG_BYTE | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_CLONE_CONTEXT, CBC_NO_FLAG, 0, \ + VM_OC_CLONE_CONTEXT) \ + CBC_OPCODE (CBC_EXT_CLONE_FULL_CONTEXT, CBC_NO_FLAG, 0, \ + VM_OC_CLONE_CONTEXT) \ CBC_OPCODE (CBC_EXT_RESOURCE_NAME, CBC_NO_FLAG, 0, \ VM_OC_RESOURCE_NAME) \ CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index ef8c642ae..dfa307e4b 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -58,6 +58,7 @@ typedef enum PARSER_STATEMENT_START, PARSER_STATEMENT_BLOCK, #if ENABLED (JERRY_ES2015) + PARSER_STATEMENT_BLOCK_SCOPE, PARSER_STATEMENT_BLOCK_CONTEXT, #endif /* ENABLED (JERRY_ES2015) */ PARSER_STATEMENT_LABEL, @@ -79,6 +80,7 @@ typedef enum PARSER_STATEMENT_FOR_IN, #if ENABLED (JERRY_ES2015) PARSER_STATEMENT_FOR_OF, + PARSER_STATEMENT_FOR_CONTEXT, #endif /* ENABLED (JERRY_ES2015) */ PARSER_STATEMENT_WITH, PARSER_STATEMENT_TRY, @@ -234,14 +236,13 @@ parser_statement_length (uint8_t type) /**< type of statement */ { static const uint8_t statement_lengths[] = { -#if ENABLED (JERRY_ES2015) /* PARSER_STATEMENT_BLOCK */ + 1, +#if ENABLED (JERRY_ES2015) + /* PARSER_STATEMENT_BLOCK_SCOPE */ (uint8_t) (sizeof (parser_block_statement_t) + 1), /* PARSER_STATEMENT_BLOCK_CONTEXT */ (uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1), -#else /* !ENABLED (JERRY_ES2015) */ - /* PARSER_STATEMENT_BLOCK */ - 1, #endif /* ENABLED (JERRY_ES2015) */ /* PARSER_STATEMENT_LABEL */ (uint8_t) (sizeof (parser_label_statement_t) + 1), @@ -268,6 +269,8 @@ parser_statement_length (uint8_t type) /**< type of statement */ #if ENABLED (JERRY_ES2015) /* PARSER_STATEMENT_FOR_OF */ (uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1), + /* PARSER_STATEMENT_FOR_CONTEXT */ + 1, #endif /* ENABLED (JERRY_ES2015) */ /* PARSER_STATEMENT_WITH */ (uint8_t) (sizeof (parser_with_statement_t) + 1), @@ -342,6 +345,85 @@ parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */ lexer_next_token (context_p); } /* parser_parse_enclosed_expr */ +#if ENABLED (JERRY_ES2015) + +/** + * Create a block context. + * + * @return true - when a context is created, false - otherwise + */ +static bool +parser_push_block_context (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); + + parser_block_statement_t block_statement; + block_statement.scope_stack_top = context_p->scope_stack_top; + block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + + bool is_context_needed = false; + + if (scanner_is_context_needed (context_p)) + { + parser_block_context_t block_context; + +#ifndef JERRY_NDEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_emit_cbc_forward_branch (context_p, + CBC_BLOCK_CREATE_CONTEXT, + &block_context.branch); + parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t)); + is_context_needed = true; + } + + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); + parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); + parser_stack_push_uint8 (context_p, (is_context_needed ? PARSER_STATEMENT_BLOCK_CONTEXT + : PARSER_STATEMENT_BLOCK_SCOPE)); + + return is_context_needed; +} /* parser_push_block_context */ + +/** + * Pop block context. + */ +static void +parser_pop_block_context (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE + || context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT); + + uint8_t type = context_p->stack_top_uint8; + + parser_block_statement_t block_statement; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); + + context_p->scope_stack_top = block_statement.scope_stack_top; + context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; + + if (type == PARSER_STATEMENT_BLOCK_SCOPE) + { + return; + } + + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#ifndef JERRY_NDEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_block_context_t block_context; + parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t)); + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_set_branch_to_current_position (context_p, &block_context.branch); +} /* parser_pop_block_context */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Parse var statement. */ @@ -980,18 +1062,33 @@ 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); + + 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; #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_get_location (&start_location, context_p); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); /* 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); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_CONTEXT); + } +#endif /* ENABLED (JERRY_ES2015) */ + scanner_seek (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); @@ -1015,6 +1112,13 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); for_in_of_statement.start_offset = context_p->byte_code_size; +#if ENABLED (JERRY_ES2015) + if (is_let_const) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_CONTEXT); + } +#endif /* ENABLED (JERRY_ES2015) */ + /* The expression parser must not read the 'in' or 'of' tokens. */ scanner_get_location (&end_location, context_p); scanner_set_location (context_p, &start_location); @@ -1024,52 +1128,72 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ scanner_seek (context_p); lexer_next_token (context_p); - if (context_p->token.type == LEXER_KEYW_VAR) + switch (context_p->token.type) { - uint16_t literal_index; - - 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); - - literal_index = context_p->lit_object.index; - - lexer_next_token (context_p); - - if (context_p->token.type == LEXER_ASSIGN) +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_LET: + case LEXER_KEYW_CONST: +#endif /* ENABLED (JERRY_ES2015) */ + case LEXER_KEYW_VAR: { - parser_branch_t branch; + uint16_t literal_index; + + 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); + + literal_index = context_p->lit_object.index; - /* Initialiser is never executed. */ - parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch); lexer_next_token (context_p); - parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA); - parser_set_branch_to_current_position (context_p, &branch); + + if (context_p->token.type == LEXER_ASSIGN) + { + parser_branch_t branch; + + /* Initialiser is never executed. */ + parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch); + lexer_next_token (context_p); + parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA); + parser_set_branch_to_current_position (context_p, &branch); + } + + 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; + } + + parser_emit_cbc_literal (context_p, + is_let_const ? 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); +#endif /* ENABLED (JERRY_ES2015) */ + break; } + default: + { + uint16_t opcode; - parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT - : CBC_EXT_FOR_OF_GET_NEXT); - parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index); - } - else - { - uint16_t opcode; + parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE); - parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE); + opcode = context_p->last_cbc_opcode; - opcode = context_p->last_cbc_opcode; + /* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */ + JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS + && opcode != CBC_PUSH_THREE_LITERALS); - /* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */ - JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS - && opcode != CBC_PUSH_THREE_LITERALS); + opcode = parser_check_left_hand_side_expression (context_p, opcode); - opcode = parser_check_left_hand_side_expression (context_p, opcode); + parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT + : CBC_EXT_FOR_OF_GET_NEXT); + parser_flush_cbc (context_p); - parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT - : CBC_EXT_FOR_OF_GET_NEXT); - parser_flush_cbc (context_p); - - context_p->last_cbc_opcode = opcode; + context_p->last_cbc_opcode = opcode; + break; + } } if (context_p->token.type != LEXER_EOS) @@ -1104,13 +1228,30 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ if (context_p->token.type != LEXER_SEMICOLON) { - if (context_p->token.type == LEXER_KEYW_VAR) + switch (context_p->token.type) { - parser_parse_var_statement (context_p); - } - else - { - parser_parse_expression_statement (context_p, PARSE_EXPR); +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_LET: + case LEXER_KEYW_CONST: + { + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + parser_push_block_context (context_p); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_CONTEXT); + } + /* FALLTHRU */ + } +#endif /* ENABLED (JERRY_ES2015) */ + case LEXER_KEYW_VAR: + { + parser_parse_var_statement (context_p); + break; + } + default: + { + parser_parse_expression_statement (context_p, PARSE_EXPR); + break; + } } if (context_p->token.type != LEXER_SEMICOLON) @@ -1194,6 +1335,24 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */ parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t)); parser_stack_iterator_read (&iterator, &for_statement, sizeof (parser_for_statement_t)); +#if ENABLED (JERRY_ES2015) + bool has_block_context = false; + uint8_t next_statement_type; + + parser_stack_iterator_skip (&iterator, sizeof (parser_for_statement_t)); + parser_stack_iterator_read (&iterator, &next_statement_type, 1); + if (next_statement_type == PARSER_STATEMENT_FOR_CONTEXT) + { + parser_stack_iterator_skip (&iterator, 1); + parser_stack_iterator_read (&iterator, &next_statement_type, 1); + + if (next_statement_type == PARSER_STATEMENT_BLOCK_CONTEXT) + { + has_block_context = true; + } + } +#endif + scanner_get_location (&location, context_p); current_token = context_p->token; @@ -1203,6 +1362,13 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */ parser_set_continues_to_current_position (context_p, loop.branch_list_p); +#if ENABLED (JERRY_ES2015) + if (has_block_context) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_FULL_CONTEXT); + } +#endif + if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_parse_expression_statement (context_p, PARSE_EXPR); @@ -1251,6 +1417,15 @@ parser_parse_for_statement_end (parser_context_t *context_p) /**< context */ parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, for_statement.start_offset); parser_set_breaks_to_current_position (context_p, loop.branch_list_p); +#if ENABLED (JERRY_ES2015) + if (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_CONTEXT) + { + parser_stack_pop_uint8 (context_p); + parser_pop_block_context (context_p); + parser_stack_iterator_init (context_p, &context_p->last_statement); + } +#endif + /* Calling scanner_seek is unnecessary because all * info blocks inside the for statement should be processed. */ scanner_set_location (context_p, &location); @@ -1324,13 +1499,6 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context * parser_emit_cbc (context_p, CBC_POP); parser_flush_cbc (context_p); -#if ENABLED (JERRY_ES2015) - parser_block_statement_t block_statement; - block_statement.scope_stack_top = context_p->scope_stack_top; - block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; - parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); -#endif /* ENABLED (JERRY_ES2015) */ - parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); parser_stack_iterator_init (context_p, &context_p->last_statement); return; @@ -2483,39 +2651,19 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ case LEXER_LEFT_BRACE: { - uint8_t block_type = PARSER_STATEMENT_BLOCK; - #if ENABLED (JERRY_ES2015) - parser_block_statement_t block_statement; - block_statement.scope_stack_top = context_p->scope_stack_top; - block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; - if (context_p->next_scanner_info_p->source_p == context_p->source_p) { - JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); - - if (scanner_is_context_needed (context_p)) - { - parser_block_context_t block_context; - -#ifndef JERRY_NDEBUG - PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); -#endif /* !JERRY_NDEBUG */ - - parser_emit_cbc_forward_branch (context_p, - CBC_BLOCK_CREATE_CONTEXT, - &block_context.branch); - parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t)); - block_type = PARSER_STATEMENT_BLOCK_CONTEXT; - } - - scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); + parser_push_block_context (context_p); } - - parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); + else + { + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); + } +#else /* !ENABLED (JERRY_ES2015) */ + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); #endif /* ENABLED (JERRY_ES2015) */ - parser_stack_push_uint8 (context_p, block_type); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); continue; @@ -2810,38 +2958,14 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK) { parser_stack_pop_uint8 (context_p); - -#if ENABLED (JERRY_ES2015) - parser_block_statement_t block_statement; - - parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); - context_p->scope_stack_top = block_statement.scope_stack_top; - context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; -#endif /* ENABLED (JERRY_ES2015) */ - parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); } #if ENABLED (JERRY_ES2015) - else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT) + else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE + || context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT) { - parser_block_statement_t block_statement; - parser_block_context_t block_context; - - parser_stack_pop_uint8 (context_p); - parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); - parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t)); - - context_p->scope_stack_top = block_statement.scope_stack_top; - context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; - - PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); -#ifndef JERRY_NDEBUG - PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); -#endif /* !JERRY_NDEBUG */ - - parser_emit_cbc (context_p, CBC_CONTEXT_END); - parser_set_branch_to_current_position (context_p, &block_context.branch); + parser_pop_block_context (context_p); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); @@ -3038,6 +3162,15 @@ consume_last_statement: parser_set_breaks_to_current_position (context_p, loop.branch_list_p); parser_set_branch_to_current_position (context_p, &for_in_of_statement.branch); + +#if ENABLED (JERRY_ES2015) + if (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_CONTEXT) + { + parser_stack_pop_uint8 (context_p); + parser_pop_block_context (context_p); + parser_stack_iterator_init (context_p, &context_p->last_statement); + } +#endif /* ENABLED (JERRY_ES2015) */ continue; } diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 789beb50d..de48ba13c 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -80,6 +80,10 @@ typedef enum /* The SCANNER_IS_FOR_START macro needs to be updated when the following constants are reordered. */ SCAN_STACK_VAR, /**< var statement */ SCAN_STACK_FOR_VAR_START, /**< start of "for" iterator with var statement */ +#if ENABLED (JERRY_ES2015) + SCAN_STACK_FOR_LET_START, /**< start of "for" iterator with let statement */ + SCAN_STACK_FOR_CONST_START, /**< start of "for" iterator with const statement */ +#endif /* ENABLED (JERRY_ES2015) */ SCAN_STACK_FOR_START, /**< start of "for" iterator */ SCAN_STACK_FOR_CONDITION, /**< condition part of "for" iterator */ SCAN_STACK_FOR_EXPRESSION, /**< expression part of "for" iterator */ @@ -93,6 +97,7 @@ typedef enum #if ENABLED (JERRY_ES2015) SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */ SCAN_STACK_TEMPLATE_STRING, /**< template string */ + SCAN_STACK_FOR_BLOCK_END, /**< end of "for" statement with let/const declaration */ SCAN_STACK_ARROW_ARGUMENTS, /**< might be arguments of an arrow function */ SCAN_STACK_ARROW_EXPRESSION, /**< expression body of an arrow function */ SCAN_STACK_CLASS_STATEMENT, /**< class statement */ @@ -517,6 +522,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * case SCAN_STACK_CONST: #endif /* ENABLED (JERRY_ES2015) */ case SCAN_STACK_FOR_VAR_START: +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_FOR_LET_START: + case SCAN_STACK_FOR_CONST_START: +#endif /* ENABLED (JERRY_ES2015) */ { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; return SCAN_NEXT_TOKEN; @@ -540,7 +549,9 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * } JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET - || context_p->stack_top_uint8 == SCAN_STACK_CONST); + || context_p->stack_top_uint8 == SCAN_STACK_CONST + || context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START + || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START); scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; return SCAN_NEXT_TOKEN; @@ -670,6 +681,13 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; } + if (context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START + || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START) + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + return SCAN_KEEP_TOKEN; + } + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET || context_p->stack_top_uint8 == SCAN_STACK_CONST); /* FALLTHRU */ @@ -689,6 +707,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * return SCAN_KEEP_TOKEN; } case SCAN_STACK_FOR_VAR_START: +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_FOR_LET_START: + case SCAN_STACK_FOR_CONST_START: +#endif /* ENABLED (JERRY_ES2015) */ case SCAN_STACK_FOR_START: { if (type == LEXER_KEYW_IN || SCANNER_IDENTIFIER_IS_OF ()) @@ -704,6 +726,11 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * sizeof (scanner_location_info_t)); #if ENABLED (JERRY_ES2015) location_info->info.type = (type == LEXER_KEYW_IN) ? SCANNER_TYPE_FOR_IN : SCANNER_TYPE_FOR_OF; + + if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_BLOCK_END); + } #else /* !ENABLED (JERRY_ES2015) */ location_info->info.type = SCANNER_TYPE_FOR_IN; #endif /* ENABLED (JERRY_ES2015) */ @@ -725,6 +752,13 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * parser_stack_pop_uint8 (context_p); parser_stack_pop (context_p, NULL, sizeof (scanner_for_statement_t)); +#if ENABLED (JERRY_ES2015) + if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_BLOCK_END); + } +#endif /* ENABLED (JERRY_ES2015) */ + for_statement.u.source_p = context_p->source_p; parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_CONDITION); @@ -1147,6 +1181,21 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ stack_mode = SCAN_STACK_FOR_VAR_START; break; } +#if ENABLED (JERRY_ES2015) + 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; + + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + + stack_mode = ((context_p->token.type == LEXER_KEYW_LET) ? SCAN_STACK_FOR_LET_START + : SCAN_STACK_FOR_CONST_START); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ } parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t)); @@ -1903,6 +1952,14 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ terminator_found = true; continue; } +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_FOR_BLOCK_END: + { + parser_stack_pop_uint8 (context_p); + scanner_pop_literal_pool (context_p, scanner_context_p); + continue; + } +#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_TRY_STATEMENT @@ -2236,16 +2293,17 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context); #if ENABLED (JERRY_ES2015) - if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST) + if (stack_top != SCAN_STACK_VAR && stack_top != SCAN_STACK_FOR_VAR_START) { scanner_detect_invalid_let (context_p, literal_p); - if (stack_top == SCAN_STACK_LET) + if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START) { literal_p->type |= SCANNER_LITERAL_IS_LET; } else { + JERRY_ASSERT (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START); literal_p->type |= SCANNER_LITERAL_IS_CONST; } diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 3a8556f72..c82249248 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1335,6 +1335,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } continue; } + case VM_OC_CLONE_CONTEXT: + { + JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); + + bool copy_values = (byte_code_start_p[1] == CBC_EXT_CLONE_FULL_CONTEXT); + frame_ctx_p->lex_env_p = ecma_clone_decl_lexical_environment (frame_ctx_p->lex_env_p, copy_values); + continue; + } case VM_OC_SET_COMPUTED_PROPERTY: { /* Swap values. */ @@ -3025,6 +3033,13 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ stack_top_p[-3] = 0; stack_top_p[-4] = expr_obj_value; +#if ENABLED (JERRY_ES2015) + if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) + { + /* No need to duplicate the first context. */ + byte_code_p += 2; + } +#endif /* ENABLED (JERRY_ES2015) */ continue; } case VM_OC_FOR_IN_GET_NEXT: @@ -3124,6 +3139,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ stack_top_p[-2] = iterator_step; stack_top_p[-3] = iterator; + if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT) + { + /* No need to duplicate the first context. */ + byte_code_p += 2; + } continue; } case VM_OC_FOR_OF_GET_NEXT: diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 400caf613..a9ee58c31 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -226,6 +226,7 @@ typedef enum #endif /* ENABLED (JERRY_LINE_INFO) */ #if ENABLED (JERRY_ES2015) VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ + VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ VM_OC_FOR_OF_CREATE_CONTEXT, /**< for of create context */ @@ -265,6 +266,7 @@ typedef enum #endif /* !ENABLED (JERRY_LINE_INFO) */ #if !ENABLED (JERRY_ES2015) VM_OC_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to let/const declarations */ + VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */ VM_OC_BLOCK_CREATE_CONTEXT = VM_OC_NONE, /**< create context for blocks enclosed in braces */ diff --git a/tests/jerry/es2015/for-let.js b/tests/jerry/es2015/for-let.js new file mode 100644 index 000000000..0967d947c --- /dev/null +++ b/tests/jerry/es2015/for-let.js @@ -0,0 +1,105 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var arr = []; +for (let i = 0, j = 0; i < 10; j++) +{ + arr[i] = function() { return i + j; } + i++; +} + +for (let i = 0; i < 10; i++) + assert(arr[i]() === (i * 2 + 1)); + +var j = 0, k = 0; +for (let i = 0; j = i, i < 10; i++) +{ + let i = -3; + assert(i === -3); + assert(j === k); + k++; +} + +var j = 0, k = 0; +for (let i = 0; eval("j = i"), i < 10; i++) +{ + let i = -3; + assert(i === -3); + assert(j === k); + k++; +} + +var arr = []; +for (let i in { x:1, y:1, z:1 }) +{ + let str = "P"; + arr.push(function () { return str + i; }); +} + +assert(arr[0]() === "Px"); +assert(arr[1]() === "Py"); +assert(arr[2]() === "Pz"); + +try { + for (let i in (function() { return i; })()) {} + assert(false); +} catch (e) { + assert(e instanceof ReferenceError); +} + +try { + for (let i = 0, j = 0; i < 5; i++, j++) + { + if (i === 3) + { + eval("throw -42") + } + } + assert(false); +} catch (e) { + assert(e === -42); +} + +exit: { + for (let i = 0, j = 0; i < 5; i++, j++) + { + if (eval("i === 3")) { + assert(i === 3); + break exit; + } + } + assert(false); +} + +var f = null, g = null, h = null; + +for (let i = 0; + f = function() { return i }, i < 1; + i++, g = function() { return i }) +{ + h = function() { return i }; +} +assert(f() === 1); +assert(g() === 1); +assert(h() === 0); + +var arr = []; +for (const i in { aa:4, bb:5, cc:6 }) +{ + arr.push(function () { return i }); +} + +assert(arr[0]() === "aa"); +assert(arr[1]() === "bb"); +assert(arr[2]() === "cc");