From 6a342fcdd6a23854b52700a5e92c910f84368092 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 31 Oct 2019 11:09:02 +0100 Subject: [PATCH] Implement throwing ReferenceErrors for let/const variables. (#3264) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-globals.h | 1 + .../ecma/operations/ecma-get-put-value.c | 12 ++- jerry-core/ecma/operations/ecma-reference.c | 12 ++- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/parser/js/byte-code.h | 2 + jerry-core/parser/js/js-parser-expr.c | 23 ++++-- jerry-core/parser/js/js-parser-internal.h | 2 +- jerry-core/parser/js/js-parser-statm.c | 47 +++++++---- jerry-core/parser/js/js-scanner-internal.h | 42 +++++----- jerry-core/parser/js/js-scanner-util.c | 43 ++++++---- jerry-core/parser/js/js-scanner.c | 80 ++++++++++++++++--- jerry-core/vm/vm.c | 35 +++++++- jerry-core/vm/vm.h | 2 + tests/jerry/es2015/let10.js | 45 +++++++++++ tests/jerry/try-eval.js | 31 +++++++ tests/unit-core/test-snapshot.c | 2 +- 16 files changed, 306 insertions(+), 75 deletions(-) create mode 100644 tests/jerry/es2015/let10.js create mode 100644 tests/jerry/try-eval.js diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 5fe8467ba..36ca34ba6 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -188,6 +188,7 @@ enum ECMA_VALUE_REGISTER_REF = ECMA_MAKE_VALUE (8), /**< register reference, * a special "base" value for vm */ ECMA_VALUE_IMPLICIT_CONSTRUCTOR = ECMA_MAKE_VALUE (9), /**< special value for bound class constructors */ + ECMA_VALUE_UNINITIALIZED = ECMA_MAKE_VALUE (10), /**< a special value for uninitialized let/const declarations */ }; #if !ENABLED (JERRY_NUMBER_TYPE_FLOAT64) diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index ca419a96d..049e47f97 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -204,7 +204,17 @@ ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme { if (ecma_is_property_writable (*property_p)) { - ecma_named_data_property_assign_value (lex_env_p, ECMA_PROPERTY_VALUE_PTR (property_p), value); + ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + +#if ENABLED (JERRY_ES2015) + if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) + { + return ecma_raise_reference_error (ECMA_ERR_MSG ("Variables declared by let/const must be" + " initialized before writing their value.")); + } +#endif /* ENABLED (JERRY_ES2015) */ + + ecma_named_data_property_assign_value (lex_env_p, property_value_p, value); } else if (is_strict) { diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 124f17b9c..26cc15ff6 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -114,7 +114,17 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical if (property_p != NULL) { - return ecma_fast_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); + ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + +#if ENABLED (JERRY_ES2015) + if (JERRY_UNLIKELY (property_value_p->value == ECMA_VALUE_UNINITIALIZED)) + { + return ecma_raise_reference_error (ECMA_ERR_MSG ("Variables declared by let/const must be" + " initialized before reading their value.")); + } +#endif /* ENABLED (JERRY_ES2015) */ + + return ecma_fast_copy_value (property_value_p->value); } } else if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND) diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 91298eab1..443360ce0 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 (27u) +#define JERRY_SNAPSHOT_VERSION (28u) /** * 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 d7005c6d5..90b264267 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -488,6 +488,8 @@ VM_OC_ASSIGN_PROP_THIS | VM_OC_GET_LITERAL | VM_OC_PUT_REFERENCE | VM_OC_PUT_BLOCK) \ CBC_OPCODE (CBC_MOV_IDENT, CBC_HAS_LITERAL_ARG, -1, \ VM_OC_MOV_IDENT | VM_OC_GET_STACK | VM_OC_PUT_IDENT) \ + CBC_OPCODE (CBC_ASSIGN_LET_CONST, CBC_HAS_LITERAL_ARG, -1, \ + VM_OC_ASSIGN_LET_CONST | VM_OC_GET_STACK) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_END, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 7a59adb98..66c389b13 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -586,6 +586,11 @@ parser_parse_class (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + /* FIXME: Remove this hack after module classes are supported. */ + uint16_t assign_opcode = CBC_ASSIGN_LET_CONST; +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + uint16_t class_ident_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; if (is_statement) @@ -595,19 +600,19 @@ parser_parse_class (parser_context_t *context_p, /**< context */ JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); -#if ENABLED (JERRY_ES2015) if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } -#endif /* ENABLED (JERRY_ES2015) */ class_ident_index = context_p->lit_object.index; #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) { + assign_opcode = CBC_ASSIGN_SET_IDENT; + context_p->module_identifier_lit_p = context_p->lit_object.literal_p; context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); } @@ -658,17 +663,19 @@ parser_parse_class (parser_context_t *context_p, /**< context */ parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, context_p->lit_object.index); - if (is_statement) - { - parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, class_ident_index); - } - if (create_class_env) { - parser_parse_super_class_context_end (context_p, is_statement); + parser_parse_super_class_context_end (context_p); context_p->status_flags &= (uint32_t) ~(PARSER_CLASS_HAS_SUPER | PARSER_CLASS_IMPLICIT_SUPER); } + if (is_statement) + { + parser_emit_cbc_literal (context_p, + class_ident_index >= PARSER_REGISTER_START ? CBC_MOV_IDENT : assign_opcode, + class_ident_index); + } + parser_flush_cbc (context_p); if (!is_strict) diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 167e1b84e..21f861e7c 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -613,7 +613,7 @@ void parser_parse_expression (parser_context_t *context_p, int options); #if ENABLED (JERRY_ES2015) void parser_parse_class (parser_context_t *context_p, bool is_statement); void parser_parse_super_class_context_start (parser_context_t *context_p); -void parser_parse_super_class_context_end (parser_context_t *context_p, bool is_statement); +void parser_parse_super_class_context_end (parser_context_t *context_p); #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 663ba512d..91314d9de 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -353,7 +353,7 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ || context_p->token.type == LEXER_KEYW_CONST); #if ENABLED (JERRY_ES2015) - bool is_const = context_p->token.type == LEXER_KEYW_CONST; + uint8_t declaration_type = context_p->token.type; #endif /* ENABLED (JERRY_ES2015) */ while (true) @@ -406,11 +406,36 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_LINE_INFO) */ - parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); - parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); +#if ENABLED (JERRY_ES2015) + if (declaration_type != LEXER_KEYW_VAR + && context_p->lit_object.index < PARSER_REGISTER_START) + { + uint16_t index = context_p->lit_object.index; + + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_emit_cbc_literal (context_p, CBC_ASSIGN_LET_CONST, index); + } + else + { +#endif /* ENABLED (JERRY_ES2015) */ + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); +#if ENABLED (JERRY_ES2015) + } +#endif /* ENABLED (JERRY_ES2015) */ } #if ENABLED (JERRY_ES2015) - else if (is_const) + else if (declaration_type == LEXER_KEYW_LET) + { + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + + uint16_t index = context_p->lit_object.index; + parser_emit_cbc_literal (context_p, + index >= PARSER_REGISTER_START ? CBC_MOV_IDENT : CBC_ASSIGN_LET_CONST, + index); + } + else if (declaration_type == LEXER_KEYW_CONST) { parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST); } @@ -699,9 +724,7 @@ parser_parse_super_class_context_start (parser_context_t *context_p) /**< contex * Parse super class context like a with statement (ending part). */ void -parser_parse_super_class_context_end (parser_context_t *context_p, /**< context */ - bool is_statement) /**< true - if class is parsed as a statement - * false - otherwise (as an expression) */ +parser_parse_super_class_context_end (parser_context_t *context_p) /**< context */ { parser_with_statement_t with_statement; parser_stack_pop_uint8 (context_p); @@ -713,15 +736,7 @@ parser_parse_super_class_context_end (parser_context_t *context_p, /**< context PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); #endif /* !JERRY_NDEBUG */ - if (is_statement) - { - parser_emit_cbc (context_p, CBC_CONTEXT_END); - } - else - { - parser_emit_cbc_ext (context_p, CBC_EXT_CLASS_EXPR_CONTEXT_END); - } - + parser_emit_cbc_ext (context_p, CBC_EXT_CLASS_EXPR_CONTEXT_END); parser_set_branch_to_current_position (context_p, &with_statement.branch); } /* parser_parse_super_class_context_end */ #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index 34a24c969..9055f12d6 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -34,38 +34,42 @@ typedef struct const uint8_t *source_p; /**< start source byte */ } scanner_source_start_t; +/** + * Descriptor for storing a let/const literal on stack. + */ +typedef struct +{ + lexer_lit_location_t *literal_p; /**< let/const literal */ +} scanner_let_const_literal_t; + /** * Flags for type member of lexer_lit_location_t structure in the literal pool. */ typedef enum { SCANNER_LITERAL_IS_ARG = (1 << 0), /**< literal is argument */ - SCANNER_LITERAL_IS_LOCAL = (1 << 1), /**< literal is local (similar to let, - * but var is allowed with the same identifier) */ - SCANNER_LITERAL_IS_VAR = (1 << 2), /**< literal is var */ - SCANNER_LITERAL_IS_FUNC = (1 << 3), /**< literal is function */ - SCANNER_LITERAL_NO_REG = (1 << 4), /**< literal cannot be stored in register */ + SCANNER_LITERAL_IS_VAR = (1 << 1), /**< literal is var */ + SCANNER_LITERAL_IS_FUNC = (1 << 2), /**< literal is function */ + SCANNER_LITERAL_NO_REG = (1 << 3), /**< literal cannot be stored in register */ + SCANNER_LITERAL_IS_LET = (1 << 4), /**< literal is let */ + SCANNER_LITERAL_IS_CONST = (1 << 5), /**< literal is const */ #if ENABLED (JERRY_ES2015) - SCANNER_LITERAL_IS_LET = (1 << 5), /**< literal is let */ - SCANNER_LITERAL_IS_CONST = (1 << 6), /**< literal is const */ + SCANNER_LITERAL_IS_USED = (1 << 6), /**< literal is used */ #endif /* ENABLED (JERRY_ES2015) */ } scanner_literal_type_flags_t; -#if ENABLED (JERRY_ES2015) +/* + * Known combinations: + * + * SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET : function declared in this block, might be let or var + * SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_CONST : function declared in this block, must be let + * SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST : catch block variable + */ /** - * Tells whether the literal is let or const declaration. + * Literal is a local declration (let, const, catch variable, etc.) */ -#define SCANNER_LITERAL_IS_LET_OR_CONST (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) - -#else /* !ENABLED (JERRY_ES2015) */ - -/** - * No literal is let or const declaration in ECMAScript 5.1. - */ -#define SCANNER_LITERAL_IS_LET_OR_CONST 0 - -#endif /* ENABLED (JERRY_ES2015) */ +#define SCANNER_LITERAL_IS_LOCAL (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) /** * For statement descriptor. diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index f99855a81..6b1af3125 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -431,7 +431,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { search_arguments = false; - if (type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET_OR_CONST)) + if (type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LOCAL)) { arguments_required = false; } @@ -444,8 +444,7 @@ 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_LET_OR_CONST)) == SCANNER_LITERAL_IS_FUNC) + if (is_function && (type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LOCAL)) == SCANNER_LITERAL_IS_FUNC) { type = (uint8_t) ((type & ~SCANNER_LITERAL_IS_FUNC) | SCANNER_LITERAL_IS_VAR); literal_p->type = type; @@ -453,7 +452,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_ES2015) */ if ((is_function && (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG))) - || (type & (SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_LET_OR_CONST))) + || (type & SCANNER_LITERAL_IS_LOCAL)) { JERRY_ASSERT (is_function || !(literal_p->type & SCANNER_LITERAL_IS_ARG)); @@ -513,16 +512,20 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, prev_literal_pool_p, literal_p); + uint8_t extended_type = literal_location_p->type; if (is_function || (type & SCANNER_LITERAL_NO_REG)) { - literal_location_p->type |= SCANNER_LITERAL_NO_REG; + extended_type |= SCANNER_LITERAL_NO_REG; } #if ENABLED (JERRY_ES2015) - if (literal_location_p->type & SCANNER_LITERAL_IS_LET_OR_CONST) + extended_type |= SCANNER_LITERAL_IS_USED; + + if (literal_location_p->type & SCANNER_LITERAL_IS_LOCAL) { JERRY_ASSERT (!(type & SCANNER_LITERAL_IS_VAR)); + /* Clears the SCANNER_LITERAL_IS_FUNC flag. */ type = 0; } #endif /* ENABLED (JERRY_ES2015) */ @@ -530,7 +533,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ type = (uint8_t) (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)); JERRY_ASSERT (type == 0 || !is_function); - literal_location_p->type = (uint8_t) (literal_location_p->type | type); + literal_location_p->type = (uint8_t) (extended_type | type); } } @@ -594,7 +597,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) || (!(is_function && (literal_p->type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG))) - && !(literal_p->type & (SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_LET_OR_CONST)))) + && !(literal_p->type & SCANNER_LITERAL_IS_LOCAL))) { continue; } @@ -632,7 +635,10 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015) else if (literal_p->type & SCANNER_LITERAL_IS_LET) { - type = SCANNER_STREAM_TYPE_LET; + if (!(literal_p->type & SCANNER_LITERAL_IS_CONST)) + { + type = SCANNER_STREAM_TYPE_LET; + } } else if (literal_p->type & SCANNER_LITERAL_IS_CONST) { @@ -873,6 +879,10 @@ scanner_add_reference (parser_context_t *context_p, /**< context */ lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &context_p->token.lit_location); +#if ENABLED (JERRY_ES2015) + lit_location_p->type |= SCANNER_LITERAL_IS_USED; +#endif /* ENABLED (JERRY_ES2015) */ + if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { lit_location_p->type |= SCANNER_LITERAL_NO_REG; @@ -985,7 +995,7 @@ scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */ { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); - if (property_p != NULL && (*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)) + if (property_p != NULL && ecma_is_property_enumerable (*property_p)) { ecma_deref_ecma_string (name_p); return true; @@ -1001,7 +1011,7 @@ scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */ { ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); - if (property_p != NULL && (*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)) + if (property_p != NULL && ecma_is_property_enumerable (*property_p)) { ecma_deref_ecma_string (name_p); return true; @@ -1021,8 +1031,9 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ lexer_lit_location_t *var_literal_p) /**< literal */ { - if (var_literal_p->type & (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) - && !(var_literal_p->type & SCANNER_LITERAL_IS_FUNC)) + if (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL + && !(var_literal_p->type & SCANNER_LITERAL_IS_FUNC) + && (var_literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL) { scanner_raise_redeclaration_error (context_p); } @@ -1043,7 +1054,8 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { - if (literal_p->type & (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) + if (literal_p->type & SCANNER_LITERAL_IS_LOCAL + && (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL && literal_p->length == length) { if (JERRY_LIKELY (!literal_p->has_escape)) @@ -1066,7 +1078,8 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ { while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { - if (literal_p->type & (SCANNER_LITERAL_IS_LET | SCANNER_LITERAL_IS_CONST) + if (literal_p->type & SCANNER_LITERAL_IS_LOCAL + && (literal_p->type & SCANNER_LITERAL_IS_LOCAL) != SCANNER_LITERAL_IS_LOCAL && literal_p->length == length && lexer_compare_identifiers (literal_p->char_p, char_p, length)) { diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 802d5887d..64389e450 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -75,6 +75,7 @@ typedef enum #if ENABLED (JERRY_ES2015) SCAN_STACK_LET, /**< let statement */ SCAN_STACK_CONST, /**< const statement */ + SCAN_STACK_LET_CONST_INIT, /**< let/const statement initializer */ #endif /* ENABLED (JERRY_ES2015) */ /* The SCANNER_IS_FOR_START macro needs to be updated when the following constants are reordered. */ SCAN_STACK_VAR, /**< var statement */ @@ -523,6 +524,24 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } #if ENABLED (JERRY_ES2015) + case SCAN_STACK_LET_CONST_INIT: + { + scanner_let_const_literal_t let_const_literal; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); + + if (let_const_literal.literal_p->type & SCANNER_LITERAL_IS_USED) + { + let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + } + + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET + || context_p->stack_top_uint8 == SCAN_STACK_CONST); + + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + return SCAN_NEXT_TOKEN; + } case SCAN_STACK_ARROW_ARGUMENTS: { lexer_next_token (context_p); @@ -635,6 +654,24 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; } +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_LET_CONST_INIT: + { + scanner_let_const_literal_t let_const_literal; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); + + if (let_const_literal.literal_p->type & SCANNER_LITERAL_IS_USED) + { + let_const_literal.literal_p->type |= SCANNER_LITERAL_NO_REG; + } + + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_LET + || context_p->stack_top_uint8 == SCAN_STACK_CONST); + /* FALLTHRU */ + } +#endif /* ENABLED (JERRY_ES2015) */ case SCAN_STACK_VAR: #if ENABLED (JERRY_ES2015) case SCAN_STACK_LET: @@ -1214,7 +1251,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); #if ENABLED (JERRY_ES2015) - if (literal_p->type & SCANNER_LITERAL_IS_LET_OR_CONST + if (literal_p->type & SCANNER_LITERAL_IS_LOCAL && !(literal_p->type & SCANNER_LITERAL_IS_FUNC)) { scanner_raise_redeclaration_error (context_p); @@ -1252,9 +1289,8 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ lexer_lit_location_t *literal_p = scanner_add_literal (context_p, scanner_context_p); if (literal_p->type & (SCANNER_LITERAL_IS_ARG - | SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_VAR - | SCANNER_LITERAL_IS_LET_OR_CONST)) + | SCANNER_LITERAL_IS_LOCAL)) { scanner_raise_redeclaration_error (context_p); } @@ -1422,7 +1458,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); #if ENABLED (JERRY_ES2015) - if (location_p->type & SCANNER_LITERAL_IS_LET_OR_CONST + if (location_p->type & SCANNER_LITERAL_IS_LOCAL && !(location_p->type & SCANNER_LITERAL_IS_FUNC)) { scanner_raise_redeclaration_error (context_p); @@ -2160,9 +2196,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST) { if (literal_p->type & (SCANNER_LITERAL_IS_ARG - | SCANNER_LITERAL_IS_LOCAL | SCANNER_LITERAL_IS_VAR - | SCANNER_LITERAL_IS_LET_OR_CONST)) + | SCANNER_LITERAL_IS_LOCAL)) { scanner_raise_redeclaration_error (context_p); } @@ -2180,15 +2215,39 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ { literal_p->type |= SCANNER_LITERAL_IS_CONST; } + + lexer_next_token (context_p); + + if (literal_p->type & SCANNER_LITERAL_IS_USED) + { + literal_p->type |= SCANNER_LITERAL_NO_REG; + } + else if (context_p->token.type == LEXER_ASSIGN) + { + scanner_let_const_literal_t let_const_literal; + let_const_literal.literal_p = literal_p; + + parser_stack_push (context_p, &let_const_literal, sizeof (scanner_let_const_literal_t)); + parser_stack_push_uint8 (context_p, SCAN_STACK_LET_CONST_INIT); + } } - else if (!(literal_p->type & SCANNER_LITERAL_IS_VAR)) + else { - scanner_detect_invalid_var (context_p, &scanner_context, literal_p); - literal_p->type |= SCANNER_LITERAL_IS_VAR; + if (!(literal_p->type & SCANNER_LITERAL_IS_VAR)) + { + scanner_detect_invalid_var (context_p, &scanner_context, literal_p); + literal_p->type |= SCANNER_LITERAL_IS_VAR; + + if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + literal_p->type |= SCANNER_LITERAL_NO_REG; + } + } + + lexer_next_token (context_p); } #else /* !ENABLED (JERRY_ES2015) */ literal_p->type |= SCANNER_LITERAL_IS_VAR; -#endif /* ENABLED (JERRY_ES2015) */ if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) { @@ -2196,6 +2255,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } lexer_next_token (context_p); +#endif /* ENABLED (JERRY_ES2015) */ switch (context_p->token.type) { diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 342683644..ea0096205 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -828,9 +828,17 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { prop_attributes = ECMA_PROPERTY_FLAG_ENUMERABLE; } -#endif /* ENABLED (JERRY_ES2015) */ + ecma_property_value_t *property_value_p; + property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); + + if (type != CBC_CREATE_LOCAL) + { + property_value_p->value = ECMA_VALUE_UNINITIALIZED; + } +#else /* !ENABLED (JERRY_ES2015) */ ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); +#endif /* ENABLED (JERRY_ES2015) */ break; } @@ -1307,6 +1315,29 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ continue; } #if ENABLED (JERRY_ES2015) + case VM_OC_ASSIGN_LET_CONST: + { + uint32_t literal_index; + READ_LITERAL_INDEX (literal_index); + + JERRY_ASSERT (literal_index >= register_end); + JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_property_t *property_p = ecma_find_named_property (frame_ctx_p->lex_env_p, name_p); + + JERRY_ASSERT (property_p != NULL + && ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA); + JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p)->value == ECMA_VALUE_UNINITIALIZED); + + ECMA_PROPERTY_VALUE_PTR (property_p)->value = left_value; + + if (ecma_is_value_object (left_value)) + { + ecma_deref_object (ecma_get_object_from_value (left_value)); + } + continue; + } case VM_OC_SET_COMPUTED_PROPERTY: { /* Swap values. */ @@ -2012,7 +2043,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_MOV_IDENT: { - uint16_t literal_index; + uint32_t literal_index; READ_LITERAL_INDEX (literal_index); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 3c3102ce3..0146f6e2a 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -225,6 +225,7 @@ typedef enum VM_OC_LINE, /**< line number of the next statement */ #endif /* ENABLED (JERRY_LINE_INFO) */ #if ENABLED (JERRY_ES2015) + VM_OC_ASSIGN_LET_CONST, /**< assign values to let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ VM_OC_FOR_OF_CREATE_CONTEXT, /**< for of create context */ @@ -262,6 +263,7 @@ 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_ASSIGN_LET_CONST = VM_OC_NONE, /**< assign values to 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/let10.js b/tests/jerry/es2015/let10.js new file mode 100644 index 000000000..63cb50861 --- /dev/null +++ b/tests/jerry/es2015/let10.js @@ -0,0 +1,45 @@ +/* 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 check_reference_error (code) +{ + try { + eval (code); + assert (false); + } catch (e) { + assert (e instanceof ReferenceError); + } +} + +check_reference_error ("let b = a, a;"); +check_reference_error ("const b = b;"); +check_reference_error ("a; let b, a;"); +check_reference_error ("a = 1; let b, a;"); + +function f() { + return x + y.a; +} + +check_reference_error ("x"); +check_reference_error ("y"); +check_reference_error ("x = 1"); +check_reference_error ("y = 1"); + +let x = 6; +assert (x === 6); +let y = { a: 7 }; +assert (y.a === 7); + +assert (f() === 13); diff --git a/tests/jerry/try-eval.js b/tests/jerry/try-eval.js new file mode 100644 index 000000000..219bb44da --- /dev/null +++ b/tests/jerry/try-eval.js @@ -0,0 +1,31 @@ +// 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. + +try { + e; + assert (false); +} catch (e) { + eval("var e"); +} +assert (e === undefined); + +function f() { + try { + throw 1; + assert (false); + } catch (e) { + eval("var e"); + } +} +f(); diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index cc075b34f..0af657b3e 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x1B, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x01, 0x00,