From 3d797b88360e948ccdad3f3d30cdaf2f958f2db8 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Fri, 25 Oct 2019 18:08:10 +0200 Subject: [PATCH] Implement the core of let/const statement. (#3239) This patch implements the core part of let/const statements. Redeclarations are correctly detected and separate contexts are correctly created for these statements. Register optimizations are also emplyed whenever possible. Lots of features are still missing: - checking the var statements in eval - const are treated as lets - single statement checks are missing - export declarations are exported as vars, let/const export is not supported JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/parser/js/byte-code.h | 18 +- jerry-core/parser/js/common.h | 3 + jerry-core/parser/js/js-lexer.c | 11 +- jerry-core/parser/js/js-lexer.h | 25 +- jerry-core/parser/js/js-parser-expr.c | 8 + jerry-core/parser/js/js-parser-internal.h | 22 +- jerry-core/parser/js/js-parser-module.c | 4 +- jerry-core/parser/js/js-parser-statm.c | 284 +++++++- jerry-core/parser/js/js-parser-util.c | 4 + jerry-core/parser/js/js-parser.c | 53 +- jerry-core/parser/js/js-parser.h | 3 + jerry-core/parser/js/js-scanner-internal.h | 36 ++ jerry-core/parser/js/js-scanner-util.c | 612 ++++++++++++++++-- jerry-core/parser/js/js-scanner.c | 221 ++++++- jerry-core/parser/js/js-scanner.h | 23 +- jerry-core/vm/vm-stack.c | 45 +- jerry-core/vm/vm-stack.h | 38 +- jerry-core/vm/vm.c | 301 ++++++--- jerry-core/vm/vm.h | 4 + .../07/07.06/07.06.01/07.06.01-001.js | 2 +- tests/jerry/es2015/let1.js | 42 ++ tests/jerry/es2015/let2.js | 44 ++ tests/jerry/es2015/let3.js | 68 ++ tests/jerry/es2015/let4.js | 68 ++ tests/jerry/es2015/let5.js | 35 + tests/jerry/es2015/let6.js | 79 +++ tests/jerry/es2015/let7.js | 68 ++ tests/jerry/es2015/let8.js | 58 ++ tests/jerry/es5.1/func-decl.js | 25 + .../regression-test-issue-3123.js | 0 tests/jerry/func-decl.js | 2 +- tests/unit-core/test-snapshot.c | 6 +- 33 files changed, 1997 insertions(+), 217 deletions(-) create mode 100644 tests/jerry/es2015/let1.js create mode 100644 tests/jerry/es2015/let2.js create mode 100644 tests/jerry/es2015/let3.js create mode 100644 tests/jerry/es2015/let4.js create mode 100644 tests/jerry/es2015/let5.js create mode 100644 tests/jerry/es2015/let6.js create mode 100644 tests/jerry/es2015/let7.js create mode 100644 tests/jerry/es2015/let8.js create mode 100644 tests/jerry/es5.1/func-decl.js rename tests/jerry/{es2015 => fail}/regression-test-issue-3123.js (100%) diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 6753328ab..1c25b5080 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 (25u) +#define JERRY_SNAPSHOT_VERSION (26u) /** * 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 aa1c1a819..2891f573c 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -192,6 +192,8 @@ #define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 3 /* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 +/* PARSER_BLOCK_CONTEXT_STACK_ALLOCATION must be <= 3 */ +#define PARSER_BLOCK_CONTEXT_STACK_ALLOCATION 1 /* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ #define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 /* PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION must be <= 4 */ @@ -244,6 +246,10 @@ VM_OC_PUSH_ELISON | VM_OC_PUT_STACK) \ CBC_FORWARD_BRANCH (CBC_BRANCH_IF_STRICT_EQUAL, -1, \ VM_OC_BRANCH_IF_STRICT_EQUAL) \ + CBC_OPCODE (CBC_PUSH_NULL, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_NULL | VM_OC_PUT_STACK) \ + CBC_FORWARD_BRANCH (CBC_BLOCK_CREATE_CONTEXT, \ + PARSER_BLOCK_CONTEXT_STACK_ALLOCATION, VM_OC_BLOCK_CREATE_CONTEXT) \ \ /* Basic opcodes. Note: These 4 opcodes must me in this order */ \ CBC_OPCODE (CBC_PUSH_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ @@ -260,8 +266,6 @@ VM_OC_PUSH_TRUE | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_FALSE, CBC_NO_FLAG, 1, \ VM_OC_PUSH_FALSE | VM_OC_PUT_STACK) \ - CBC_OPCODE (CBC_PUSH_NULL, CBC_NO_FLAG, 1, \ - VM_OC_PUSH_NULL | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_THIS, CBC_NO_FLAG, 1, \ VM_OC_PUSH_THIS | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_PUSH_NUMBER_0, CBC_NO_FLAG, 1, \ @@ -302,6 +306,10 @@ VM_OC_NONE) \ CBC_OPCODE (CBC_INIT_LOCAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_NONE) \ + CBC_OPCODE (CBC_CREATE_VAR_FUNC, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ + CBC_OPCODE (CBC_SET_VAR_FUNC, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_ASSIGN | VM_OC_GET_LITERAL | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_SET_BYTECODE_PTR, CBC_NO_FLAG, 0, \ VM_OC_NONE) \ CBC_OPCODE (CBC_RETURN, CBC_NO_FLAG, -1, \ @@ -508,8 +516,8 @@ VM_OC_SET_SETTER | VM_OC_NON_STATIC_FLAG | VM_OC_GET_LITERAL_LITERAL) \ CBC_FORWARD_BRANCH (CBC_EXT_TRY_CREATE_CONTEXT, PARSER_TRY_CONTEXT_STACK_ALLOCATION, \ VM_OC_TRY) \ - CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \ - VM_OC_THROW_REFERENCE_ERROR) \ + CBC_OPCODE (CBC_EXT_TRY_CREATE_ENV, CBC_NO_FLAG, 0, \ + VM_OC_BLOCK_CREATE_CONTEXT) \ CBC_FORWARD_BRANCH (CBC_EXT_CATCH, 1, \ VM_OC_CATCH) \ CBC_OPCODE (CBC_EXT_PUSH_UNDEFINED_BASE, CBC_NO_FLAG, 1, \ @@ -554,6 +562,8 @@ VM_OC_SET_SETTER | VM_OC_GET_STACK_LITERAL) \ CBC_OPCODE (CBC_EXT_RESOLVE_BASE, CBC_NO_FLAG, 0, \ VM_OC_RESOLVE_BASE_FOR_CALL) \ + CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \ + VM_OC_THROW_REFERENCE_ERROR) \ \ /* Class opcodes */ \ CBC_OPCODE (CBC_EXT_INHERIT_AND_SET_CONSTRUCTOR, CBC_NO_FLAG, 0, \ diff --git a/jerry-core/parser/js/common.h b/jerry-core/parser/js/common.h index 65fe8cc25..3f504c90b 100644 --- a/jerry-core/parser/js/common.h +++ b/jerry-core/parser/js/common.h @@ -74,6 +74,9 @@ typedef enum LEXER_FLAG_SOURCE_PTR = (1 << 2), /**< the literal is directly referenced in the source code * (no need to allocate memory) */ LEXER_FLAG_LATE_INIT = (1 << 3), /**< initialize this variable after the byte code is freed */ +#if ENABLED (JERRY_ES2015) + LEXER_FLAG_GLOBAL = (1 << 4), /**< this local identifier is not a let or const declaration */ +#endif /* ENABLED (JERRY_ES2015) */ } lexer_literal_status_flags_t; /** diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index e7575060c..88eface52 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -1476,7 +1476,14 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ { scope_stack_p--; - if (scope_stack_p->map_from == literal_index) +#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) { JERRY_ASSERT (scope_stack_p->map_to >= PARSER_REGISTER_START || (literal_p->status_flags & LEXER_FLAG_USED)); @@ -2322,7 +2329,7 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ context_p->token.literal_is_reserved = false; context_p->token.lit_location.type = LEXER_IDENT_LITERAL; context_p->token.lit_location.has_escape = false; - lexer_construct_literal_object (context_p, &lexer_default_literal, lexer_default_literal.type); + lexer_construct_literal_object (context_p, &lexer_default_literal, literal_type); context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC); return; } diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 04e159850..017a76e4a 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -151,10 +151,6 @@ typedef enum LEXER_COMMA_SEP_LIST, /**< comma separated bracketed expression list */ LEXER_SCAN_SWITCH, /**< special value for switch pre-scan */ LEXER_CLASS_CONSTRUCTOR, /**< special value for class constructor method */ -#if ENABLED (JERRY_ES2015) - LEXER_FOR_IN_OF, /**< special value during for in/of statmenet scanning */ - LEXER_LITERAL_OF, /**< 'of' literal */ -#endif /* ENABLED (JERRY_ES2015) */ #if !ENABLED (JERRY_ES2015) /* Future reserved words: these keywords @@ -164,6 +160,9 @@ typedef enum LEXER_KEYW_CLASS, /**< class */ LEXER_KEYW_EXTENDS, /**< extends */ LEXER_KEYW_SUPER, /**< super */ +#if ENABLED (JERRY_ES2015) + LEXER_KEYW_LET, /**< let */ +#endif /* ENABLED (JERRY_ES2015) */ LEXER_KEYW_CONST, /**< const */ LEXER_KEYW_EXPORT, /**< export */ LEXER_KEYW_IMPORT, /**< import */ @@ -190,25 +189,13 @@ typedef enum LEXER_KEYW_PACKAGE, /**< package */ LEXER_KEYW_PROTECTED, /**< protected */ -#if ENABLED (JERRY_ES2015) - /* Context dependent strict reserved words: - * See also: ECMA-262 v6, 11.6.2.1 */ -#define LEXER_FIRST_CONTEXT_DEPENDENT_RESERVED_WORD LEXER_KEYW_STATIC - LEXER_KEYW_STATIC, /**< static */ -#else /* !ENABLED (JERRY_ES2015) */ - /* Context dependent strict reserved words: - * See also: ECMA-262 v6, 11.6.2.1 */ -#define LEXER_FIRST_CONTEXT_DEPENDENT_RESERVED_WORD -#endif /* ENABLED (JERRY_ES2015) */ - /* Context dependent future strict reserved words: * See also: ECMA-262 v6, 11.6.2.1 */ -#define LEXER_FIRST_CONTEXT_DEPENDENT_FUTURE_RESERVED_WORD LEXER_KEYW_LET - LEXER_KEYW_LET, /**< let */ - LEXER_KEYW_YIELD, /**< yield */ #if !ENABLED (JERRY_ES2015) - LEXER_KEYW_STATIC, /**< static */ + LEXER_KEYW_LET, /**< let */ #endif /* !ENABLED (JERRY_ES2015) */ + LEXER_KEYW_YIELD, /**< yield */ + LEXER_KEYW_STATIC, /**< static */ } lexer_token_type_t; #define LEXER_NEWLINE_LS_PS_BYTE_1 0xe2 diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index a4db37045..7a59adb98 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -595,6 +595,14 @@ 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) diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index cafba078e..06fbd0c00 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -64,9 +64,10 @@ typedef enum PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 15), /**< pending (unsent) breakpoint * info is available */ #if ENABLED (JERRY_ES2015) - PARSER_FUNCTION_HAS_REST_PARAM = (1u << 16), /**< function has rest parameter */ - PARSER_IS_ARROW_FUNCTION = (1u << 18), /**< an arrow function is parsed */ - PARSER_ARROW_PARSE_ARGS = (1u << 19), /**< parse the argument list of an arrow function */ + PARSER_INSIDE_BLOCK = (1u << 16), /**< script has a lexical environment for let and const */ + PARSER_IS_ARROW_FUNCTION = (1u << 17), /**< an arrow function is parsed */ + PARSER_ARROW_PARSE_ARGS = (1u << 18), /**< parse the argument list of an arrow function */ + PARSER_FUNCTION_HAS_REST_PARAM = (1u << 19), /**< function has rest parameter */ /* These four status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed (this value must be kept in * in sync with ECMA_PARSE_CLASS_CONSTRUCTOR) */ @@ -371,8 +372,11 @@ typedef struct parser_saved_context_t parser_mem_data_t literal_pool_data; /**< literal list */ parser_scope_stack *scope_stack_p; /**< scope stack */ uint16_t scope_stack_size; /**< size of scope stack */ - uint16_t scope_stack_top; /**< current top of scope stack */ - uint16_t scope_stack_reg_top; /**< current top register of scope stack */ + uint16_t scope_stack_top; /**< preserved top of scope stack */ + uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */ +#if ENABLED (JERRY_ES2015) + uint16_t scope_stack_global_end; /**< end of global declarations of a function */ +#endif /* ENABLED (JERRY_ES2015) */ #ifndef JERRY_NDEBUG uint16_t context_stack_depth; /**< current context stack depth */ @@ -440,6 +444,9 @@ typedef struct uint16_t scope_stack_size; /**< size of scope stack */ uint16_t scope_stack_top; /**< current top of scope stack */ uint16_t scope_stack_reg_top; /**< current top register of scope stack */ +#if ENABLED (JERRY_ES2015) + uint16_t scope_stack_global_end; /**< end of global declarations of a function */ +#endif /* ENABLED (JERRY_ES2015) */ uint8_t stack_top_uint8; /**< top byte stored on the stack */ #ifndef JERRY_NDEBUG @@ -615,10 +622,6 @@ void parser_parse_super_class_context_end (parser_context_t *context_p, bool is_ * @{ */ -void scanner_raise_error (parser_context_t *context_p); -void *scanner_malloc (parser_context_t *context_p, size_t size); -void scanner_free (void *ptr, size_t size); - void scanner_release_next (parser_context_t *context_p, size_t size); void scanner_set_active (parser_context_t *context_p); void scanner_release_active (parser_context_t *context_p, size_t size); @@ -627,6 +630,7 @@ void scanner_seek (parser_context_t *context_p); void scanner_reverse_info_list (parser_context_t *context_p); void scanner_cleanup (parser_context_t *context_p); +bool scanner_is_context_needed (parser_context_t *context_p); void scanner_create_variables (parser_context_t *context_p, size_t size); void scanner_get_location (scanner_location_t *location_p, parser_context_t *context_p); diff --git a/jerry-core/parser/js/js-parser-module.c b/jerry-core/parser/js/js-parser-module.c index 0878931e4..da5ebe061 100644 --- a/jerry-core/parser/js/js-parser-module.c +++ b/jerry-core/parser/js/js-parser-module.c @@ -438,7 +438,7 @@ parser_module_parse_import_clause (parser_context_t *context_p) /**< parser cont ecma_string_t *import_name_p = NULL; ecma_string_t *local_name_p = NULL; - lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); uint16_t import_name_index = context_p->lit_object.index; uint16_t local_name_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS; @@ -454,7 +454,7 @@ parser_module_parse_import_clause (parser_context_t *context_p) /**< parser cont parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } - lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); local_name_index = context_p->lit_object.index; diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 82bd11d04..3a71d116f 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -57,12 +57,18 @@ typedef enum { PARSER_STATEMENT_START, PARSER_STATEMENT_BLOCK, +#if ENABLED (JERRY_ES2015) + PARSER_STATEMENT_BLOCK_CONTEXT, +#endif /* ENABLED (JERRY_ES2015) */ PARSER_STATEMENT_LABEL, PARSER_STATEMENT_IF, PARSER_STATEMENT_ELSE, /* From switch -> for-in : break target statements */ PARSER_STATEMENT_SWITCH, PARSER_STATEMENT_SWITCH_NO_DEFAULT, +#if ENABLED (JERRY_ES2015) + PARSER_STATEMENT_SWITCH_BLOCK_CONTEXT, +#endif /* ENABLED (JERRY_ES2015) */ /* From do-while -> for->in : continue target statements */ PARSER_STATEMENT_DO_WHILE, PARSER_STATEMENT_WHILE, @@ -78,6 +84,40 @@ typedef enum PARSER_STATEMENT_TRY, } parser_statement_type_t; +#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) + +/** + * Block statement. + */ +typedef struct +{ + uint16_t scope_stack_top; /**< preserved top of scope stack */ + uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */ +} parser_block_statement_t; + +/** + * Context of block statement. + */ +typedef struct +{ + parser_branch_t branch; /**< branch to the end */ +} parser_block_context_t; + +#endif /* !ENABLED (JERRY_ES2015) */ + /** * Loop statement. */ @@ -110,6 +150,10 @@ typedef struct { parser_branch_t default_branch; /**< branch to the default case */ parser_branch_node_t *branch_list_p; /**< branches of case statements */ +#if ENABLED (JERRY_ES2015) + uint16_t scope_stack_top; /**< preserved top of scope stack */ + uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */ +#endif /* ENABLED (JERRY_ES2015) */ } parser_switch_statement_t; /** @@ -190,8 +234,15 @@ parser_statement_length (uint8_t type) /**< type of statement */ { static const uint8_t statement_lengths[] = { +#if ENABLED (JERRY_ES2015) + /* PARSER_STATEMENT_BLOCK */ + (uint8_t) (sizeof (parser_block_statement_t) + 1), + /* PARSER_STATEMENT_BLOCK_CONTEXT */ + (uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1), +#else /* !ENABLED (JERRY_ES2015) */ /* PARSER_STATEMENT_BLOCK */ 1, +#endif /* ENABLED (JERRY_ES2015) */ /* PARSER_STATEMENT_LABEL */ (uint8_t) (sizeof (parser_label_statement_t) + 1), /* PARSER_STATEMENT_IF */ @@ -202,6 +253,10 @@ parser_statement_length (uint8_t type) /**< type of statement */ (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_SWITCH_NO_DEFAULT */ (uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1), +#if ENABLED (JERRY_ES2015) + /* PARSER_STATEMENT_SWITCH_BLOCK_CONTEXT */ + (uint8_t) (sizeof (parser_block_context_t) + 1), +#endif /* ENABLED (JERRY_ES2015) */ /* PARSER_STATEMENT_DO_WHILE */ (uint8_t) (sizeof (parser_do_while_statement_t) + sizeof (parser_loop_statement_t) + 1), /* PARSER_STATEMENT_WHILE */ @@ -293,7 +348,9 @@ parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */ static void parser_parse_var_statement (parser_context_t *context_p) /**< context */ { - JERRY_ASSERT (context_p->token.type == LEXER_KEYW_VAR); + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_VAR + || context_p->token.type == LEXER_KEYW_LET + || context_p->token.type == LEXER_KEYW_CONST); while (true) { @@ -313,6 +370,14 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ +#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) */ + lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) @@ -368,6 +433,14 @@ parser_parse_function_statement (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) */ + if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) { context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; @@ -1185,6 +1258,33 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context * parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } +#if ENABLED (JERRY_ES2015) + switch_statement.scope_stack_top = context_p->scope_stack_top; + switch_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + + if (context_p->next_scanner_info_p->source_p == context_p->source_p - 1) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); + + if (scanner_is_context_needed (context_p)) + { + parser_block_context_t block_context; + +#ifndef JERRY_NDEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_emit_cbc_forward_branch (context_p, + CBC_BLOCK_CREATE_CONTEXT, + &block_context.branch); + parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t)); + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_SWITCH_BLOCK_CONTEXT); + } + + scanner_create_variables (context_p, sizeof (scanner_info_t)); + } +#endif /* ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_SWITCH); @@ -1201,6 +1301,14 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context * parser_emit_cbc (context_p, CBC_POP); parser_flush_cbc (context_p); + +#if ENABLED (JERRY_ES2015) + parser_block_statement_t block_statement; + block_statement.scope_stack_top = context_p->scope_stack_top; + block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); parser_stack_iterator_init (context_p, &context_p->last_statement); return; @@ -1349,6 +1457,11 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_stack_iterator_skip (&iterator, 1); parser_stack_iterator_read (&iterator, &try_statement, sizeof (parser_try_statement_t)); +#if ENABLED (JERRY_ES2015) + context_p->scope_stack_top = try_statement.scope_stack_top; + context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top; +#endif /* ENABLED (JERRY_ES2015) */ + lexer_next_token (context_p); if (try_statement.type == parser_finally_block) @@ -1368,8 +1481,10 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ if (try_statement.type == parser_catch_block) { +#if !ENABLED (JERRY_ES2015) context_p->scope_stack_top = try_statement.scope_stack_top; context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top; +#endif /* !ENABLED (JERRY_ES2015) */ if (context_p->token.type != LEXER_KEYW_FINALLY) { @@ -1423,12 +1538,18 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ bool block_found = false; #endif /* !JERRY_NDEBUG */ - if (context_p->next_scanner_info_p->source_p == context_p->source_p - && context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK) + if (context_p->next_scanner_info_p->source_p == context_p->source_p) { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); #ifndef JERRY_NDEBUG block_found = true; #endif /* !JERRY_NDEBUG */ + + if (scanner_is_context_needed (context_p)) + { + parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); + } + scanner_create_variables (context_p, sizeof (scanner_info_t)); } @@ -1474,6 +1595,20 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_FINALLY, &try_statement.branch); + +#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_BLOCK); + + if (scanner_is_context_needed (context_p)) + { + parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); + } + + scanner_create_variables (context_p, sizeof (scanner_info_t)); + } +#endif /* ENABLED (JERRY_ES2015) */ } lexer_next_token (context_p); @@ -1585,6 +1720,9 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */ || type == PARSER_STATEMENT_FOR_OF #endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_WITH +#if ENABLED (JERRY_ES2015) + || type == PARSER_STATEMENT_BLOCK_CONTEXT +#endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_TRY) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; @@ -1629,6 +1767,9 @@ parser_parse_break_statement (parser_context_t *context_p) /**< context */ || type == PARSER_STATEMENT_FOR_OF #endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_WITH +#if ENABLED (JERRY_ES2015) + || type == PARSER_STATEMENT_BLOCK_CONTEXT +#endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_TRY) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; @@ -1723,6 +1864,9 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */ #endif /* ENABLED (JERRY_ES2015) */ if (type == PARSER_STATEMENT_WITH +#if ENABLED (JERRY_ES2015) + || type == PARSER_STATEMENT_BLOCK_CONTEXT +#endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_TRY || for_in_of_was_seen) { @@ -1782,6 +1926,9 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */ } if (type == PARSER_STATEMENT_WITH +#if ENABLED (JERRY_ES2015) + || type == PARSER_STATEMENT_BLOCK_CONTEXT +#endif /* ENABLED (JERRY_ES2015) */ || type == PARSER_STATEMENT_TRY) { opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT; @@ -1993,6 +2140,10 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ break; } case LEXER_KEYW_VAR: +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_LET: + case LEXER_KEYW_CONST: +#endif /* ENABLED (JERRY_ES2015) */ { context_p->status_flags |= PARSER_MODULE_STORE_IDENT; parser_parse_var_statement (context_p); @@ -2151,7 +2302,10 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ lexer_lit_location_t lit_location; uint32_t status_flags = context_p->status_flags; - JERRY_ASSERT (context_p->stack_depth == 0); + JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p)); +#ifndef JERRY_NDEBUG + JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth); +#endif /* !JERRY_NDEBUG */ lit_location = context_p->token.lit_location; @@ -2246,6 +2400,8 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ && context_p->token.type != LEXER_LEFT_BRACE && context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_KEYW_VAR + && context_p->token.type != LEXER_KEYW_LET + && context_p->token.type != LEXER_KEYW_CONST && context_p->token.type != LEXER_KEYW_FUNCTION && context_p->token.type != LEXER_KEYW_CASE && context_p->token.type != LEXER_KEYW_DEFAULT) @@ -2265,6 +2421,8 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ && context_p->token.type != LEXER_LEFT_BRACE && context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_KEYW_VAR + && context_p->token.type != LEXER_KEYW_LET + && context_p->token.type != LEXER_KEYW_CONST && context_p->token.type != LEXER_KEYW_FUNCTION && context_p->token.type != LEXER_KEYW_CASE && context_p->token.type != LEXER_KEYW_DEFAULT) @@ -2301,13 +2459,49 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ case LEXER_LEFT_BRACE: { - parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK); + uint8_t block_type = PARSER_STATEMENT_BLOCK; + +#if ENABLED (JERRY_ES2015) + parser_block_statement_t block_statement; + block_statement.scope_stack_top = context_p->scope_stack_top; + block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); + + if (scanner_is_context_needed (context_p)) + { + parser_block_context_t block_context; + +#ifndef JERRY_NDEBUG + PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_emit_cbc_forward_branch (context_p, + CBC_BLOCK_CREATE_CONTEXT, + &block_context.branch); + parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t)); + block_type = PARSER_STATEMENT_BLOCK_CONTEXT; + } + + scanner_create_variables (context_p, sizeof (scanner_info_t)); + } + + parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); +#endif /* ENABLED (JERRY_ES2015) */ + + parser_stack_push_uint8 (context_p, block_type); parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); continue; } case LEXER_KEYW_VAR: +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_LET: + case LEXER_KEYW_CONST: +#endif /* ENABLED (JERRY_ES2015) */ { parser_parse_var_statement (context_p); break; @@ -2409,6 +2603,23 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ CBC_EXT_TRY_CREATE_CONTEXT, &try_statement.branch); +#if ENABLED (JERRY_ES2015) + try_statement.scope_stack_top = context_p->scope_stack_top; + try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + + if (context_p->next_scanner_info_p->source_p == context_p->source_p) + { + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK); + + if (scanner_is_context_needed (context_p)) + { + parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); + } + + scanner_create_variables (context_p, sizeof (scanner_info_t)); + } +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_push (context_p, &try_statement, sizeof (parser_try_statement_t)); parser_stack_push_uint8 (context_p, PARSER_STATEMENT_TRY); parser_stack_iterator_init (context_p, &context_p->last_statement); @@ -2575,9 +2786,43 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK) { parser_stack_pop_uint8 (context_p); + +#if ENABLED (JERRY_ES2015) + parser_block_statement_t block_statement; + + parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); + context_p->scope_stack_top = block_statement.scope_stack_top; + context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_iterator_init (context_p, &context_p->last_statement); lexer_next_token (context_p); } +#if ENABLED (JERRY_ES2015) + else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT) + { + parser_block_statement_t block_statement; + parser_block_context_t block_context; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t)); + parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t)); + + context_p->scope_stack_top = block_statement.scope_stack_top; + context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top; + + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#ifndef JERRY_NDEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_set_branch_to_current_position (context_p, &block_context.branch); + + parser_stack_iterator_init (context_p, &context_p->last_statement); + lexer_next_token (context_p); + } +#endif /* ENABLED (JERRY_ES2015) */ else if (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH || context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH_NO_DEFAULT) { @@ -2590,6 +2835,11 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ parser_stack_pop (context_p, &switch_statement, sizeof (parser_switch_statement_t)); parser_stack_iterator_init (context_p, &context_p->last_statement); +#if ENABLED (JERRY_ES2015) + context_p->scope_stack_top = switch_statement.scope_stack_top; + context_p->scope_stack_reg_top = switch_statement.scope_stack_reg_top; +#endif /* ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (switch_statement.branch_list_p == NULL); if (!has_default) @@ -2599,6 +2849,26 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ parser_set_breaks_to_current_position (context_p, loop.branch_list_p); lexer_next_token (context_p); + +#if ENABLED (JERRY_ES2015) + if (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH_BLOCK_CONTEXT) + { + parser_block_context_t block_context; + + parser_stack_pop_uint8 (context_p); + parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t)); + + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#ifndef JERRY_NDEBUG + PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); +#endif /* !JERRY_NDEBUG */ + + parser_emit_cbc (context_p, CBC_CONTEXT_END); + parser_set_branch_to_current_position (context_p, &block_context.branch); + + parser_stack_iterator_init (context_p, &context_p->last_statement); + } +#endif /* ENABLED (JERRY_ES2015) */ } else if (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY) { @@ -2762,9 +3032,9 @@ consume_last_statement: } } - JERRY_ASSERT (context_p->stack_depth == 0); + JERRY_ASSERT (context_p->stack_depth == JERRY_GET_EXPECTED_DEPTH (context_p)); #ifndef JERRY_NDEBUG - JERRY_ASSERT (context_p->context_stack_depth == 0); + JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth); #endif /* !JERRY_NDEBUG */ parser_stack_pop_uint8 (context_p); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index f390d3647..833e36d4a 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1117,6 +1117,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Rest parameter may not have a default initializer."; } + case PARSER_ERR_VARIABLE_REDECLARED: + { + return "Local variable is redeclared."; + } #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) case PARSER_ERR_FILE_NOT_FOUND: diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 6334ce2cb..7a0104dd3 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1813,6 +1813,9 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.scope_stack_size = 0; context.scope_stack_top = 0; context.scope_stack_reg_top = 0; +#if ENABLED (JERRY_ES2015) + context.scope_stack_global_end = 0; +#endif /* ENABLED (JERRY_ES2015) */ #ifndef JERRY_NDEBUG context.context_stack_depth = 0; @@ -1896,12 +1899,51 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } else { - JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); + JERRY_ASSERT (context.next_scanner_info_p->source_p == source_p - 1 + && context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); scanner_create_variables (&context, sizeof (scanner_function_info_t)); + +#if ENABLED (JERRY_ES2015) + if (context.next_scanner_info_p->source_p == source_p) + { + JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_BLOCK); + + if (scanner_is_context_needed (&context)) + { + parser_branch_t branch; + +#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; + } + + scanner_create_variables (&context, sizeof (scanner_info_t)); + context.scope_stack_global_end = context.scope_stack_top; + } +#endif /* ENABLED (JERRY_ES2015) */ } 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 @@ -2022,6 +2064,9 @@ parser_save_context (parser_context_t *context_p, /**< context */ saved_context_p->scope_stack_size = context_p->scope_stack_size; saved_context_p->scope_stack_top = context_p->scope_stack_top; saved_context_p->scope_stack_reg_top = context_p->scope_stack_reg_top; +#if ENABLED (JERRY_ES2015) + saved_context_p->scope_stack_global_end = context_p->scope_stack_global_end; +#endif /* ENABLED (JERRY_ES2015) */ #ifndef JERRY_NDEBUG saved_context_p->context_stack_depth = context_p->context_stack_depth; @@ -2046,6 +2091,9 @@ parser_save_context (parser_context_t *context_p, /**< context */ context_p->scope_stack_size = 0; context_p->scope_stack_top = 0; context_p->scope_stack_reg_top = 0; +#if ENABLED (JERRY_ES2015) + context_p->scope_stack_global_end = 0; +#endif /* ENABLED (JERRY_ES2015) */ #ifndef JERRY_NDEBUG context_p->context_stack_depth = 0; @@ -2087,6 +2135,9 @@ parser_restore_context (parser_context_t *context_p, /**< context */ context_p->scope_stack_size = saved_context_p->scope_stack_size; context_p->scope_stack_top = saved_context_p->scope_stack_top; context_p->scope_stack_reg_top = saved_context_p->scope_stack_reg_top; +#if ENABLED (JERRY_ES2015) + context_p->scope_stack_global_end = saved_context_p->scope_stack_global_end; +#endif /* ENABLED (JERRY_ES2015) */ #ifndef JERRY_NDEBUG context_p->context_stack_depth = saved_context_p->context_stack_depth; diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index c6284c87c..fba7965a3 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -112,6 +112,9 @@ typedef enum PARSER_ERR_INVALID_RIGHT_SQUARE, /**< right square must terminate a block */ PARSER_ERR_DUPLICATED_LABEL, /**< duplicated label */ PARSER_ERR_OBJECT_PROPERTY_REDEFINED, /**< property of object literal redefined */ +#if ENABLED (JERRY_ES2015) + PARSER_ERR_VARIABLE_REDECLARED, /**< a variable redeclared */ +#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015) PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, /**< multiple class constructor */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index 3bf6d4426..0a797a800 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -45,8 +45,28 @@ typedef enum 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 */ +#if ENABLED (JERRY_ES2015) + SCANNER_LITERAL_IS_LET = (1 << 5), /**< literal is let */ + SCANNER_LITERAL_IS_CONST = (1 << 6), /**< literal is const */ +#endif /* ENABLED (JERRY_ES2015) */ } scanner_literal_type_flags_t; +#if ENABLED (JERRY_ES2015) + +/** + * Tells whether the literal is let or const declaration. + */ +#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) */ + /** * For statement descriptor. */ @@ -112,6 +132,14 @@ struct scanner_context_t scanner_info_t *end_arguments_p; /**< position of end arguments */ }; +void scanner_raise_error (parser_context_t *context_p); +#if ENABLED (JERRY_ES2015) +void scanner_raise_redeclaration_error (parser_context_t *context_p); +#endif /* ENABLED (JERRY_ES2015) */ + +void *scanner_malloc (parser_context_t *context_p, size_t size); +void scanner_free (void *ptr, size_t size); + size_t scanner_get_stream_size (scanner_info_t *info_p, size_t size); scanner_info_t *scanner_insert_info (parser_context_t *context_p, const uint8_t *source_p, size_t size); scanner_info_t *scanner_insert_info_before (parser_context_t *context_p, const uint8_t *source_p, @@ -119,14 +147,22 @@ scanner_info_t *scanner_insert_info_before (parser_context_t *context_p, const u scanner_literal_pool_t *scanner_push_literal_pool (parser_context_t *context_p, scanner_context_t *scanner_context_p, uint16_t status_flags); void scanner_pop_literal_pool (parser_context_t *context_p, scanner_context_t *scanner_context_p); +#if ENABLED (JERRY_ES2015) +void scanner_construct_global_block (parser_context_t *context_p, scanner_context_t *scanner_context_p); +#endif /* ENABLED (JERRY_ES2015) */ void scanner_filter_arguments (parser_context_t *context_p, scanner_context_t *scanner_context_p); lexer_lit_location_t *scanner_add_custom_literal (parser_context_t *context_p, scanner_literal_pool_t *literal_pool_p, const lexer_lit_location_t *literal_location_p); lexer_lit_location_t *scanner_add_literal (parser_context_t *context_p, scanner_context_t *scanner_context_p); 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) +void scanner_detect_invalid_var (parser_context_t *context_p, scanner_context_t *scanner_context_p, + lexer_lit_location_t *var_literal_p); +#endif /* ENABLED (JERRY_ES2015) */ void scanner_detect_eval_call (parser_context_t *context_p, scanner_context_t *scanner_context_p); + /** * @} * @} diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index bce4e3728..565ebea39 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -43,6 +43,22 @@ scanner_raise_error (parser_context_t *context_p) /**< context */ JERRY_ASSERT (0); } /* scanner_raise_error */ +#if ENABLED (JERRY_ES2015) + +/** + * Raise a variable redeclaration error. + */ +void +scanner_raise_redeclaration_error (parser_context_t *context_p) +{ + scanner_info_t *info_p = scanner_insert_info (context_p, context_p->source_p, sizeof (scanner_info_t)); + info_p->type = SCANNER_TYPE_ERR_REDECLARED; + + scanner_raise_error (context_p); +} /* scanner_raise_redeclaration_error */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Allocate memory for scanner. * @@ -94,10 +110,17 @@ scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ { switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) { + case SCANNER_STREAM_TYPE_VAR: +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_LET: + case SCANNER_STREAM_TYPE_CONST: +#endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_ARG: case SCANNER_STREAM_TYPE_ARG_FUNC: - case SCANNER_STREAM_TYPE_VAR: case SCANNER_STREAM_TYPE_FUNC: +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_VAR_FUNC: +#endif /* ENABLED (JERRY_ES2015) */ { break; } @@ -404,7 +427,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { search_arguments = false; - if (type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC)) + if (type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET_OR_CONST)) { arguments_required = false; } @@ -416,8 +439,16 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ } } - if (((type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG)) && is_function) - || (type & SCANNER_LITERAL_IS_LOCAL)) +#if ENABLED (JERRY_ES2015) + if (is_function && (type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET)) == SCANNER_LITERAL_IS_FUNC) + { + type = (uint8_t) ((type & ~SCANNER_LITERAL_IS_FUNC) | SCANNER_LITERAL_IS_VAR); + literal_p->type = type; + } +#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))) { JERRY_ASSERT (is_function || !(literal_p->type & SCANNER_LITERAL_IS_ARG)); @@ -457,7 +488,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ prev_source_p = literal_p->char_p + literal_p->length; - if (is_function || !(type & SCANNER_LITERAL_IS_VAR)) + if (is_function || !(type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC))) { continue; } @@ -475,7 +506,15 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ literal_location_p->type |= SCANNER_LITERAL_NO_REG; } - type = (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)); +#if ENABLED (JERRY_ES2015) + if (literal_location_p->type & SCANNER_LITERAL_IS_LET_OR_CONST) + { + JERRY_ASSERT (!(type & SCANNER_LITERAL_IS_VAR)); + type = 0; + } +#endif /* ENABLED (JERRY_ES2015) */ + + 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); @@ -532,21 +571,17 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ data_p += sizeof (scanner_info_t); JERRY_ASSERT (prev_literal_pool_p != NULL); - if (prev_literal_pool_p->no_declarations < no_declarations) - { - prev_literal_pool_p->no_declarations = (uint16_t) no_declarations; - } } parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); prev_source_p = literal_pool_p->source_p - 1; - no_declarations = 0; + no_declarations = literal_pool_p->no_declarations; while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) { if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) - || (!((literal_p->type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG)) && is_function) - && !(literal_p->type & SCANNER_LITERAL_IS_LOCAL))) + || (!(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)))) { continue; } @@ -575,6 +610,16 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { type = SCANNER_STREAM_TYPE_ARG; } +#if ENABLED (JERRY_ES2015) + else if (literal_p->type & SCANNER_LITERAL_IS_LET) + { + type = SCANNER_STREAM_TYPE_LET; + } + else if (literal_p->type & SCANNER_LITERAL_IS_CONST) + { + type = SCANNER_STREAM_TYPE_CONST; + } +#endif /* ENABLED (JERRY_ES2015) */ if (literal_p->has_escape) { @@ -624,12 +669,192 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ JERRY_ASSERT (((uint8_t *) info_p) + compressed_size == data_p + 1); } + if (!is_function && prev_literal_pool_p->no_declarations < no_declarations) + { + prev_literal_pool_p->no_declarations = (uint16_t) no_declarations; + } + scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p; parser_list_free (&literal_pool_p->literal_pool); scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); } /* scanner_pop_literal_pool */ +#if ENABLED (JERRY_ES2015) + +/** + * Extract certain (e.g. let/const) global declarations and construct a scanner info structure from them. + */ +void +scanner_construct_global_block (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; + + parser_list_iterator_t literal_iterator; + lexer_lit_location_t *literal_p; + bool no_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_REG) != 0; + +#if ENABLED (JERRY_DEBUGGER) + if (scanner_context_p->debugger_enabled) + { + /* When debugger is enabled, identifiers are not stored in registers. However, + * this does not affect 'eval' detection, so 'arguments' object is not created. */ + no_reg = true; + } +#endif /* ENABLED (JERRY_DEBUGGER) */ + + JERRY_ASSERT (literal_pool_p->prev_p == NULL); + + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + + const uint8_t *prev_source_p = literal_pool_p->source_p; + size_t compressed_size = 1; + uint32_t no_declarations = literal_pool_p->no_declarations; + + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + uint8_t type = literal_p->type; + + if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) + || !(type & SCANNER_LITERAL_IS_LET_OR_CONST)) + { + continue; + } + + no_declarations++; + + if (type & SCANNER_LITERAL_IS_FUNC) + { + JERRY_ASSERT (type & SCANNER_LITERAL_IS_LET); + + no_declarations++; + + type |= SCANNER_LITERAL_NO_REG; + literal_p->type = type; + } + else if (no_reg) + { + type |= SCANNER_LITERAL_NO_REG; + literal_p->type = type; + } + + intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p); + + if (diff >= 1 && diff <= UINT8_MAX) + { + compressed_size += 2 + 1; + } + else if (diff >= -UINT8_MAX && diff <= UINT16_MAX) + { + compressed_size += 2 + 2; + } + else + { + compressed_size += 2 + 1 + sizeof (const uint8_t *); + } + + prev_source_p = literal_p->char_p + literal_p->length; + } + + if (compressed_size <= 1) + { + return; + } + + scanner_info_t *info_p; + + compressed_size += sizeof (scanner_info_t); + + info_p = scanner_insert_info (context_p, literal_pool_p->source_p + 1, compressed_size); + info_p->type = SCANNER_TYPE_BLOCK; + + uint8_t *data_p = ((uint8_t *) info_p) + sizeof (scanner_info_t); + + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + prev_source_p = literal_pool_p->source_p; + no_declarations = literal_pool_p->no_declarations; + + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (JERRY_UNLIKELY (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) + || !(literal_p->type & SCANNER_LITERAL_IS_LET_OR_CONST)) + { + continue; + } + + no_declarations++; + + uint8_t type = SCANNER_STREAM_TYPE_LET; + + if (literal_p->type & SCANNER_LITERAL_IS_CONST) + { + type = SCANNER_STREAM_TYPE_CONST; + } + else if (literal_p->type & SCANNER_LITERAL_IS_FUNC) + { + type = SCANNER_STREAM_TYPE_VAR_FUNC; + no_declarations++; + } + + if (literal_p->has_escape) + { + type |= SCANNER_STREAM_HAS_ESCAPE; + } + + if (literal_p->type & SCANNER_LITERAL_NO_REG) + { + type |= SCANNER_STREAM_NO_REG; + } + + literal_p->type = 0; + + data_p[0] = type; + data_p[1] = (uint8_t) literal_p->length; + data_p += 3; + + intptr_t diff = (intptr_t) (literal_p->char_p - prev_source_p); + + if (diff >= 1 && diff <= UINT8_MAX) + { + data_p[-1] = (uint8_t) diff; + } + else if (diff >= -UINT8_MAX && diff <= UINT16_MAX) + { + if (diff < 0) + { + diff = -diff; + } + + data_p[-3] |= SCANNER_STREAM_UINT16_DIFF; + data_p[-1] = (uint8_t) diff; + data_p[0] = (uint8_t) (diff >> 8); + data_p += 1; + } + else + { + data_p[-1] = 0; + memcpy (data_p, &literal_p->char_p, sizeof (const uint8_t *)); + data_p += sizeof (const uint8_t *); + } + + prev_source_p = literal_p->char_p + literal_p->length; + } + + data_p[0] = SCANNER_STREAM_TYPE_END; + + JERRY_ASSERT (((uint8_t *) info_p) + compressed_size == data_p + 1); + + if (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) + { + no_declarations = PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK; + } + + literal_pool_p->no_declarations = (uint16_t) no_declarations; +} /* scanner_construct_global_block */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Filter out the arguments from a literal pool. */ @@ -883,6 +1108,75 @@ scanner_detect_eval_call (parser_context_t *context_p, /**< context */ } } /* scanner_detect_eval_call */ +#if ENABLED (JERRY_ES2015) + +/** + * Throws an error for invalid var statements. + */ +void +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)) + { + scanner_raise_redeclaration_error (context_p); + } + + scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; + const uint8_t *char_p = var_literal_p->char_p; + prop_length_t length = var_literal_p->length; + + while (!(literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)) + { + literal_pool_p = literal_pool_p->prev_p; + + parser_list_iterator_t literal_iterator; + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + lexer_lit_location_t *literal_p; + + if (JERRY_LIKELY (!context_p->token.lit_location.has_escape)) + { + 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) + && literal_p->length == length) + { + if (JERRY_LIKELY (!literal_p->has_escape)) + { + if (memcmp (literal_p->char_p, char_p, length) == 0) + { + scanner_raise_redeclaration_error (context_p); + return; + } + } + else if (lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + scanner_raise_redeclaration_error (context_p); + return; + } + } + } + } + else + { + 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) + && literal_p->length == length + && lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + scanner_raise_redeclaration_error (context_p); + return; + } + } + } + } +} /* scanner_detect_invalid_var */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Reverse the scanner info chain after the scanning is completed. */ @@ -973,7 +1267,12 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ } default: { +#if ENABLED (JERRY_ES2015) + JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS + || scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); +#else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS); +#endif /* ENABLED (JERRY_ES2015) */ break; } } @@ -986,6 +1285,158 @@ 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 *scope_stack_start_p = context_p->scope_stack_p; + parser_scope_stack *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 scope_stack_p->map_to; + } + } + + return -1; +} /* scanner_get_function_target */ + +#endif /* ENABLED (JERRY_ES2015) */ + +/** + * Checks whether a context needs to be created for a block. + * + * @return true - if context is needed, + * false - otherwise + */ +bool +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; + +#if ENABLED (JERRY_ES2015) + JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR + || type == SCANNER_STREAM_TYPE_LET + || type == SCANNER_STREAM_TYPE_CONST + || type == SCANNER_STREAM_TYPE_FUNC + || type == SCANNER_STREAM_TYPE_VAR_FUNC); +#else /* !ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR); +#endif /* ENABLED (JERRY_ES2015) */ + + size_t length; + + if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) + { + 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) + { + scope_stack_reg_top++; + } + else + { +#if ENABLED (JERRY_ES2015) + if (type != SCANNER_STREAM_TYPE_VAR_FUNC) + { + return true; + } +#else /* !ENABLED (JERRY_ES2015) */ + return true; +#endif /* ENABLED (JERRY_ES2015) */ + } + +#if ENABLED (JERRY_ES2015) + literal.char_p += data_p[1]; +#endif /* ENABLED (JERRY_ES2015) */ + data_p += length; + } + + return false; +} /* scanner_is_context_needed */ + /** * Description of "arguments" literal string. */ @@ -1111,64 +1562,124 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL); + scope_stack_p->map_from = context_p->lit_object.index; + 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) +#if ENABLED (JERRY_ES2015) + if (info_type == SCANNER_TYPE_FUNCTION) { - map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); - - scope_stack_p->map_from = context_p->lit_object.index; - scope_stack_p->map_to = map_to; - scope_stack_reg_top++; + if (type != SCANNER_STREAM_TYPE_LET + && type != SCANNER_STREAM_TYPE_CONST) + { + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_GLOBAL; + } } - else + else if (type == SCANNER_STREAM_TYPE_FUNC) { - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; - map_to = context_p->lit_object.index; + int32_t target_id = scanner_get_function_target (context_p); - scope_stack_p->map_from = map_to; - scope_stack_p->map_to = map_to; - - if (info_type == SCANNER_TYPE_FUNCTION) + if (target_id >= 0) { - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + map_to = (uint16_t) target_id; + + scope_stack_p->map_to = PARSER_SCOPE_STACK_FUNC; + func_init_opcode = CBC_SET_VAR_FUNC; } + } + else if (type == SCANNER_STREAM_TYPE_VAR_FUNC) + { + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_GLOBAL; + } +#endif /* ENABLED (JERRY_ES2015) */ - if (type == SCANNER_STREAM_TYPE_VAR) + if (func_init_opcode == CBC_INIT_LOCAL) + { + if (!(data_p[0] & SCANNER_STREAM_NO_REG) + && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { -#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); -#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); - parser_emit_cbc_literal (context_p, CBC_CREATE_VAR, map_to); + scope_stack_p->map_to = map_to; + scope_stack_reg_top++; +#if ENABLED (JERRY_ES2015) + func_init_opcode = CBC_SET_VAR_FUNC; +#endif /* ENABLED (JERRY_ES2015) */ } - else if (type == SCANNER_STREAM_TYPE_ARG || type == SCANNER_STREAM_TYPE_ARG_FUNC) + else { - if (type == SCANNER_STREAM_TYPE_ARG) + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; + map_to = context_p->lit_object.index; + + scope_stack_p->map_to = map_to; + + if (info_type == SCANNER_TYPE_FUNCTION) { -#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - 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); + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; } - if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + switch (type) { - scope_stack_reg_top++; + 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); +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + + parser_emit_cbc_literal (context_p, CBC_CREATE_VAR, map_to); + break; + } +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_LET: + case SCANNER_STREAM_TYPE_CONST: + { +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + + /* FIXME: introduce CBC_CREATE_LET / CBC_CREATE_CONST. */ + parser_emit_cbc_literal (context_p, CBC_CREATE_VAR, map_to); + break; + } + case SCANNER_STREAM_TYPE_VAR_FUNC: + { + if (context_p->status_flags & PARSER_INSIDE_BLOCK) + { + func_init_opcode = CBC_CREATE_VAR_FUNC; + } + break; + } +#endif /* ENABLED (JERRY_ES2015) */ + case SCANNER_STREAM_TYPE_ARG: + { +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + 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); + /* FALLTHRU */ + } + case SCANNER_STREAM_TYPE_ARG_FUNC: + { + if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + scope_stack_reg_top++; + } + } } } } + scope_stack_p++; literal.char_p += data_p[1]; data_p += length; - if (type != SCANNER_STREAM_TYPE_ARG_FUNC && type != SCANNER_STREAM_TYPE_FUNC) + if (!SCANNER_STREAM_TYPE_IS_FUNCTION (type)) { continue; } @@ -1188,7 +1699,7 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ 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, context_p->literal_count, map_to); + parser_emit_cbc_literal_value (context_p, func_init_opcode, context_p->literal_count, map_to); scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC; scope_stack_p->map_to = context_p->literal_count; @@ -1222,6 +1733,13 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ context_p->scope_stack_top = (uint16_t) (scope_stack_p - context_p->scope_stack_p); context_p->scope_stack_reg_top = (uint16_t) scope_stack_reg_top; +#if ENABLED (JERRY_ES2015) + if (info_type == SCANNER_TYPE_FUNCTION) + { + context_p->scope_stack_global_end = context_p->scope_stack_top; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->register_count < scope_stack_reg_top) { context_p->register_count = (uint16_t) scope_stack_reg_top; diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index a74178937..adad62c03 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -72,6 +72,10 @@ typedef enum SCAN_STACK_WHILE_EXPRESSION, /**< expression part of "while" iterator */ SCAN_STACK_PAREN_EXPRESSION, /**< expression in brackets */ SCAN_STACK_STATEMENT_WITH_EXPR, /**< statement which starts with expression enclosed in brackets */ +#if ENABLED (JERRY_ES2015) + SCAN_STACK_LET, /**< let statement */ + SCAN_STACK_CONST, /**< const statement */ +#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 */ SCAN_STACK_FOR_VAR_START, /**< start of "for" iterator with var statement */ @@ -504,6 +508,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * return SCAN_KEEP_TOKEN; } case SCAN_STACK_VAR: +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_LET: + case SCAN_STACK_CONST: +#endif /* ENABLED (JERRY_ES2015) */ case SCAN_STACK_FOR_VAR_START: { scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; @@ -628,6 +636,10 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * return SCAN_NEXT_TOKEN; } case SCAN_STACK_VAR: +#if ENABLED (JERRY_ES2015) + case SCAN_STACK_LET: + case SCAN_STACK_CONST: +#endif /* ENABLED (JERRY_ES2015) */ { parser_stack_pop_uint8 (context_p); return SCAN_KEEP_TOKEN; @@ -752,6 +764,12 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } +#if ENABLED (JERRY_ES2015) + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_BLOCK); + literal_pool_p->source_p = context_p->source_p - 1; +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_pop_uint8 (context_p); scanner_switch_statement_t switch_statement = scanner_context_p->active_switch_statement; @@ -966,6 +984,14 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ } case LEXER_LEFT_BRACE: { +#if ENABLED (JERRY_ES2015) + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, + scanner_context_p, + SCANNER_LITERAL_POOL_BLOCK); + literal_pool_p->source_p = context_p->source_p; +#endif /* ENABLED (JERRY_ES2015) */ + scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); return SCAN_NEXT_TOKEN; @@ -985,6 +1011,14 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } +#if ENABLED (JERRY_ES2015) + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, + scanner_context_p, + SCANNER_LITERAL_POOL_BLOCK); + literal_pool_p->source_p = context_p->source_p; +#endif /* ENABLED (JERRY_ES2015) */ + scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_TRY_STATEMENT); return SCAN_NEXT_TOKEN; @@ -1081,6 +1115,20 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ parser_stack_push_uint8 (context_p, SCAN_STACK_VAR); return SCAN_NEXT_TOKEN; } +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_LET: + { + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + parser_stack_push_uint8 (context_p, SCAN_STACK_LET); + return SCAN_NEXT_TOKEN; + } + case LEXER_KEYW_CONST: + { + scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT; + parser_stack_push_uint8 (context_p, SCAN_STACK_CONST); + return SCAN_NEXT_TOKEN; + } +#endif /* ENABLED (JERRY_ES2015) */ case LEXER_KEYW_THROW: { scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; @@ -1164,7 +1212,18 @@ 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 + && !(location_p->type & SCANNER_LITERAL_IS_FUNC)) + { + scanner_raise_redeclaration_error (context_p); + } + + location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; +#else location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; +#endif /* ENABLED (JERRY_ES2015) */ scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); @@ -1182,8 +1241,22 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } - lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); - location_p->type |= SCANNER_LITERAL_IS_VAR; + 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_raise_redeclaration_error (context_p); + } + + if (literal_p->type & SCANNER_LITERAL_IS_FUNC) + { + literal_p->type = (uint8_t) (literal_p->type & ~SCANNER_LITERAL_IS_FUNC); + } + + literal_p->type |= SCANNER_LITERAL_IS_LET; scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_STATEMENT); @@ -1339,7 +1412,17 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { 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 + && !(location_p->type & SCANNER_LITERAL_IS_FUNC)) + { + scanner_raise_redeclaration_error (context_p); + } + location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; +#else /* !ENABLED (JERRY_ES2015) */ location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; +#endif /* ENABLED (JERRY_ES2015) */ lexer_next_token (context_p); } @@ -1349,7 +1432,11 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ location_p = scanner_add_custom_literal (context_p, scanner_context_p->active_literal_pool_p, &lexer_default_literal); +#if ENABLED (JERRY_ES2015) + location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; +#else /* !ENABLED (JERRY_ES2015) */ location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; +#endif /* ENABLED (JERRY_ES2015) */ } scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); @@ -1478,6 +1565,26 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ return SCAN_NEXT_TOKEN; } +#if ENABLED (JERRY_ES2015) + if (context_p->token.type == LEXER_KEYW_CLASS) + { + /* FIXME: classes should be let declarations. */ + scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; + parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_STATEMENT); + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + scanner_raise_error (context_p); + } + + lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_VAR; + return SCAN_NEXT_TOKEN; + } +#endif /* ENABLED (JERRY_ES2015) */ + scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_KEEP_TOKEN; } @@ -1566,10 +1673,17 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ break; } +#if ENABLED (JERRY_ES2015) + if (context_p->stack_top_uint8 != SCAN_STACK_CLASS_STATEMENT) + { + scanner_pop_literal_pool (context_p, scanner_context_p); + } +#else /* !ENABLED (JERRY_ES2015) */ if (context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_STATEMENT) { scanner_pop_literal_pool (context_p, scanner_context_p); } +#endif /* ENABLED (JERRY_ES2015) */ terminator_found = true; parser_stack_pop_uint8 (context_p); @@ -1640,6 +1754,10 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ scanner_context_p->active_switch_statement = switch_statement; +#if ENABLED (JERRY_ES2015) + scanner_pop_literal_pool (context_p, scanner_context_p); +#endif /* ENABLED (JERRY_ES2015) */ + terminator_found = true; lexer_next_token (context_p); continue; @@ -1712,10 +1830,14 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); +#if ENABLED (JERRY_ES2015) + scanner_pop_literal_pool (context_p, scanner_context_p); +#else /* !ENABLED (JERRY_ES2015) */ if (stack_top == SCAN_STACK_CATCH_STATEMENT) { scanner_pop_literal_pool (context_p, scanner_context_p); } +#endif /* ENABLED (JERRY_ES2015) */ /* A finally statement is optional after a try or catch statement. */ if (context_p->token.type == LEXER_KEYW_FINALLY) @@ -1727,6 +1849,14 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } +#if ENABLED (JERRY_ES2015) + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, + scanner_context_p, + SCANNER_LITERAL_POOL_BLOCK); + literal_pool_p->source_p = context_p->source_p; +#endif /* ENABLED (JERRY_ES2015) */ + parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; return SCAN_NEXT_TOKEN; @@ -1839,9 +1969,14 @@ 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; +#else /* !ENABLED (JERRY_DEBUGGER) */ uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_REG; +#endif /* ENABLED (JERRY_DEBUGGER) */ + scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, status_flags); - literal_pool_p->source_p = source_p; + literal_pool_p->source_p = source_p - 1; scanner_context.mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT); @@ -2011,12 +2146,45 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } - lexer_lit_location_t *location_p = scanner_add_literal (context_p, &scanner_context); - location_p->type |= SCANNER_LITERAL_IS_VAR; + lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context); + +#if ENABLED (JERRY_ES2015) + if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST) + { + if (literal_p->type & (SCANNER_LITERAL_IS_ARG + | SCANNER_LITERAL_IS_LOCAL + | SCANNER_LITERAL_IS_VAR + | SCANNER_LITERAL_IS_LET_OR_CONST)) + { + scanner_raise_redeclaration_error (context_p); + } + + if (literal_p->type & SCANNER_LITERAL_IS_FUNC) + { + literal_p->type = (uint8_t) (literal_p->type & ~SCANNER_LITERAL_IS_FUNC); + } + + if (stack_top == SCAN_STACK_LET) + { + literal_p->type |= SCANNER_LITERAL_IS_LET; + } + else + { + literal_p->type |= SCANNER_LITERAL_IS_CONST; + } + } + else 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; + } +#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) { - location_p->type |= SCANNER_LITERAL_NO_REG; + literal_p->type |= SCANNER_LITERAL_NO_REG; } lexer_next_token (context_p); @@ -2048,7 +2216,11 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ continue; } +#if ENABLED (JERRY_ES2015) + JERRY_ASSERT (stack_top == SCAN_STACK_VAR || stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST); +#else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (stack_top == SCAN_STACK_VAR); +#endif /* ENABLED (JERRY_ES2015) */ scanner_context.mode = SCAN_MODE_STATEMENT_END; parser_stack_pop_uint8 (context_p); @@ -2275,6 +2447,14 @@ scan_completed: scanner_raise_error (context_p); } +#if ENABLED (JERRY_ES2015) + if (arg_list_p == NULL) + { + scanner_construct_global_block (context_p, &scanner_context); + scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_REG; + } +#endif /* ENABLED (JERRY_ES2015) */ + scanner_pop_literal_pool (context_p, &scanner_context); #ifndef JERRY_NDEBUG @@ -2364,6 +2544,23 @@ scan_completed: { switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) { + case SCANNER_STREAM_TYPE_VAR: + { + JERRY_DEBUG_MSG (" VAR "); + break; + } +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_LET: + { + JERRY_DEBUG_MSG (" LET "); + break; + } + case SCANNER_STREAM_TYPE_CONST: + { + JERRY_DEBUG_MSG (" CONST "); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_ARG: { JERRY_DEBUG_MSG (" ARG "); @@ -2374,16 +2571,18 @@ scan_completed: JERRY_DEBUG_MSG (" ARG_FUNC "); break; } - case SCANNER_STREAM_TYPE_VAR: - { - JERRY_DEBUG_MSG (" VAR "); - break; - } case SCANNER_STREAM_TYPE_FUNC: { JERRY_DEBUG_MSG (" FUNC "); break; } +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_VAR_FUNC: + { + JERRY_DEBUG_MSG (" VAR_FUNC "); + 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 a7985cc83..f3c5a6dac 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -44,6 +44,9 @@ typedef enum #endif /* ENABLED (JERRY_ES2015) */ SCANNER_TYPE_SWITCH, /**< switch statement */ SCANNER_TYPE_CASE, /**< case statement */ +#if ENABLED (JERRY_ES2015) + SCANNER_TYPE_ERR_REDECLARED, /**< syntax error: a variable is redeclared */ +#endif /* ENABLED (JERRY_ES2015) */ } scanner_info_type_t; /** @@ -127,6 +130,7 @@ typedef enum SCANNER_STREAM_UINT16_DIFF = (1 << 7), /**< relative distance is between -256 and 65535 */ SCANNER_STREAM_HAS_ESCAPE = (1 << 6), /**< literal has escape */ SCANNER_STREAM_NO_REG = (1 << 5), /**< identifier cannot be stored in register */ + /* Update SCANNER_STREAM_TYPE_MASK macro if more bits are added. */ } scanner_compressed_stream_flags_t; /** @@ -136,10 +140,18 @@ typedef enum { SCANNER_STREAM_TYPE_END, /**< end of scanner data */ SCANNER_STREAM_TYPE_HOLE, /**< no name is assigned to this argument */ - SCANNER_STREAM_TYPE_ARG, /**< argument declaration */ - SCANNER_STREAM_TYPE_ARG_FUNC, /**< argument declaration which is later initialized with a function */ SCANNER_STREAM_TYPE_VAR, /**< var declaration */ - SCANNER_STREAM_TYPE_FUNC, /**< function declaration */ +#if ENABLED (JERRY_ES2015) + SCANNER_STREAM_TYPE_LET, /**< let declaration */ + SCANNER_STREAM_TYPE_CONST, /**< const declaration */ +#endif /* ENABLED (JERRY_ES2015) */ + SCANNER_STREAM_TYPE_ARG, /**< argument declaration */ + /* Function types should be at the end. See the SCANNER_STREAM_TYPE_IS_FUNCTION macro. */ + SCANNER_STREAM_TYPE_ARG_FUNC, /**< argument declaration which is later initialized with a function */ + SCANNER_STREAM_TYPE_FUNC, /**< local function declaration */ +#if ENABLED (JERRY_ES2015) + SCANNER_STREAM_TYPE_VAR_FUNC, /**< var function declaration */ +#endif /* ENABLED (JERRY_ES2015) */ } scanner_compressed_stream_types_t; /** @@ -147,6 +159,11 @@ typedef enum */ #define SCANNER_STREAM_TYPE_MASK 0xf +/** + * Mask for decoding the type from the compressed stream. + */ +#define SCANNER_STREAM_TYPE_IS_FUNCTION(type) ((type) >= SCANNER_STREAM_TYPE_ARG_FUNC) + /** * Constants for u8_arg flags in scanner_function_info_t. */ diff --git a/jerry-core/vm/vm-stack.c b/jerry-core/vm/vm-stack.c index 14122018a..654a5babf 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -26,6 +26,8 @@ * @{ */ +JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_BLOCK_CONTEXT_STACK_ALLOCATION, + parser_with_context_stack_allocation_must_be_equal_to_parser_block_context_stack_allocation); JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION, parser_with_context_stack_allocation_must_be_equal_to_parser_super_class_context_stack_allocation); @@ -38,7 +40,17 @@ ecma_value_t * vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_value_t *vm_stack_top_p) /**< current stack top */ { - switch (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1])) + ecma_value_t context_info = vm_stack_top_p[-1]; + + if (context_info & VM_CONTEXT_HAS_LEX_ENV) + { + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + ecma_deref_object (lex_env_p); + } + + switch (VM_GET_CONTEXT_TYPE (context_info)) { case VM_CONTEXT_FINALLY_THROW: case VM_CONTEXT_FINALLY_RETURN: @@ -66,16 +78,14 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ vm_stack_top_p -= size_diff; /* FALLTHRU */ } +#if ENABLED (JERRY_ES2015) + case VM_CONTEXT_BLOCK: +#endif /* ENABLED (JERRY_ES2015) */ case VM_CONTEXT_WITH: #if ENABLED (JERRY_ES2015) case VM_CONTEXT_SUPER_CLASS: #endif /* ENABLED (JERRY_ES2015) */ { - ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; - JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); - frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); - ecma_deref_object (lex_env_p); - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_WITH_CONTEXT_STACK_ALLOCATION; break; @@ -194,6 +204,16 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ return false; } +#if ENABLED (JERRY_ES2015) + if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + { + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + ecma_deref_object (lex_env_p); + } +#endif /* ENABLED (JERRY_ES2015) */ + byte_code_p = frame_ctx_p->byte_code_start_p + context_end; if (context_type == VM_CONTEXT_TRY) @@ -232,10 +252,15 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ } else { - ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; - JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); - frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); - ecma_deref_object (lex_env_p); +#if !ENABLED (JERRY_ES2015) + if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + { + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + ecma_deref_object (lex_env_p); + } +#endif /* !ENABLED (JERRY_ES2015) */ if (byte_code_p[0] == CBC_CONTEXT_END) { diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index d967f368a..16f0212e4 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -25,20 +25,46 @@ * @{ */ -#define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | (end_offset) << 4)) -#define VM_GET_CONTEXT_TYPE(value) ((vm_stack_context_type_t) ((value) & 0xf)) -#define VM_GET_CONTEXT_END(value) ((value) >> 4) +/** + * Create context on the vm stack. + */ +#define VM_CREATE_CONTEXT(type, end_offset) ((ecma_value_t) ((type) | ((end_offset) << 6))) + +/** + * Create context on the vm stack with environment. + */ +#define VM_CREATE_CONTEXT_WITH_ENV(type, end_offset) \ + ((ecma_value_t) ((type) | ((end_offset) << 6) | VM_CONTEXT_HAS_LEX_ENV)) + +/** + * Get type of a vm context. + */ +#define VM_GET_CONTEXT_TYPE(value) ((vm_stack_context_type_t) ((value) & 0x1f)) + +/** + * Get the end position of a vm context. + */ +#define VM_GET_CONTEXT_END(value) ((value) >> 6) + +/** + * This flag is set if the context has a lexical environment. + */ +#define VM_CONTEXT_HAS_LEX_ENV 0x20 /** * Context types for the vm stack. */ typedef enum { + /* Update VM_CONTEXT_IS_FINALLY macro if the following three values are changed. */ VM_CONTEXT_FINALLY_JUMP, /**< finally context with a jump */ VM_CONTEXT_FINALLY_THROW, /**< finally context with a throw */ VM_CONTEXT_FINALLY_RETURN, /**< finally context with a return */ VM_CONTEXT_TRY, /**< try context */ VM_CONTEXT_CATCH, /**< catch context */ +#if ENABLED (JERRY_ES2015) + VM_CONTEXT_BLOCK, /**< block context */ +#endif /* ENABLED (JERRY_ES2015) */ VM_CONTEXT_WITH, /**< with context */ VM_CONTEXT_FOR_IN, /**< for-in context */ #if ENABLED (JERRY_ES2015) @@ -47,6 +73,12 @@ typedef enum #endif /* ENABLED (JERRY_ES2015) */ } vm_stack_context_type_t; +/** + * Checks whether the context type is a finally type. + */ +#define VM_CONTEXT_IS_FINALLY(context_type) \ + ((context_type) <= VM_CONTEXT_FINALLY_RETURN) + ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p); bool vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_ref_p, vm_stack_context_type_t finally_type, uint32_t search_limit); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 49fedfb16..78595bf2e 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -835,35 +835,99 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { ecma_fast_free_value (frame_ctx_p->registers_p[literal_index]); frame_ctx_p->registers_p[literal_index] = lit_value; + break; } - else + + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + + vm_var_decl (frame_ctx_p, name_p); + + ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (frame_ctx_p->lex_env_p, + name_p, + is_strict, + lit_value); + + JERRY_ASSERT (ecma_is_value_boolean (put_value_result) + || ecma_is_value_empty (put_value_result) + || ECMA_IS_VALUE_ERROR (put_value_result)); + + if (ECMA_IS_VALUE_ERROR (put_value_result)) { - ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_free_value (JERRY_CONTEXT (error_value)); + } - vm_var_decl (frame_ctx_p, name_p); - - ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (frame_ctx_p->lex_env_p, - name_p, - is_strict, - lit_value); - - JERRY_ASSERT (ecma_is_value_boolean (put_value_result) - || ecma_is_value_empty (put_value_result) - || ECMA_IS_VALUE_ERROR (put_value_result)); - - if (ECMA_IS_VALUE_ERROR (put_value_result)) - { - ecma_free_value (JERRY_CONTEXT (error_value)); - } - - if (value_index >= register_end) - { - ecma_free_value (lit_value); - } + if (value_index >= register_end) + { + ecma_free_value (lit_value); } break; } +#if ENABLED (JERRY_ES2015) + case CBC_CREATE_VAR_FUNC: + case CBC_SET_VAR_FUNC: + { + uint8_t type = *byte_code_p; + uint32_t literal_index, value_index; + ecma_value_t lit_value; + + byte_code_p++; + READ_LITERAL_INDEX (value_index); + READ_LITERAL_INDEX (literal_index); + + JERRY_ASSERT (value_index != literal_index && value_index >= register_end); + + lit_value = vm_construct_literal_object (frame_ctx_p, + literal_start_p[value_index]); + + if (literal_index < register_end) + { + JERRY_ASSERT (type == CBC_SET_VAR_FUNC); + + ecma_fast_free_value (frame_ctx_p->registers_p[literal_index]); + frame_ctx_p->registers_p[literal_index] = lit_value; + break; + } + + 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; + + if (type == CBC_CREATE_VAR_FUNC) + { + 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 (!ecma_op_has_binding (lex_env_p, name_p)) + { + const bool is_configurable_bindings = frame_ctx_p->is_eval_code; + + ecma_value_t completion_value = ecma_op_create_mutable_binding (lex_env_p, + name_p, + is_configurable_bindings); + + JERRY_ASSERT (ecma_is_value_empty (completion_value)); + } + } + + ecma_value_t put_value_result = ecma_op_put_value_lex_env_base (lex_env_p, + name_p, + is_strict, + lit_value); + + JERRY_ASSERT (ecma_is_value_boolean (put_value_result) + || ecma_is_value_empty (put_value_result) + || ECMA_IS_VALUE_ERROR (put_value_result)); + + if (ECMA_IS_VALUE_ERROR (put_value_result)) + { + ecma_free_value (JERRY_CONTEXT (error_value)); + } + + ecma_free_value (lit_value); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ + #if ENABLED (JERRY_SNAPSHOT_EXEC) case CBC_SET_BYTECODE_PTR: { @@ -1364,7 +1428,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION; - stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_SUPER_CLASS, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_SUPER_CLASS, branch_offset); frame_ctx_p->lex_env_p = super_env_p; @@ -2818,6 +2882,58 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = result; goto free_both_values; } + case VM_OC_BLOCK_CREATE_CONTEXT: + { +#if ENABLED (JERRY_ES2015) + ecma_value_t *stack_context_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth; + JERRY_ASSERT (stack_context_top_p == stack_top_p || stack_context_top_p == stack_top_p - 1); + + if (byte_code_start_p[0] != CBC_EXT_OPCODE) + { + branch_offset += (int32_t) (byte_code_start_p - frame_ctx_p->byte_code_start_p); + + if (stack_context_top_p != stack_top_p) + { + /* Preserve the value of switch statement. */ + stack_context_top_p[1] = stack_context_top_p[0]; + } + + stack_context_top_p[0] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_BLOCK, branch_offset); + + VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); + stack_top_p += PARSER_BLOCK_CONTEXT_STACK_ALLOCATION; + } + else + { + JERRY_ASSERT (byte_code_start_p[1] == CBC_EXT_TRY_CREATE_ENV); + + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_TRY + || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_CATCH + || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP + || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_THROW + || VM_GET_CONTEXT_TYPE (stack_context_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN); + + JERRY_ASSERT (!(stack_context_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); + + stack_context_top_p[-1] |= VM_CONTEXT_HAS_LEX_ENV; + } +#else /* !ENABLED (JERRY_ES2015) */ + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-2]) == VM_CONTEXT_CATCH + && !(stack_top_p[-2] & VM_CONTEXT_HAS_LEX_ENV)); + + stack_top_p[-2] |= VM_CONTEXT_HAS_LEX_ENV; +#endif /* ENABLED (JERRY_ES2015) */ + + frame_ctx_p->lex_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p); +#if ENABLED (JERRY_DEBUGGER) + frame_ctx_p->lex_env_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_NON_CLOSURE; +#endif /* ENABLED (JERRY_DEBUGGER) */ + + frame_ctx_p->byte_code_p = byte_code_p; + vm_init_loop (frame_ctx_p); + byte_code_p = frame_ctx_p->byte_code_p; + continue; + } case VM_OC_WITH: { ecma_value_t value = *(--stack_top_p); @@ -2846,7 +2962,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_WITH_CONTEXT_STACK_ALLOCATION; - stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_WITH, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_WITH, branch_offset); frame_ctx_p->lex_env_p = with_env_p; continue; @@ -2872,7 +2988,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; - stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FOR_IN, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_IN, branch_offset); ECMA_SET_INTERNAL_VALUE_ANY_POINTER (stack_top_p[-2], prop_names_p); stack_top_p[-3] = 0; stack_top_p[-4] = expr_obj_value; @@ -2972,7 +3088,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION; - stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset); stack_top_p[-2] = iterator_step; stack_top_p[-3] = iterator; @@ -3032,7 +3148,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); stack_top_p += PARSER_TRY_CONTEXT_STACK_ALLOCATION; - stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_TRY, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_TRY, branch_offset); continue; } case VM_OC_CATCH: @@ -3053,15 +3169,17 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY || VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); - if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH) + if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) { ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); ecma_deref_object (lex_env_p); + + stack_top_p[-1] &= (ecma_value_t) ~VM_CONTEXT_HAS_LEX_ENV; } - stack_top_p[-1] = (ecma_value_t) VM_CREATE_CONTEXT (VM_CONTEXT_FINALLY_JUMP, branch_offset); + stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FINALLY_JUMP, branch_offset); stack_top_p[-2] = (ecma_value_t) branch_offset; continue; } @@ -3069,59 +3187,64 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); - switch (VM_GET_CONTEXT_TYPE (stack_top_p[-1])) + ecma_value_t context_type = VM_GET_CONTEXT_TYPE (stack_top_p[-1]); + + if (!VM_CONTEXT_IS_FINALLY (context_type)) { - case VM_CONTEXT_FINALLY_JUMP: - { - uint32_t jump_target = stack_top_p[-2]; + stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, - PARSER_TRY_CONTEXT_STACK_ALLOCATION); - stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); + continue; + } - if (vm_stack_find_finally (frame_ctx_p, - &stack_top_p, - VM_CONTEXT_FINALLY_JUMP, - jump_target)) - { - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); - byte_code_p = frame_ctx_p->byte_code_p; - stack_top_p[-2] = jump_target; - } - else - { - byte_code_p = frame_ctx_p->byte_code_start_p + jump_target; - } - break; - } - case VM_CONTEXT_FINALLY_THROW: - { - JERRY_CONTEXT (error_value) = stack_top_p[-2]; - JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; +#if ENABLED (JERRY_ES2015) + if (stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + { + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + ecma_deref_object (lex_env_p); + } +#endif /* ENABLED (JERRY_ES2015) */ - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, - PARSER_TRY_CONTEXT_STACK_ALLOCATION); - stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; - result = ECMA_VALUE_ERROR; + VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, + PARSER_TRY_CONTEXT_STACK_ALLOCATION); + stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; + + if (context_type == VM_CONTEXT_FINALLY_RETURN) + { + result = *stack_top_p; + goto error; + } + + if (context_type == VM_CONTEXT_FINALLY_THROW) + { + JERRY_CONTEXT (error_value) = *stack_top_p; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + result = ECMA_VALUE_ERROR; #if ENABLED (JERRY_DEBUGGER) - JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); + JERRY_DEBUGGER_SET_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); #endif /* ENABLED (JERRY_DEBUGGER) */ - goto error; - } - case VM_CONTEXT_FINALLY_RETURN: - { - result = stack_top_p[-2]; + goto error; + } - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, - PARSER_TRY_CONTEXT_STACK_ALLOCATION); - stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; - goto error; - } - default: - { - stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); - } + JERRY_ASSERT (context_type == VM_CONTEXT_FINALLY_JUMP); + + uint32_t jump_target = *stack_top_p; + + if (vm_stack_find_finally (frame_ctx_p, + &stack_top_p, + VM_CONTEXT_FINALLY_JUMP, + jump_target)) + { + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_JUMP); + byte_code_p = frame_ctx_p->byte_code_p; + stack_top_p[-2] = jump_target; + } + else + { + byte_code_p = frame_ctx_p->byte_code_start_p + jump_target; } JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); @@ -3466,31 +3589,23 @@ error: 0)) { JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); + JERRY_ASSERT (!(stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)); #if ENABLED (JERRY_DEBUGGER) JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); #endif /* ENABLED (JERRY_DEBUGGER) */ - if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH) - { - *stack_top_p++ = JERRY_CONTEXT (error_value); - - ecma_object_t *catch_env_p = ecma_create_decl_lex_env (frame_ctx_p->lex_env_p); -#if ENABLED (JERRY_DEBUGGER) - catch_env_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_NON_CLOSURE; -#endif /* ENABLED (JERRY_DEBUGGER) */ - - frame_ctx_p->lex_env_p = catch_env_p; - - vm_init_loop (frame_ctx_p); - } - else - { - JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW); - stack_top_p[-2] = JERRY_CONTEXT (error_value); - } - byte_code_p = frame_ctx_p->byte_code_p; + + if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) + { + stack_top_p[-2] = JERRY_CONTEXT (error_value); + continue; + } + + JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH); + + *stack_top_p++ = JERRY_CONTEXT (error_value); continue; } } diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 103305b23..3c3102ce3 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -202,6 +202,9 @@ typedef enum VM_OC_RIGHT_SHIFT, /**< right shift */ VM_OC_UNS_RIGHT_SHIFT, /**< unsigned right shift */ +#if ENABLED (JERRY_ES2015) + VM_OC_BLOCK_CREATE_CONTEXT, /**< create lexical environment for blocks enclosed in braces */ +#endif /* ENABLED (JERRY_ES2015) */ VM_OC_WITH, /**< with */ VM_OC_FOR_IN_CREATE_CONTEXT, /**< for in create context */ VM_OC_FOR_IN_GET_NEXT, /**< get next */ @@ -260,6 +263,7 @@ typedef enum #endif /* !ENABLED (JERRY_LINE_INFO) */ #if !ENABLED (JERRY_ES2015) 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 */ VM_OC_FOR_OF_CREATE_CONTEXT = VM_OC_NONE, /**< for of create context */ VM_OC_FOR_OF_GET_NEXT = VM_OC_NONE, /**< get next */ diff --git a/tests/jerry-test-suite/07/07.06/07.06.01/07.06.01-001.js b/tests/jerry-test-suite/07/07.06/07.06.01/07.06.01-001.js index d0e33f881..23e8d133f 100644 --- a/tests/jerry-test-suite/07/07.06/07.06.01/07.06.01-001.js +++ b/tests/jerry-test-suite/07/07.06/07.06.01/07.06.01-001.js @@ -12,4 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -var let = 1; +var package = 1; diff --git a/tests/jerry/es2015/let1.js b/tests/jerry/es2015/let1.js new file mode 100644 index 000000000..809fc3ce5 --- /dev/null +++ b/tests/jerry/es2015/let1.js @@ -0,0 +1,42 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var a = 5; +var a = 6; +let b = 7; + +assert (a === 6); +assert (this.a === 6); +assert (b === 7); +assert (this.b === undefined); + +{ + let c; + c = 8; + + { + let c = 9; + assert (c === 9); + } + + { + function c() { return 10 } + assert (c() === 10); + } + + assert (c === 8); +} +assert (typeof c === "undefined"); + diff --git a/tests/jerry/es2015/let2.js b/tests/jerry/es2015/let2.js new file mode 100644 index 000000000..0372c780f --- /dev/null +++ b/tests/jerry/es2015/let2.js @@ -0,0 +1,44 @@ +/* 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_syntax_error (code) +{ + try { + eval (code); + assert (false); + } catch (e) { + assert (e instanceof SyntaxError); + } +} + +check_syntax_error ("let a; let b; let a"); +check_syntax_error ("let a, b, a"); +check_syntax_error ("var a; let a;"); +check_syntax_error ("var a; const a = 3;"); +check_syntax_error ("let a; var a;"); +check_syntax_error ("const a = 3; var x, y, a;"); +check_syntax_error ("let a; { let b; { var a; } }"); +check_syntax_error ("{ { var a = 4; } }; let a = 3"); +check_syntax_error ("function a() {}; let a;"); +check_syntax_error ("let a; function a() {};"); +check_syntax_error ("{ { function a() {}; let a; } }"); +check_syntax_error ("{ { let a; function a() {}; } }"); +check_syntax_error ("let a = 1; const b = 5; const a = 2;"); +check_syntax_error ("try {} catch (e) { let e; }"); +check_syntax_error ("try {} catch (e) { const e = 1; }"); +check_syntax_error ("let A; class A {}"); +check_syntax_error ("const A; class A {}"); +check_syntax_error ("class A {}; let A"); +check_syntax_error ("class A {}; const A"); diff --git a/tests/jerry/es2015/let3.js b/tests/jerry/es2015/let3.js new file mode 100644 index 000000000..c392081a0 --- /dev/null +++ b/tests/jerry/es2015/let3.js @@ -0,0 +1,68 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var g = -1; + +function f1() { + /* Function hoisted as var. */ + assert (g === undefined); + + { + assert (g() === 1); + + { + assert (g() === 2); + + function g() { return 2 }; + } + + function g() { return 1 }; + + assert (g() === 2); + } + + assert (g() === 2); +} +f1(); + +function f2() { + /* Function hoisted as let. */ + assert (g === -1); + + { + let g = 1; + + { + if (true) + { + assert (g() === 2); + + if (true) + { + assert (g() === 2); + } + + function g() { return 2 }; + } + + assert (g === 1); + } + + assert (g === 1); + } + + assert (g === -1); +} +f2(); diff --git a/tests/jerry/es2015/let4.js b/tests/jerry/es2015/let4.js new file mode 100644 index 000000000..ebfb6af8b --- /dev/null +++ b/tests/jerry/es2015/let4.js @@ -0,0 +1,68 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var g = -1; + +function f1() { + /* Function hoisted as var. */ + assert (g === undefined); + + { + assert (g() === 1); + + { + eval("assert (g() === 2)"); + + function g() { return 2 }; + } + + function g() { return 1 }; + + assert (g() === 2); + } + + assert (g() === 2); +} +f1(); + +function f2() { + /* Function hoisted as let. */ + assert (g === -1); + + { + let g = 1; + + { + if (true) + { + assert (g() === 2); + + if (true) + { + eval("assert (g() === 2)"); + } + + function g() { return 2 }; + } + + assert (g === 1); + } + + assert (g === 1); + } + + assert (g === -1); +} +f2(); diff --git a/tests/jerry/es2015/let5.js b/tests/jerry/es2015/let5.js new file mode 100644 index 000000000..05ec3e0a6 --- /dev/null +++ b/tests/jerry/es2015/let5.js @@ -0,0 +1,35 @@ +/* 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. + */ + +let arr = []; + +for (var a = 0; a < 10; a++) +{ + let j = a; + var f; + + { + f = function () { return j; } + + let j = (a & 0x1) ? a + 10 : a + 100; + } + + arr[j] = f; +} + +for (var a = 0; a < 10; a++) +{ + assert (arr[a]() == ((a & 0x1) ? a + 10 : a + 100)) +} diff --git a/tests/jerry/es2015/let6.js b/tests/jerry/es2015/let6.js new file mode 100644 index 000000000..f1f270b6d --- /dev/null +++ b/tests/jerry/es2015/let6.js @@ -0,0 +1,79 @@ +/* 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 { + let a = 1; + eval ("assert(a === 1)"); +} catch (e) { + assert (false); +} + +assert (typeof a === "undefined"); + +try { + let a = 2; + eval ("assert(a === 2)"); + + assert (typeof b === "undefined"); + throw 3; +} catch (e) { + let b = e; + eval ("assert(b === 3)"); + + assert (typeof a === "undefined"); +} + +assert (typeof a === "undefined"); +assert (typeof b === "undefined"); + +try { + let a = 4; + eval ("assert(a === 4)"); + + assert (typeof b === "undefined"); +} finally { + let b = 5; + eval ("assert(b === 5)"); + + assert (typeof a === "undefined"); +} + +assert (typeof a === "undefined"); +assert (typeof b === "undefined"); + +try { + let a = 6; + eval ("assert(a === 6)"); + + assert (typeof b === "undefined"); + assert (typeof c === "undefined"); + throw 7; +} catch (e) { + let b = e; + eval ("assert(b === 7)"); + + assert (typeof a === "undefined"); + assert (typeof c === "undefined"); +} finally { + let c = 8; + eval ("assert(c === 8)"); + + assert (typeof a === "undefined"); + assert (typeof b === "undefined"); +} + +assert (typeof a === "undefined"); +assert (typeof b === "undefined"); +assert (typeof c === "undefined"); diff --git a/tests/jerry/es2015/let7.js b/tests/jerry/es2015/let7.js new file mode 100644 index 000000000..9e3cea216 --- /dev/null +++ b/tests/jerry/es2015/let7.js @@ -0,0 +1,68 @@ +/* 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. + */ + + +assert (typeof f === "undefined"); + +function g() { return 6; } + +switch (g()) { +case f(): + + let g = 9; + + assert (g === 9); + + function f() { return 6; } + break; + +default: + assert (false); +} + +assert (f() === 6); + +switch (g()) { +case f() - 2: + + let g = 9; + + assert ((function() { return g + f(); })() === 17); + + function f() { return 8; } + break; + +default: + assert (false); +} + +assert (f() === 8); + +switch (g()) { +case g() * 2: + + { + let g = 4; + assert (g == 4); + } + + function g() { return 3; } + break; + +default: + assert (false); +} + +assert (g() === 3); diff --git a/tests/jerry/es2015/let8.js b/tests/jerry/es2015/let8.js new file mode 100644 index 000000000..86b3b68cc --- /dev/null +++ b/tests/jerry/es2015/let8.js @@ -0,0 +1,58 @@ +/* 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. + */ + +assert (typeof A === "undefined"); + +{ + class A {}; + + { + let A = 6; + + assert (A === 6); + } + + assert (typeof A === "function"); +} + +assert (typeof A === "undefined"); + +{ + let A = 5; + { + { + class A {}; + + assert (typeof A === "function"); + } + + assert (A === 5); + } +} + +assert (typeof A === "undefined"); + +{ + let A = 5; + { + { + class A {}; + + eval ('assert (typeof A === "function")'); + } + + eval ('assert (A === 5)'); + } +} diff --git a/tests/jerry/es5.1/func-decl.js b/tests/jerry/es5.1/func-decl.js new file mode 100644 index 000000000..4529e580b --- /dev/null +++ b/tests/jerry/es5.1/func-decl.js @@ -0,0 +1,25 @@ +// 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 'foo'; +} +assert ((function() { + if (1 === 0) { + function f() { + return 'bar'; + } + } + return f(); +})() === 'bar'); diff --git a/tests/jerry/es2015/regression-test-issue-3123.js b/tests/jerry/fail/regression-test-issue-3123.js similarity index 100% rename from tests/jerry/es2015/regression-test-issue-3123.js rename to tests/jerry/fail/regression-test-issue-3123.js diff --git a/tests/jerry/func-decl.js b/tests/jerry/func-decl.js index 6be92e279..186fa7857 100644 --- a/tests/jerry/func-decl.js +++ b/tests/jerry/func-decl.js @@ -16,7 +16,7 @@ function f() { return 'foo'; } assert ((function() { - if (1 === 0) { + if (1 === 1) { function f() { return 'bar'; } diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index e83ab43ba..65287a9e3 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, 0x19, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x1A, 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, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x28, 0x00, 0xB7, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0xBC, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4B, 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,