diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 41a549e74..31c8b88b8 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 (37u) +#define JERRY_SNAPSHOT_VERSION (38u) /** * 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 922ddc06f..5630a1a86 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -328,6 +328,8 @@ VM_OC_RETURN | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_SET_LITERAL_PROPERTY, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_SET_PROPERTY | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ + CBC_OPCODE (CBC_COPY_TO_GLOBAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_COPY_TO_GLOBAL | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_BREAKPOINT_ENABLED, CBC_NO_FLAG, 0, \ VM_OC_BREAKPOINT_ENABLED) \ CBC_OPCODE (CBC_BREAKPOINT_DISABLED, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index e50ad141e..2f591affc 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -2206,14 +2206,7 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ { scope_stack_p--; -#if ENABLED (JERRY_ES2015) - bool cond = (scope_stack_p->map_from == literal_index - && scope_stack_p->map_to != PARSER_SCOPE_STACK_FUNC); -#else /* ENABLED (JERRY_ES2015) */ - bool cond = (scope_stack_p->map_from == literal_index); -#endif /* ENABLED (JERRY_ES2015) */ - - if (cond) + if (scope_stack_p->map_from == literal_index) { JERRY_ASSERT (scanner_decode_map_to (scope_stack_p) >= PARSER_REGISTER_START || (literal_p->status_flags & LEXER_FLAG_USED)); diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 39e1b0467..9d46b64c3 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -73,11 +73,12 @@ typedef enum PARSER_CLASS_STATIC_FUNCTION = (1u << 22), /**< this function is a static class method */ PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 23), /**< super property call or assignment */ PARSER_IS_EVAL = (1u << 24), /**< eval code */ + PARSER_IS_DIRECT_EVAL = (1u << 25), /**< direct eval code */ #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - PARSER_IS_MODULE = (1u << 25), /**< an export / import keyword is encountered */ - PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 26), /**< parsing a function or class default export */ - PARSER_MODULE_STORE_IDENT = (1u << 27), /**< store identifier of the current export statement */ + PARSER_IS_MODULE = (1u << 26), /**< an export / import keyword is encountered */ + PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 27), /**< parsing a function or class default export */ + PARSER_MODULE_STORE_IDENT = (1u << 28), /**< store identifier of the current export statement */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ PARSER_HAS_LATE_LIT_INIT = (1u << 30), /**< there are identifier or string literals which construction * is postponed after the local parser data is freed */ @@ -361,10 +362,7 @@ typedef struct * When map_from == PARSER_SCOPE_STACK_FUNC: * map_to represents the literal reserved for a function literal * Note: the name of the function is the previous value in the scope stack - * - * When map_to == PARSER_SCOPE_STACK_FUNC: - * map_from represents the name of the function literal following this literal - * Note: only the name, the real mapping is somewhere else in the scope stack + * Note: map_to is not encoded in this case */ #define PARSER_SCOPE_STACK_FUNC 0xffff @@ -726,6 +724,7 @@ void scanner_cleanup (parser_context_t *context_p); bool scanner_is_context_needed (parser_context_t *context_p); #if ENABLED (JERRY_ES2015) bool scanner_is_global_context_needed (parser_context_t *context_p); +bool scanner_scope_find_let_declaration (parser_context_t *context_p, lexer_lit_location_t *literal_p); bool scanner_try_scan_new_target (parser_context_t *context_p); #endif /* ENABLED (JERRY_ES2015) */ void scanner_create_variables (parser_context_t *context_p, uint32_t option_flags); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 33be9acee..0f20561ee 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -711,6 +711,66 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ JERRY_ASSERT (scope_stack_p[1].map_from == PARSER_SCOPE_STACK_FUNC); +#if ENABLED (JERRY_ES2015) + if (!(context_p->status_flags & PARSER_IS_STRICT)) + { + bool copy_value = true; + + parser_scope_stack_t *stack_p = context_p->scope_stack_p; + + while (stack_p < scope_stack_p) + { + if (literal_index == stack_p->map_from + && (stack_p->map_to & PARSER_SCOPE_STACK_IS_LEXICAL)) + { + copy_value = false; + break; + } + stack_p++; + } + + if (copy_value) + { + if (context_p->status_flags & PARSER_IS_DIRECT_EVAL) + { + if (!scanner_scope_find_let_declaration (context_p, &context_p->token.lit_location)) + { + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; + parser_emit_cbc_literal_value (context_p, + CBC_COPY_TO_GLOBAL, + scanner_decode_map_to (scope_stack_p), + literal_index); + } + } + else + { + stack_p = context_p->scope_stack_p; + + while (stack_p < scope_stack_p) + { + if (literal_index == stack_p->map_from) + { + JERRY_ASSERT (!(stack_p->map_to & PARSER_SCOPE_STACK_IS_LEXICAL)); + + uint16_t map_to = scanner_decode_map_to (stack_p); + uint16_t opcode = ((map_to >= PARSER_REGISTER_START) ? CBC_ASSIGN_LITERAL_SET_IDENT + : CBC_COPY_TO_GLOBAL); + + parser_emit_cbc_literal_value (context_p, + opcode, + scanner_decode_map_to (scope_stack_p), + map_to); + break; + } + stack_p++; + } + } + + parser_flush_cbc (context_p); + } + } +#endif /* ENABLED (JERRY_ES2015) */ + lexer_literal_t *literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to); JERRY_ASSERT ((literal_p->type == LEXER_UNUSED_LITERAL || literal_p->type == LEXER_FUNCTION_LITERAL) diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index b198d4cce..bad4d77a9 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -110,8 +110,7 @@ parser_print_literal (parser_context_t *context_p, /**< context */ break; } } - else if (scope_stack_end_p->map_to != PARSER_SCOPE_STACK_FUNC - && literal_index == scanner_decode_map_to (scope_stack_end_p)) + else if (literal_index == scanner_decode_map_to (scope_stack_end_p)) { in_scope_literal = true; break; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 665e74800..e8d819f78 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1991,10 +1991,14 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ #if ENABLED (JERRY_ES2015) - if (parse_opts & ECMA_PARSE_DIRECT_EVAL) + if (parse_opts & ECMA_PARSE_EVAL) { context.status_flags |= PARSER_IS_EVAL; } + if (parse_opts & ECMA_PARSE_DIRECT_EVAL) + { + context.status_flags |= PARSER_IS_DIRECT_EVAL; + } if (parse_opts & ECMA_PARSE_FUNCTION) { context.status_flags |= PARSER_IS_FUNCTION; @@ -2048,12 +2052,6 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.status_flags |= PARSER_IS_STRICT; } - if (parse_opts & ECMA_PARSE_EVAL) - { - /* After this point this flag is set for non-direct evals as well. */ - context.status_flags |= PARSER_IS_EVAL; - } - context.module_current_node_p = NULL; #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ @@ -2110,7 +2108,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } #endif /* ENABLED (JERRY_ES2015) */ - scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_EVAL); + scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_SCRIPT); } parser_parse_statements (&context); diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index 6460ec848..d29970848 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -160,8 +160,6 @@ typedef enum #if ENABLED (JERRY_ES2015) /** literal is a destructured argument binding */ SCANNER_LITERAL_IS_DESTRUCTURED_ARG = SCANNER_LITERAL_IS_CONST, - /** literal is a local function */ - SCANNER_LITERAL_IS_FUNC_LOCAL = SCANNER_LITERAL_IS_CONST, SCANNER_LITERAL_IS_USED = (1 << 6), /**< literal is used */ #endif /* ENABLED (JERRY_ES2015) */ } scanner_literal_type_flags_t; @@ -170,12 +168,10 @@ typedef enum * Known combinations: * * SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_DECLARATION : - * function declared in this block, might be let or var - * SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_LOCAL : - * function is visible only in this block + * function declared in this block * SCANNER_LITERAL_IS_LOCAL : * module import on global scope, catch block variable otherwise - * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC : + * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC : * a function argument which is reassigned to a function later * SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_DESTRUCTURED_ARG : * destructured binding argument @@ -346,7 +342,6 @@ lexer_lit_location_t *scanner_add_literal (parser_context_t *context_p, scanner_ void scanner_add_reference (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_append_argument (parser_context_t *context_p, scanner_context_t *scanner_context_p); #if ENABLED (JERRY_ES2015) -bool scanner_scope_find_let_declaration (parser_context_t *context_p, lexer_lit_location_t *literal_p); void scanner_detect_invalid_var (parser_context_t *context_p, scanner_context_t *scanner_context_p, lexer_lit_location_t *var_literal_p); void scanner_detect_invalid_let (parser_context_t *context_p, lexer_lit_location_t *let_literal_p); diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index 35eb9ed24..9b1aa06f3 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -150,9 +150,6 @@ scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: #endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_FUNC: -#if ENABLED (JERRY_ES2015) - case SCANNER_STREAM_TYPE_FUNC_LOCAL: -#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) case SCANNER_STREAM_TYPE_IMPORT: #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ @@ -474,9 +471,19 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ bool search_arguments = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0; bool arguments_required = (no_reg && search_arguments); #if ENABLED (JERRY_ES2015) - bool no_var_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_VAR_REG) != 0; + uint8_t no_var_flags = 0; + + if (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_VAR_REG) + { + no_var_flags = SCANNER_LITERAL_IS_VAR; + + if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL)) + { + no_var_flags |= SCANNER_LITERAL_IS_FUNC; + } + } #else /* !ENABLED (JERRY_ES2015) */ - bool no_var_reg = false; + uint8_t no_var_flags = 0; #endif /* ENABLED (JERRY_ES2015) */ if (no_reg && prev_literal_pool_p != NULL) @@ -527,6 +534,14 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015) if (is_function && (type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LOCAL)) == SCANNER_LITERAL_IS_FUNC) { + if (prev_literal_pool_p == NULL + && (context_p->status_flags & PARSER_IS_DIRECT_EVAL) + && scanner_scope_find_let_declaration (context_p, literal_p)) + { + literal_p->type = 0; + continue; + } + type = (uint8_t) ((type & ~SCANNER_LITERAL_IS_FUNC) | SCANNER_LITERAL_IS_VAR); literal_p->type = type; } @@ -550,7 +565,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ no_declarations++; } - if (no_reg || (no_var_reg && (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)))) + if (no_reg || (type & no_var_flags)) { type |= SCANNER_LITERAL_NO_REG; literal_p->type = type; @@ -573,13 +588,9 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ prev_source_p = literal_p->char_p + literal_p->length; -#if ENABLED (JERRY_ES2015) - const uint8_t local_function_flags = SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_CONST; -#endif /* ENABLED (JERRY_ES2015) */ - if (is_function #if ENABLED (JERRY_ES2015) - || (type & local_function_flags) == local_function_flags + || ((type & SCANNER_LITERAL_IS_FUNC) && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IS_STRICT)) #endif /* ENABLED (JERRY_ES2015) */ || !(type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC))) { @@ -731,12 +742,6 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015) */ } -#if ENABLED (JERRY_ES2015) - else if (literal_p->type & SCANNER_LITERAL_IS_FUNC_LOCAL) - { - type = SCANNER_STREAM_TYPE_FUNC_LOCAL; - } -#endif /* ENABLED (JERRY_ES2015) */ } else if (literal_p->type & SCANNER_LITERAL_IS_ARG) { @@ -1302,7 +1307,7 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ } } - if ((context_p->status_flags & PARSER_IS_EVAL) + if ((context_p->status_flags & PARSER_IS_DIRECT_EVAL) && scanner_scope_find_let_declaration (context_p, var_literal_p)) { scanner_raise_redeclaration_error (context_p); @@ -1530,43 +1535,6 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ context_p->active_scanner_info_p = NULL; } /* scanner_cleanup */ -#if ENABLED (JERRY_ES2015) - -/** - * Finds the literal id of a function if its target is a var declaration - * - * @return function id - if the target of a function is a var declaration, - * negative value - otherwise - */ -static int32_t -scanner_get_function_target (parser_context_t *context_p) /**< context */ -{ - uint16_t literal_index = context_p->lit_object.index; - parser_scope_stack_t *scope_stack_start_p = context_p->scope_stack_p; - parser_scope_stack_t *scope_stack_p = scope_stack_start_p + context_p->scope_stack_top; - - while (scope_stack_p > scope_stack_start_p) - { - scope_stack_p--; - - if (scope_stack_p->map_from == literal_index - && scope_stack_p->map_to != PARSER_SCOPE_STACK_FUNC) - { - if ((scope_stack_p - scope_stack_start_p) >= context_p->scope_stack_global_end - || !(context_p->lit_object.literal_p->status_flags & LEXER_FLAG_GLOBAL)) - { - return -1; - } - - return scanner_decode_map_to (scope_stack_p); - } - } - - return -1; -} /* scanner_get_function_target */ - -#endif /* ENABLED (JERRY_ES2015) */ - /** * Checks whether a context needs to be created for a block. * @@ -1578,18 +1546,11 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ { scanner_info_t *info_p = context_p->next_scanner_info_p; const uint8_t *data_p = ((const uint8_t *) info_p) + sizeof (scanner_info_t); -#if ENABLED (JERRY_ES2015) - lexer_lit_location_t literal; -#endif /* ENABLED (JERRY_ES2015) */ JERRY_ASSERT (info_p->type == SCANNER_TYPE_BLOCK); uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top; -#if ENABLED (JERRY_ES2015) - literal.char_p = info_p->source_p - 1; -#endif /* ENABLED (JERRY_ES2015) */ - while (data_p[0] != SCANNER_STREAM_TYPE_END) { uint32_t type = data_p[0] & SCANNER_STREAM_TYPE_MASK; @@ -1599,8 +1560,7 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL - || type == SCANNER_STREAM_TYPE_FUNC - || type == SCANNER_STREAM_TYPE_FUNC_LOCAL); + || type == SCANNER_STREAM_TYPE_FUNC); #else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR); #endif /* ENABLED (JERRY_ES2015) */ @@ -1611,52 +1571,18 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ { if (data_p[2] != 0) { -#if ENABLED (JERRY_ES2015) - literal.char_p += data_p[2]; -#endif /* ENABLED (JERRY_ES2015) */ length = 2 + 1; } else { -#if ENABLED (JERRY_ES2015) - memcpy (&literal.char_p, data_p + 2 + 1, sizeof (const uint8_t *)); -#endif /* ENABLED (JERRY_ES2015) */ length = 2 + 1 + sizeof (const uint8_t *); } } else { -#if ENABLED (JERRY_ES2015) - int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; - - if (diff <= UINT8_MAX) - { - diff = -diff; - } - - literal.char_p += diff; -#endif /* ENABLED (JERRY_ES2015) */ length = 2 + 2; } -#if ENABLED (JERRY_ES2015) - if (type == SCANNER_STREAM_TYPE_FUNC) - { - literal.length = data_p[1]; - literal.type = LEXER_IDENT_LITERAL; - literal.has_escape = (data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? 1 : 0; - - lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL); - - if (scanner_get_function_target (context_p) >= 0) - { - literal.char_p += data_p[1]; - data_p += length; - continue; - } - } -#endif /* ENABLED (JERRY_ES2015) */ - if (!(data_p[0] & SCANNER_STREAM_NO_REG) && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { @@ -1667,9 +1593,6 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ return true; } -#if ENABLED (JERRY_ES2015) - literal.char_p += data_p[1]; -#endif /* ENABLED (JERRY_ES2015) */ data_p += length; } @@ -1704,11 +1627,11 @@ scanner_is_global_context_needed (parser_context_t *context_p) /**< context */ || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_FUNC - || type == SCANNER_STREAM_TYPE_FUNC_LOCAL || type == SCANNER_STREAM_TYPE_IMPORT); /* Only let/const can be stored in registers */ JERRY_ASSERT ((data & SCANNER_STREAM_NO_REG) + || (type == SCANNER_STREAM_TYPE_FUNC && (context_p->status_flags & PARSER_IS_DIRECT_EVAL)) || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST); @@ -1729,7 +1652,7 @@ scanner_is_global_context_needed (parser_context_t *context_p) /**< context */ } if (type == SCANNER_STREAM_TYPE_VAR - || type == SCANNER_STREAM_TYPE_FUNC + || (type == SCANNER_STREAM_TYPE_FUNC && !(context_p->status_flags & PARSER_IS_DIRECT_EVAL)) || type == SCANNER_STREAM_TYPE_IMPORT) { continue; @@ -1981,9 +1904,6 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ scope_stack_p->map_from = context_p->lit_object.index; - uint16_t map_to; - uint16_t func_init_opcode = CBC_INIT_LOCAL; - #if ENABLED (JERRY_ES2015) if (info_type == SCANNER_TYPE_FUNCTION) { @@ -1996,142 +1916,125 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_GLOBAL; } } - else if (type == SCANNER_STREAM_TYPE_FUNC) +#endif /* ENABLED (JERRY_ES2015) */ + + uint16_t map_to; + uint16_t func_init_opcode = CBC_INIT_LOCAL; + + if (!(data_p[0] & SCANNER_STREAM_NO_REG) + && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { - int32_t target_id = scanner_get_function_target (context_p); + map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); - if (target_id >= 0) +#if ENABLED (JERRY_ES2015) + scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1); +#else /* !ENABLED (JERRY_ES2015) */ + scope_stack_p->map_to = map_to; +#endif /* ENABLED (JERRY_ES2015) */ + + scope_stack_reg_top++; +#if ENABLED (JERRY_ES2015) + switch (type) { - map_to = (uint16_t) target_id; - - scope_stack_p->map_to = PARSER_SCOPE_STACK_FUNC; - func_init_opcode = CBC_SET_VAR_FUNC; + case SCANNER_STREAM_TYPE_CONST: + { + scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST; + /* FALLTHRU */ + } + case SCANNER_STREAM_TYPE_LET: + { + scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_LEXICAL; + break; + } } + + func_init_opcode = CBC_SET_VAR_FUNC; +#endif /* ENABLED (JERRY_ES2015) */ } -#endif /* ENABLED (JERRY_ES2015) */ - - if (func_init_opcode == CBC_INIT_LOCAL) + else { - if (!(data_p[0] & SCANNER_STREAM_NO_REG) - && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) - { - map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; + map_to = context_p->lit_object.index; #if ENABLED (JERRY_ES2015) - scope_stack_p->map_to = (uint16_t) (scope_stack_reg_top + 1); + scope_stack_p->map_to = 0; #else /* !ENABLED (JERRY_ES2015) */ - scope_stack_p->map_to = map_to; + scope_stack_p->map_to = map_to; #endif /* ENABLED (JERRY_ES2015) */ - scope_stack_reg_top++; -#if ENABLED (JERRY_ES2015) - switch (type) - { - case SCANNER_STREAM_TYPE_CONST: - { - scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST; - /* FALLTHRU */ - } - case SCANNER_STREAM_TYPE_LET: - { - scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_LEXICAL; - break; - } - } - - func_init_opcode = CBC_SET_VAR_FUNC; -#endif /* ENABLED (JERRY_ES2015) */ + if (info_type == SCANNER_TYPE_FUNCTION) + { + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; } - else + + switch (type) { - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; - map_to = context_p->lit_object.index; - #if ENABLED (JERRY_ES2015) - scope_stack_p->map_to = 0; -#else /* !ENABLED (JERRY_ES2015) */ - scope_stack_p->map_to = map_to; -#endif /* ENABLED (JERRY_ES2015) */ - - if (info_type == SCANNER_TYPE_FUNCTION) + case SCANNER_STREAM_TYPE_CONST: { - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST; + /* FALLTHRU */ } - - switch (type) + case SCANNER_STREAM_TYPE_LET: { -#if ENABLED (JERRY_ES2015) - case SCANNER_STREAM_TYPE_CONST: - { - scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_CONST; - /* FALLTHRU */ - } - case SCANNER_STREAM_TYPE_LET: - { - scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_LEXICAL; - /* FALLTHRU */ - } - case SCANNER_STREAM_TYPE_LOCAL: - case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: - case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: + scope_stack_p->map_to |= PARSER_SCOPE_STACK_IS_LEXICAL; + /* FALLTHRU */ + } + case SCANNER_STREAM_TYPE_LOCAL: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: #endif /* ENABLED (JERRY_ES2015) */ - case SCANNER_STREAM_TYPE_VAR: - { + case SCANNER_STREAM_TYPE_VAR: + { #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); + context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - uint16_t opcode = CBC_CREATE_VAR; - - if (option_flags & SCANNER_CREATE_VARS_IS_EVAL) - { - opcode = CBC_CREATE_VAR_EVAL; - } - + uint16_t opcode = ((option_flags & SCANNER_CREATE_VARS_IS_SCRIPT) ? CBC_CREATE_VAR_EVAL + : CBC_CREATE_VAR); #if ENABLED (JERRY_ES2015) - switch (type) + switch (type) + { + case SCANNER_STREAM_TYPE_LET: { - case SCANNER_STREAM_TYPE_LET: - { - opcode = CBC_CREATE_LET; - break; - } - case SCANNER_STREAM_TYPE_CONST: - { - opcode = CBC_CREATE_CONST; - break; - } - case SCANNER_STREAM_TYPE_LOCAL: - case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: - case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: - { - opcode = CBC_CREATE_LOCAL; - break; - } + opcode = CBC_CREATE_LET; + break; } + case SCANNER_STREAM_TYPE_CONST: + { + opcode = CBC_CREATE_CONST; + break; + } + case SCANNER_STREAM_TYPE_LOCAL: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: + { + opcode = CBC_CREATE_LOCAL; + break; + } + } #endif /* ENABLED (JERRY_ES2015) */ - parser_emit_cbc_literal (context_p, opcode, map_to); - break; - } - case SCANNER_STREAM_TYPE_ARG: - case SCANNER_STREAM_TYPE_ARG_FUNC: - { + parser_emit_cbc_literal (context_p, opcode, map_to); + break; + } + case SCANNER_STREAM_TYPE_ARG: + case SCANNER_STREAM_TYPE_ARG_FUNC: + { #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); + context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - parser_emit_cbc_literal_value (context_p, - CBC_INIT_LOCAL, - (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top), - map_to); + parser_emit_cbc_literal_value (context_p, + CBC_INIT_LOCAL, + (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top), + map_to); - if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) - { - scope_stack_reg_top++; - } - break; + if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + scope_stack_reg_top++; } + break; } } } @@ -2155,13 +2058,16 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ if (!SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)) { - if (func_init_opcode == CBC_INIT_LOCAL -#if ENABLED (JERRY_ES2015) - && type != SCANNER_STREAM_TYPE_FUNC_LOCAL -#endif /* ENABLED (JERRY_ES2015) */ - && (option_flags & SCANNER_CREATE_VARS_IS_EVAL)) + if (func_init_opcode == CBC_INIT_LOCAL && (option_flags & SCANNER_CREATE_VARS_IS_SCRIPT)) { +#if ENABLED (JERRY_ES2015) + if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL)) + { + func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL; + } +#else /* !ENABLED (JERRY_ES2015) */ func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL; +#endif /* ENABLED (JERRY_ES2015) */ } parser_emit_cbc_literal_value (context_p, func_init_opcode, context_p->literal_count, map_to); @@ -2251,8 +2157,7 @@ scanner_set_location (parser_context_t *context_p, /**< context */ inline uint16_t JERRY_ATTR_ALWAYS_INLINE scanner_decode_map_to (parser_scope_stack_t *stack_item_p) /**< scope stack item */ { - JERRY_ASSERT (stack_item_p->map_from != PARSER_SCOPE_STACK_FUNC - && stack_item_p->map_to != PARSER_SCOPE_STACK_FUNC); + JERRY_ASSERT (stack_item_p->map_from != PARSER_SCOPE_STACK_FUNC); #if ENABLED (JERRY_ES2015) uint16_t value = (stack_item_p->map_to & PARSER_SCOPE_STACK_REGISTER_MASK); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index a0f8cbf27..1cd145fef 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -1393,15 +1393,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_raise_redeclaration_error (context_p); } - if ((context_p->status_flags & PARSER_IS_EVAL) - && scanner_scope_find_let_declaration (context_p, literal_p)) - { - literal_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_LOCAL; - } - else - { - literal_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_DECLARATION; - } + literal_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_FUNC_DECLARATION; #else literal_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; #endif /* ENABLED (JERRY_ES2015) */ @@ -3228,13 +3220,6 @@ scan_completed: JERRY_DEBUG_MSG (" FUNC "); break; } -#if ENABLED (JERRY_ES2015) - case SCANNER_STREAM_TYPE_FUNC_LOCAL: - { - JERRY_DEBUG_MSG (" FUNC_LOCAL "); - break; - } -#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE); diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index d74dcfe14..1737cc8af 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -160,10 +160,7 @@ typedef enum SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC, /**< destructuring argument declaration which * is later initialized with a function */ #endif /* ENABLED (JERRY_ES2015) */ - SCANNER_STREAM_TYPE_FUNC, /**< local or global function declaration */ -#if ENABLED (JERRY_ES2015) - SCANNER_STREAM_TYPE_FUNC_LOCAL, /**< always local function declaration */ -#endif /* ENABLED (JERRY_ES2015) */ + SCANNER_STREAM_TYPE_FUNC, /**< function declaration */ } scanner_compressed_stream_types_t; /** @@ -222,7 +219,7 @@ typedef enum typedef enum { SCANNER_CREATE_VARS_NO_OPTS = 0, /**< no options */ - SCANNER_CREATE_VARS_IS_EVAL = (1 << 0), /**< create variables for script / direct eval */ + SCANNER_CREATE_VARS_IS_SCRIPT = (1 << 0), /**< create variables for script or direct eval */ SCANNER_CREATE_VARS_IS_FUNCTION_ARGS = (1 << 1), /**< create variables for function arguments */ SCANNER_CREATE_VARS_IS_FUNCTION_BODY = (1 << 2), /**< create variables for function body */ } scanner_create_variables_flags_t; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index a5d0ede13..6f6af2558 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1463,6 +1463,41 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } continue; } + case VM_OC_COPY_TO_GLOBAL: + { + uint32_t literal_index; + READ_LITERAL_INDEX (literal_index); + + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + + while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + { +#if ENABLED (JERRY_ES2015) && !(defined JERRY_NDEBUG) + 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); + + JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); + } +#endif /* ENABLED (JERRY_ES2015) && !JERRY_NDEBUG */ + + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + } + +#if ENABLED (JERRY_ES2015) && !(defined JERRY_NDEBUG) + 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); + + JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); + } +#endif /* ENABLED (JERRY_ES2015) && !JERRY_NDEBUG */ + + vm_set_var (lex_env_p, name_p, is_strict, left_value); + continue; + } case VM_OC_CLONE_CONTEXT: { JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 2d1e9bbfb..b478707d9 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -226,6 +226,7 @@ typedef enum #if ENABLED (JERRY_ES2015) VM_OC_INIT_LOCALS, /**< call vm_init_loop() */ VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ + VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ @@ -283,6 +284,7 @@ typedef enum #if !ENABLED (JERRY_ES2015) VM_OC_INIT_LOCALS = VM_OC_NONE, /**< call vm_init_loop() */ VM_OC_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to let/const declarations */ + VM_OC_COPY_TO_GLOBAL = VM_OC_NONE, /**< copy value to global lex env */ 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 */ diff --git a/tests/jerry/es2015/let14.js b/tests/jerry/es2015/let14.js new file mode 100644 index 000000000..5020c085f --- /dev/null +++ b/tests/jerry/es2015/let14.js @@ -0,0 +1,54 @@ +// 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 f() { return 4 } + +exit: { + assert(f() === 6); + break exit; + function f() { return 6; } +} +assert(f() === 4); + +{ + assert(f() === 6); + f = 1; + assert(f === 1); + function f() { return 6; } + f = 2; + assert(f === 2); +} +assert(f === 1); + +function g() { return 3 } +exit: { + assert(g() === 5); + function g() { return 4; } + break exit; + function g() { return 5; } +} +assert(g() === 5); + +function h() { + try { + x; + assert(false); + } catch (e) { + assert(e instanceof ReferenceError); + } + + eval("exit: { assert(x() === 8); x = 4; break exit; function x() { return 8; } }"); + assert(x === undefined); +} +h(); diff --git a/tests/jerry/es2015/let3.js b/tests/jerry/es2015/let3.js index c392081a0..0e74c619d 100644 --- a/tests/jerry/es2015/let3.js +++ b/tests/jerry/es2015/let3.js @@ -16,21 +16,19 @@ var g = -1; function f1() { - /* Function hoisted as var. */ + /* Function copied to var. */ assert (g === undefined); { assert (g() === 1); + function g() { return 1 }; { assert (g() === 2); - function g() { return 2 }; } - function g() { return 1 }; - - assert (g() === 2); + assert (g() === 1); } assert (g() === 2); @@ -38,6 +36,27 @@ function f1() { f1(); function f2() { + /* Function is not copied to var. */ + 'use strict' + assert (g === -1); + + { + assert (g() === 1); + function g() { return 1 }; + + { + assert (g() === 2); + function g() { return 2 }; + } + + assert (g() === 1); + } + + assert (g === -1); +} +f2(); + +function f3() { /* Function hoisted as let. */ assert (g === -1); @@ -65,4 +84,4 @@ function f2() { assert (g === -1); } -f2(); +f3(); diff --git a/tests/jerry/es2015/let4.js b/tests/jerry/es2015/let4.js index ebfb6af8b..ffeb39790 100644 --- a/tests/jerry/es2015/let4.js +++ b/tests/jerry/es2015/let4.js @@ -16,21 +16,19 @@ var g = -1; function f1() { - /* Function hoisted as var. */ + /* Function copied to var. */ assert (g === undefined); { assert (g() === 1); + function g() { return 1 }; { eval("assert (g() === 2)"); - function g() { return 2 }; } - function g() { return 1 }; - - assert (g() === 2); + assert (g() === 1); } assert (g() === 2); @@ -38,6 +36,27 @@ function f1() { f1(); function f2() { + /* Function is not copied to var. */ + 'use strict' + assert (g === -1); + + { + assert (g() === 1); + function g() { return 1 }; + + { + eval("assert (g() === 2)"); + function g() { return 2 }; + } + + assert (g() === 1); + } + + assert (g === -1); +} +f2(); + +function f3() { /* Function hoisted as let. */ assert (g === -1); @@ -65,4 +84,4 @@ function f2() { assert (g === -1); } -f2(); +f3(); diff --git a/tests/jerry/es2015/let7.js b/tests/jerry/es2015/let7.js index 9e3cea216..461905b49 100644 --- a/tests/jerry/es2015/let7.js +++ b/tests/jerry/es2015/let7.js @@ -51,12 +51,28 @@ default: assert (f() === 8); switch (g()) { -case g() * 2: - +case g() + 5: { let g = 4; assert (g == 4); } + break; + +default: + /* If the declaration is not "executed", it has no effect */ + function g() { return 1; } + assert (false); +} + +assert (g() === 6); + +switch (g()) { +case g() * 2: + { + let g = 4; + assert (g == 4); + eval(); + } function g() { return 3; } break; diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index fa449f1c9..db1926f5a 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,12 +223,12 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x25, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0xC0, 0x4E, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0xC1, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,