diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index 309d5cdfe..ae9f3eacc 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -989,7 +989,14 @@ jerry_snapshot_result (const uint32_t *snapshot_p, /**< snapshot */ if (as_function) { - ecma_object_t *lex_env_p = ecma_get_global_environment (); +#if ENABLED (JERRY_ES2015) + if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) + { + ecma_create_global_lexical_block (); + } +#endif /* ENABLED (JERRY_ES2015) */ + + ecma_object_t *lex_env_p = ecma_get_global_scope (); ecma_object_t *func_obj_p = ecma_op_create_simple_function_object (lex_env_p, bytecode_p); if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index 3062889e6..e0bca19f3 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -44,7 +44,7 @@ ecma_init (void) JERRY_CONTEXT (ecma_gc_mark_recursion_limit) = JERRY_GC_MARK_LIMIT; #endif /* (JERRY_GC_MARK_LIMIT != 0) */ - ecma_init_global_lex_env (); + ecma_init_global_environment (); #if ENABLED (JERRY_PROPRETY_HASHMAP) JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_ON; @@ -77,7 +77,7 @@ ecma_finalize (void) JERRY_ASSERT (JERRY_CONTEXT (current_function_obj_p) == NULL); #endif /* ENABLED (JERRY_ES2015) */ - ecma_finalize_global_lex_env (); + ecma_finalize_global_environment (); uint8_t runs = 0; do { diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index 3f176ec5b..0967a19ce 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -660,7 +660,7 @@ ecma_module_evaluate (ecma_module_t *module_p) /**< module */ * @return ECMA_VALUE_ERROR - if an error occured * ECMA_VALUE_EMPTY - otherwise */ -ecma_value_t +static ecma_value_t ecma_module_connect_imports (void) { ecma_module_context_t *current_context_p = JERRY_CONTEXT (module_top_context_p); @@ -669,6 +669,39 @@ ecma_module_connect_imports (void) JERRY_ASSERT (ecma_is_lexical_environment (local_env_p)); ecma_module_node_t *import_node_p = current_context_p->imports_p; + + /* Check that the imported bindings don't exist yet. */ + while (import_node_p != NULL) + { + ecma_module_names_t *import_names_p = import_node_p->module_names_p; + + while (import_names_p != NULL) + { + ecma_object_t *lex_env_p = local_env_p; + ecma_property_t *binding_p = NULL; + + if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + { + binding_p = ecma_find_named_property (lex_env_p, import_names_p->local_name_p); + + 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 (binding_p != NULL || ecma_op_has_binding (lex_env_p, import_names_p->local_name_p)) + { + return ecma_raise_syntax_error (ECMA_ERR_MSG ("Imported binding shadows local variable.")); + } + + import_names_p = import_names_p->next_p; + } + + import_node_p = import_node_p->next_p; + } + + import_node_p = current_context_p->imports_p; + + /* Resolve imports and create local bindings. */ while (import_node_p != NULL) { ecma_value_t result = ecma_module_evaluate (import_node_p->module_request_p); @@ -761,6 +794,26 @@ ecma_module_connect_imports (void) return ECMA_VALUE_EMPTY; } /* ecma_module_connect_imports */ +/** + * Initialize the current module by creating the local binding for the imported variables + * and verifying indirect exports. + * + * @return ECMA_VALUE_ERROR - if an error occured + * ECMA_VALUE_EMPTY - otherwise + */ +ecma_value_t +ecma_module_initialize_current (void) +{ + ecma_value_t ret_value = ecma_module_connect_imports (); + + if (ecma_is_value_empty (ret_value)) + { + ret_value = ecma_module_check_indirect_exports (); + } + + return ret_value; +} /* ecma_module_initialize_current */ + /** * Parses an EcmaScript module. * @@ -975,7 +1028,8 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */ ecma_module_release_module_context (module_p->context_p); } - if (module_p->state >= ECMA_MODULE_STATE_EVALUATING) + if (module_p->state >= ECMA_MODULE_STATE_EVALUATING + && module_p->scope_p != NULL) { ecma_deref_object (module_p->scope_p); } @@ -996,11 +1050,6 @@ finished: void ecma_module_cleanup (void) { - if (JERRY_CONTEXT (module_top_context_p)->parent_p != NULL) - { - return; - } - ecma_module_t *current_p = JERRY_CONTEXT (ecma_modules_p); while (current_p != NULL) { @@ -1009,6 +1058,7 @@ ecma_module_cleanup (void) current_p = next_p; } + JERRY_CONTEXT (ecma_modules_p) = NULL; JERRY_CONTEXT (module_top_context_p) = NULL; } /* ecma_module_cleanup */ diff --git a/jerry-core/ecma/base/ecma-module.h b/jerry-core/ecma/base/ecma-module.h index 290b4d5c3..230a6ded3 100644 --- a/jerry-core/ecma/base/ecma-module.h +++ b/jerry-core/ecma/base/ecma-module.h @@ -133,7 +133,7 @@ ecma_module_t *ecma_module_create_native_module (ecma_string_t *const path_p, ecma_object_t *const namespace_p); ecma_module_t *ecma_module_find_or_create_module (ecma_string_t *const path_p); -ecma_value_t ecma_module_connect_imports (void); +ecma_value_t ecma_module_initialize_current (void); ecma_value_t ecma_module_parse_modules (void); ecma_value_t ecma_module_check_indirect_exports (void); diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index 76c57a033..3b244e6f4 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -37,25 +37,35 @@ * Initialize Global environment */ void -ecma_init_global_lex_env (void) +ecma_init_global_environment (void) { ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); ecma_object_t *global_lex_env_p = ecma_create_object_lex_env (NULL, glob_obj_p, ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); - ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_lex_env_cp), global_lex_env_p); -} /* ecma_init_global_lex_env */ + ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_env_cp), global_lex_env_p); +#if ENABLED (JERRY_ES2015) + ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_scope_cp), global_lex_env_p); +#endif /* ENABLED (JERRY_ES2015) */ +} /* ecma_init_global_environment */ /** * Finalize Global environment */ void -ecma_finalize_global_lex_env (void) +ecma_finalize_global_environment (void) { - ecma_deref_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_lex_env_cp))); - JERRY_CONTEXT (ecma_global_lex_env_cp) = JMEM_CP_NULL; -} /* ecma_finalize_global_lex_env */ +#if ENABLED (JERRY_ES2015) + if (JERRY_CONTEXT (ecma_global_scope_cp) != JERRY_CONTEXT (ecma_global_env_cp)) + { + ecma_deref_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_scope_cp))); + } + JERRY_CONTEXT (ecma_global_scope_cp) = JMEM_CP_NULL; +#endif /* ENABLED (JERRY_ES2015) */ + ecma_deref_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_env_cp))); + JERRY_CONTEXT (ecma_global_env_cp) = JMEM_CP_NULL; +} /* ecma_finalize_global_environment */ /** * Get reference to Global lexical environment @@ -66,10 +76,43 @@ ecma_finalize_global_lex_env (void) ecma_object_t * ecma_get_global_environment (void) { - JERRY_ASSERT (JERRY_CONTEXT (ecma_global_lex_env_cp) != JMEM_CP_NULL); - return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_lex_env_cp)); + JERRY_ASSERT (JERRY_CONTEXT (ecma_global_env_cp) != JMEM_CP_NULL); + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_env_cp)); } /* ecma_get_global_environment */ +#if ENABLED (JERRY_ES2015) +/** + * Create the global lexical block on top of the global environment. + */ +void +ecma_create_global_lexical_block (void) +{ + if (JERRY_CONTEXT (ecma_global_scope_cp) == JERRY_CONTEXT (ecma_global_env_cp)) + { + ecma_object_t *global_scope_p = ecma_create_decl_lex_env (ecma_get_global_environment ()); + global_scope_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_BLOCK; + ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_scope_cp), global_scope_p); + } +} /* ecma_create_global_lexical_block */ +#endif /* ENABLED (JERRY_ES2015) */ + +/** + * Get reference to Global lexical scope + * without increasing its reference count. + * + * @return pointer to the object's instance + */ +ecma_object_t * +ecma_get_global_scope (void) +{ +#if ENABLED (JERRY_ES2015) + JERRY_ASSERT (JERRY_CONTEXT (ecma_global_scope_cp) != JMEM_CP_NULL); + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_scope_cp)); +#else /* !ENABLED (JERRY_ES2015) */ + return ecma_get_global_environment (); +#endif /* !ENABLED (JERRY_ES2015) */ +} /* ecma_get_global_scope */ + /** * @} */ diff --git a/jerry-core/ecma/operations/ecma-lex-env.h b/jerry-core/ecma/operations/ecma-lex-env.h index 7ff5c98b9..c43c3b147 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.h +++ b/jerry-core/ecma/operations/ecma-lex-env.h @@ -30,9 +30,13 @@ * @{ */ -void ecma_init_global_lex_env (void); -void ecma_finalize_global_lex_env (void); +void ecma_init_global_environment (void); +void ecma_finalize_global_environment (void); ecma_object_t *ecma_get_global_environment (void); +ecma_object_t *ecma_get_global_scope (void); +#if ENABLED (JERRY_ES2015) +void ecma_create_global_lexical_block (void); +#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) void ecma_module_add_lex_env (ecma_object_t *lex_env_p); diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 40a5c99bf..cba1d4c4e 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -106,7 +106,7 @@ ecma_value_t ecma_op_is_prop_unscopable (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *prop_name_p) /**< property's name */ { - if (lex_env_p == ecma_get_global_environment ()) + if (lex_env_p == ecma_get_global_scope ()) { return ECMA_VALUE_FALSE; } diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 31c8b88b8..57f867846 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 (38u) +#define JERRY_SNAPSHOT_VERSION (39u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 9f949014a..5f3f02ebb 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -142,7 +142,10 @@ struct jerry_context_t jmem_cpointer_t symbol_list_first_cp; /**< first item of the global symbol list */ #endif /* ENABLED (JERRY_ES2015) */ jmem_cpointer_t number_list_first_cp; /**< first item of the literal number list */ - jmem_cpointer_t ecma_global_lex_env_cp; /**< global lexical environment */ + jmem_cpointer_t ecma_global_env_cp; /**< global lexical environment */ +#if ENABLED (JERRY_ES2015) + jmem_cpointer_t ecma_global_scope_cp; /**< global lexical scope */ +#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) ecma_module_t *ecma_modules_p; /**< list of referenced modules */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 886379b84..4ee13c446 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -302,6 +302,10 @@ VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, \ VM_OC_EVAL) \ + CBC_OPCODE (CBC_CHECK_VAR, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_CHECK_VAR) \ + CBC_OPCODE (CBC_CHECK_LET, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_CHECK_LET) \ CBC_OPCODE (CBC_CREATE_VAR, CBC_HAS_LITERAL_ARG, 0, \ VM_OC_CREATE_BINDING) \ CBC_OPCODE (CBC_CREATE_LET, CBC_HAS_LITERAL_ARG, 0, \ @@ -780,6 +784,7 @@ typedef enum CBC_CODE_FLAGS_GENERATOR = (1u << 11), /**< this function is a generator */ CBC_CODE_FLAGS_REST_PARAMETER = (1u << 12), /**< this function has rest parameter */ CBC_CODE_FLAG_HAS_TAGGED_LITERALS = (1u << 13), /**< this function has tagged template literal list */ + CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED = (1u << 14), /**< compiled code needs a lexical block */ } cbc_code_flags; /** diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 9d46b64c3..b69cc9342 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -58,7 +58,7 @@ typedef enum PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 11), /**< pending (unsent) breakpoint * info is available */ #if ENABLED (JERRY_ES2015) - PARSER_INSIDE_BLOCK = (1u << 12), /**< script has a lexical environment for let and const */ + PARSER_LEXICAL_BLOCK_NEEDED = (1u << 12), /**< script needs a lexical environment for let and const */ PARSER_IS_ARROW_FUNCTION = (1u << 13), /**< an arrow function is parsed */ PARSER_IS_GENERATOR_FUNCTION = (1u << 14), /**< a generator function is parsed */ PARSER_IS_ASYNC_FUNCTION = (1u << 15), /**< an async function is parsed */ @@ -726,6 +726,7 @@ bool scanner_is_context_needed (parser_context_t *context_p); 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); +void scanner_check_variables (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-module.c b/jerry-core/parser/js/js-parser-module.c index 4d5334eee..c53016945 100644 --- a/jerry-core/parser/js/js-parser-module.c +++ b/jerry-core/parser/js/js-parser-module.c @@ -325,8 +325,8 @@ parser_module_context_init (void) ecma_module_t *module_p = ecma_module_find_or_create_module (path_p); module_p->state = ECMA_MODULE_STATE_EVALUATED; - module_p->scope_p = ecma_get_global_environment (); - ecma_ref_object (module_p->scope_p); + /* The lexical scope of the root module does not exist yet. */ + module_p->scope_p = NULL; module_p->context_p = module_context_p; module_context_p->module_p = module_p; diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 0f20561ee..c28c38356 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -125,21 +125,7 @@ static const uint8_t parser_statement_flags[] = PARSER_STATM_CONTEXT_BREAK }; -#if !ENABLED (JERRY_ES2015) - -/** - * Get the expected depth of a function call. - */ -#define JERRY_GET_EXPECTED_DEPTH(context_p) 0 - -#else /* ENABLED (JERRY_ES2015) */ - -/** - * Get the expected depth of a function call. - */ -#define JERRY_GET_EXPECTED_DEPTH(context_p) \ - (((context_p)->status_flags & PARSER_INSIDE_BLOCK) ? PARSER_BLOCK_CONTEXT_STACK_ALLOCATION : 0) - +#if ENABLED (JERRY_ES2015) /** * Block statement. */ @@ -2637,7 +2623,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ lexer_lit_location_t lit_location; bool is_use_strict = false; - JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p)); + JERRY_ASSERT (context_p->stack_depth == 0); #ifndef JERRY_NDEBUG JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth); #endif /* !JERRY_NDEBUG */ @@ -3348,7 +3334,7 @@ consume_last_statement: } } - JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p)); + JERRY_ASSERT (context_p->stack_depth == 0); #ifndef JERRY_NDEBUG JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth); #endif /* !JERRY_NDEBUG */ diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index e8d819f78..7aa6869f3 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1311,6 +1311,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */ { compiled_code_p->status_flags |= CBC_CODE_FLAG_HAS_TAGGED_LITERALS; } + + if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED; + } #endif /* ENABLED (JERRY_ES2015) */ literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count; @@ -2093,18 +2098,12 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ #if ENABLED (JERRY_ES2015) if (scanner_is_global_context_needed (&context)) { - parser_branch_t branch; + context.status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; + } -#ifndef JERRY_NDEBUG - PARSER_PLUS_EQUAL_U16 (context.context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); -#endif /* !JERRY_NDEBUG */ - - parser_emit_cbc_forward_branch (&context, - CBC_BLOCK_CREATE_CONTEXT, - &branch); - - parser_stack_push (&context, &branch, sizeof (parser_branch_t)); - context.status_flags |= PARSER_INSIDE_BLOCK; + if ((parse_opts & ECMA_PARSE_EVAL) == 0) + { + scanner_check_variables (&context); } #endif /* ENABLED (JERRY_ES2015) */ @@ -2113,18 +2112,6 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ parser_parse_statements (&context); -#if ENABLED (JERRY_ES2015) - if (context.status_flags & PARSER_INSIDE_BLOCK) - { - parser_branch_t branch; - parser_stack_pop (&context, &branch, sizeof (parser_branch_t)); - - parser_emit_cbc (&context, CBC_CONTEXT_END); - parser_set_branch_to_current_position (&context, &branch); - parser_flush_cbc (&context); - } -#endif /* ENABLED (JERRY_ES2015) */ - /* When the parsing is successful, only the * dummy value can be remained on the stack. */ JERRY_ASSERT (context.stack_top_uint8 == CBC_MAXIMUM_BYTE_VALUE diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index d29970848..02552522a 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -257,9 +257,6 @@ typedef enum SCANNER_LITERAL_POOL_BLOCK = (1 << 1), /**< literal pool represents a code block */ SCANNER_LITERAL_POOL_IS_STRICT = (1 << 2), /**< literal pool represents a strict mode code block */ SCANNER_LITERAL_POOL_NO_REG = (1 << 3), /**< variable declarations cannot be kept in registers */ -#if ENABLED (JERRY_ES2015) - SCANNER_LITERAL_POOL_NO_VAR_REG = (1 << 4), /**< non let/const declarations cannot be kept in registers */ -#endif /* ENABLED (JERRY_ES2015) */ SCANNER_LITERAL_POOL_NO_ARGUMENTS = (1 << 5), /**< arguments object must not be constructed */ #if ENABLED (JERRY_ES2015) SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED = (1 << 6), /**< arguments object should be unmapped */ diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index 9b1aa06f3..c6922a0c4 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -470,21 +470,14 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ bool no_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_REG) != 0; bool search_arguments = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0; bool arguments_required = (no_reg && search_arguments); + + uint8_t no_reg_types = 0; #if ENABLED (JERRY_ES2015) - uint8_t no_var_flags = 0; - - if (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_VAR_REG) + if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL)) { - no_var_flags = SCANNER_LITERAL_IS_VAR; - - if (!(context_p->status_flags & PARSER_IS_DIRECT_EVAL)) - { - no_var_flags |= SCANNER_LITERAL_IS_FUNC; - } + no_reg_types |= SCANNER_LITERAL_IS_FUNC; } -#else /* !ENABLED (JERRY_ES2015) */ - uint8_t no_var_flags = 0; -#endif /* ENABLED (JERRY_ES2015) */ +#endif /* !ENABLED (JERRY_ES2015) */ if (no_reg && prev_literal_pool_p != NULL) { @@ -565,7 +558,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ no_declarations++; } - if (no_reg || (type & no_var_flags)) + if (no_reg || (type & no_reg_types)) { type |= SCANNER_LITERAL_NO_REG; literal_p->type = type; @@ -1738,6 +1731,90 @@ scanner_create_unused_literal (parser_context_t *context_p, /**< context */ context_p->literal_count++; } /* scanner_create_unused_literal */ +#if ENABLED (JERRY_ES2015) +/** + * Emit checks for redeclared bindings in the global lexical scope. + */ +void +scanner_check_variables (parser_context_t *context_p) /**< context */ +{ + scanner_info_t *info_p = context_p->next_scanner_info_p; + const uint8_t *next_data_p = (const uint8_t *) (info_p + 1); + lexer_lit_location_t literal; + + JERRY_ASSERT (info_p->type == SCANNER_TYPE_FUNCTION); + + literal.char_p = info_p->source_p - 1; + + while (next_data_p[0] != SCANNER_STREAM_TYPE_END) + { + uint32_t type = next_data_p[0] & SCANNER_STREAM_TYPE_MASK; + const uint8_t *data_p = next_data_p; + + JERRY_ASSERT (type != SCANNER_STREAM_TYPE_HOLE + && !SCANNER_STREAM_TYPE_IS_ARG (type) + && !SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)); + JERRY_ASSERT (data_p[0] & SCANNER_STREAM_NO_REG); + + if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) + { + if (data_p[2] != 0) + { + literal.char_p += data_p[2]; + next_data_p += 2 + 1; + } + else + { + memcpy (&literal.char_p, data_p + 2 + 1, sizeof (const uint8_t *)); + next_data_p += 2 + 1 + sizeof (const uint8_t *); + } + } + else + { + int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; + + if (diff <= UINT8_MAX) + { + diff = -diff; + } + + literal.char_p += diff; + next_data_p += 2 + 2; + } + + 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); + literal.char_p += data_p[1]; + +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + if (type == SCANNER_STREAM_TYPE_IMPORT) + { + continue; + } +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; + + uint16_t opcode; + if (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_FUNC) + { + opcode = CBC_CHECK_VAR; + } + else + { + opcode = CBC_CHECK_LET; + } + + parser_emit_cbc_literal (context_p, opcode, context_p->lit_object.index); + } + + parser_flush_cbc (context_p); +} /* scanner_check_variables */ +#endif /* ENABLED (JERRY_ES2015) */ + /** * Create and/or initialize var/let/const/function/etc. variables. */ diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 1cd145fef..eb575b9a9 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -2263,11 +2263,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ context_p->source_p = source_p; context_p->source_end_p = source_end_p; -#if ENABLED (JERRY_ES2015) - uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_VAR_REG; -#else /* !ENABLED (JERRY_DEBUGGER) */ uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_REG; -#endif /* ENABLED (JERRY_DEBUGGER) */ if (context_p->status_flags & PARSER_IS_STRICT) { diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index d20be60d0..15aabfa09 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -260,10 +260,14 @@ ecma_value_t vm_run_module (const ecma_compiled_code_t *bytecode_p, /**< pointer to bytecode to run */ ecma_object_t *lex_env_p) /**< pointer to the specified lexenv to run in */ { - ecma_object_t *glob_obj_p = ecma_builtin_get_global (); + const ecma_value_t module_init_result = ecma_module_initialize_current (); + if (ECMA_IS_VALUE_ERROR (module_init_result)) + { + return module_init_result; + } return vm_run (bytecode_p, - ecma_make_object_value (glob_obj_p), + ECMA_VALUE_UNDEFINED, lex_env_p, NULL, 0); @@ -283,9 +287,39 @@ vm_run_global (const ecma_compiled_code_t *bytecode_p) /**< pointer to bytecode { ecma_object_t *glob_obj_p = ecma_builtin_get_global (); +#if ENABLED (JERRY_ES2015) + if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) + { + ecma_create_global_lexical_block (); + } +#endif /* ENABLED (JERRY_ES2015) */ + + ecma_object_t *const global_scope_p = ecma_get_global_scope (); + +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + if (JERRY_CONTEXT (module_top_context_p) != NULL) + { + JERRY_ASSERT (JERRY_CONTEXT (module_top_context_p)->parent_p == NULL); + ecma_module_t *module_p = JERRY_CONTEXT (module_top_context_p)->module_p; + + JERRY_ASSERT (module_p->scope_p == NULL); + ecma_ref_object (global_scope_p); + module_p->scope_p = global_scope_p; + + const ecma_value_t module_init_result = ecma_module_initialize_current (); + ecma_module_cleanup (); + JERRY_CONTEXT (module_top_context_p) = NULL; + + if (ECMA_IS_VALUE_ERROR (module_init_result)) + { + return module_init_result; + } + } +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + return vm_run (bytecode_p, ecma_make_object_value (glob_obj_p), - ecma_get_global_environment (), + global_scope_p, NULL, 0); } /* vm_run_global */ @@ -333,7 +367,7 @@ vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ ecma_object_t *global_obj_p = ecma_builtin_get_global (); ecma_ref_object (global_obj_p); this_binding = ecma_make_object_value (global_obj_p); - lex_env_p = ecma_get_global_environment (); + lex_env_p = ecma_get_global_scope (); } ecma_ref_object (lex_env_p); @@ -341,11 +375,20 @@ vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0) { ecma_object_t *strict_lex_env_p = ecma_create_decl_lex_env (lex_env_p); - ecma_deref_object (lex_env_p); + ecma_deref_object (lex_env_p); lex_env_p = strict_lex_env_p; } + if ((bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) != 0) + { + ecma_object_t *lex_block_p = ecma_create_decl_lex_env (lex_env_p); + lex_block_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_BLOCK; + + ecma_deref_object (lex_env_p); + lex_env_p = lex_block_p; + } + ecma_value_t completion_value = vm_run (bytecode_data_p, this_binding, lex_env_p, @@ -1337,6 +1380,56 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ continue; } #if ENABLED (JERRY_ES2015) + case VM_OC_CHECK_VAR: + { + JERRY_ASSERT (ecma_get_global_scope () == frame_ctx_p->lex_env_p); + + uint32_t literal_index; + READ_LITERAL_INDEX (literal_index); + + if ((frame_ctx_p->lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) == 0) + { + continue; + } + + ecma_string_t *const literal_name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_property_t *const binding_p = ecma_find_named_property (frame_ctx_p->lex_env_p, literal_name_p); + + if (binding_p != NULL) + { + result = ecma_raise_syntax_error (ECMA_ERR_MSG ("Local variable is redeclared.")); + goto error; + } + + continue; + } + case VM_OC_CHECK_LET: + { + JERRY_ASSERT (ecma_get_global_scope () == frame_ctx_p->lex_env_p); + + uint32_t literal_index; + READ_LITERAL_INDEX (literal_index); + + ecma_string_t *literal_name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + ecma_property_t *binding_p = NULL; + + if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + { + binding_p = ecma_find_named_property (lex_env_p, literal_name_p); + + 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 (binding_p != NULL || ecma_op_has_binding (lex_env_p, literal_name_p)) + { + result = ecma_raise_syntax_error (ECMA_ERR_MSG ("Local variable is redeclared.")); + goto error; + } + + continue; + } case VM_OC_ASSIGN_LET_CONST: { uint32_t literal_index; @@ -4181,27 +4274,6 @@ vm_run (const ecma_compiled_code_t *bytecode_header_p, /**< byte-code data heade vm_frame_ctx_t *frame_ctx_p; size_t frame_size; -#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (JERRY_CONTEXT (module_top_context_p) != NULL) - { - ecma_value_t ret_value = ecma_module_connect_imports (); - - if (ecma_is_value_empty (ret_value)) - { - ret_value = ecma_module_check_indirect_exports (); - } - - ecma_module_cleanup (); - - if (!ecma_is_value_empty (ret_value)) - { - return ret_value; - } - - JERRY_CONTEXT (module_top_context_p) = NULL; - } -#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ - if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 79caa024a..ef3291b50 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -229,6 +229,8 @@ typedef enum VM_OC_LINE, /**< line number of the next statement */ #endif /* ENABLED (JERRY_LINE_INFO) */ #if ENABLED (JERRY_ES2015) + VM_OC_CHECK_VAR, /**< check redeclared vars in the global scope */ + VM_OC_CHECK_LET, /**< check redeclared lets in the global scope */ 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 */ @@ -286,6 +288,8 @@ typedef enum VM_OC_LINE = VM_OC_NONE, /**< line number of the next statement is unused */ #endif /* !ENABLED (JERRY_LINE_INFO) */ #if !ENABLED (JERRY_ES2015) + VM_OC_CHECK_VAR = VM_OC_NONE, /**< check redeclared vars in the global scope */ + VM_OC_CHECK_LET = VM_OC_NONE, /**< check redeclared lets in the global scope */ 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 */ diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index c0c3eb502..9cd8b8ff6 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -889,33 +889,44 @@ main (int argc, } /* Evaluate the line */ - jerry_value_t ret_val_eval = jerry_eval (buffer, len, JERRY_PARSE_NO_OPTS); + jerry_value_t ret_val = jerry_parse (NULL, + 0, + buffer, + len, + JERRY_PARSE_NO_OPTS); - if (!jerry_value_is_error (ret_val_eval)) + if (!jerry_value_is_error (ret_val)) + { + jerry_value_t func_val = ret_val; + ret_val = jerry_run (func_val); + jerry_release_value (func_val); + } + + if (!jerry_value_is_error (ret_val)) { /* Print return value */ - const jerry_value_t args[] = { ret_val_eval }; + const jerry_value_t args[] = { ret_val }; jerry_value_t ret_val_print = jerryx_handler_print (jerry_create_undefined (), jerry_create_undefined (), args, 1); jerry_release_value (ret_val_print); - jerry_release_value (ret_val_eval); - ret_val_eval = jerry_run_all_enqueued_jobs (); + jerry_release_value (ret_val); + ret_val = jerry_run_all_enqueued_jobs (); - if (jerry_value_is_error (ret_val_eval)) + if (jerry_value_is_error (ret_val)) { - ret_val_eval = jerry_get_value_from_error (ret_val_eval, true); - print_unhandled_exception (ret_val_eval); + ret_val = jerry_get_value_from_error (ret_val, true); + print_unhandled_exception (ret_val); } } else { - ret_val_eval = jerry_get_value_from_error (ret_val_eval, true); - print_unhandled_exception (ret_val_eval); + ret_val = jerry_get_value_from_error (ret_val, true); + print_unhandled_exception (ret_val); } - jerry_release_value (ret_val_eval); + jerry_release_value (ret_val); } } } diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index ffb216844..d19e8a280 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -811,6 +811,47 @@ main (void) jerry_cleanup (); } + /* Test parsing/executing scripts with lexically scoped global variables multiple times. */ + if (jerry_is_feature_enabled (JERRY_FEATURE_SYMBOL)) + { + jerry_init (JERRY_INIT_EMPTY); + const jerry_char_t scoped_src_p[] = "let a;"; + jerry_value_t parse_result = jerry_parse (NULL, + 0, + scoped_src_p, + sizeof (scoped_src_p) - 1, + JERRY_PARSE_NO_OPTS); + TEST_ASSERT (!jerry_value_is_error (parse_result)); + jerry_release_value (parse_result); + + parse_result = jerry_parse (NULL, + 0, + scoped_src_p, + sizeof (scoped_src_p) - 1, + JERRY_PARSE_NO_OPTS); + TEST_ASSERT (!jerry_value_is_error (parse_result)); + + jerry_value_t run_result = jerry_run (parse_result); + TEST_ASSERT (!jerry_value_is_error (run_result)); + jerry_release_value (run_result); + + /* Should be a syntax error due to redeclaration. */ + run_result = jerry_run (parse_result); + TEST_ASSERT (jerry_value_is_error (run_result)); + jerry_release_value (run_result); + jerry_release_value (parse_result); + + /* The variable should have no effect on parsing. */ + parse_result = jerry_parse (NULL, + 0, + scoped_src_p, + sizeof (scoped_src_p) - 1, + JERRY_PARSE_NO_OPTS); + TEST_ASSERT (!jerry_value_is_error (parse_result)); + jerry_release_value (parse_result); + jerry_cleanup (); + } + /* Test: parser error location */ if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES)) { diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index db1926f5a..7d3dc4c49 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,15 +223,15 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x26, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x27, 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, 0xC1, 0x4E, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0xC3, 0x50, 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, + 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x73, 0x6E, 0x61, 0x70, 0x73, 0x68, 0x6F, 0x74, @@ -416,7 +416,10 @@ main (void) size_t snapshot_size = (size_t) jerry_get_number_value (generate_result); jerry_release_value (generate_result); - TEST_ASSERT (snapshot_size == 124); + + /* In ES2015 we emit extra bytecode instructions to check global variable redeclaration. */ + const size_t expected_size = (jerry_is_feature_enabled (JERRY_FEATURE_SYMBOL)) ? 132 : 124; + TEST_ASSERT (snapshot_size == expected_size); const size_t lit_c_buf_sz = jerry_get_literals_from_snapshot (literal_snapshot_buffer, snapshot_size,