From 1edfa81c761acd6c8f18c5558c6690b7306e5a20 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Wed, 30 Oct 2019 16:01:55 +0100 Subject: [PATCH] Implement correct construction of let/const declarations and function statements. (#3259) Various cbc opcodes are added to support the different instantiations. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/debugger/debugger.c | 2 +- jerry-core/ecma/base/ecma-globals.h | 6 +- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/parser/js/byte-code.h | 12 +- jerry-core/parser/js/js-lexer.c | 57 +-- jerry-core/parser/js/js-parser-internal.h | 12 +- jerry-core/parser/js/js-parser-statm.c | 20 +- jerry-core/parser/js/js-parser-util.c | 12 +- jerry-core/parser/js/js-parser.c | 56 +-- jerry-core/parser/js/js-parser.h | 3 +- jerry-core/parser/js/js-scanner-internal.h | 8 +- jerry-core/parser/js/js-scanner-util.c | 405 ++++++++++----------- jerry-core/parser/js/js-scanner.c | 34 +- jerry-core/parser/js/js-scanner.h | 13 +- jerry-core/vm/opcodes.c | 45 ++- jerry-core/vm/opcodes.h | 7 +- jerry-core/vm/vm.c | 150 +++++--- tests/jerry/es2015/let2.js | 5 +- tests/jerry/es2015/let9.js | 48 +++ tests/jerry/eval-with.js | 53 +++ tests/unit-core/test-snapshot.c | 6 +- 21 files changed, 571 insertions(+), 385 deletions(-) create mode 100644 tests/jerry/es2015/let9.js create mode 100644 tests/jerry/eval-with.js diff --git a/jerry-core/debugger/debugger.c b/jerry-core/debugger/debugger.c index 4751c1b91..809d9adf3 100644 --- a/jerry-core/debugger/debugger.c +++ b/jerry-core/debugger/debugger.c @@ -235,7 +235,7 @@ jerry_debugger_send_scope_chain (void) if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { - if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_NON_CLOSURE) != 0) + if ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) != 0) { message_type_p->string[buffer_pos++] = JERRY_DEBUGGER_SCOPE_NON_CLOSURE; } diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index d6222098a..5fe8467ba 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -709,9 +709,7 @@ typedef enum /** * Non closure flag for debugger. */ -#if ENABLED (JERRY_DEBUGGER) -#define ECMA_OBJECT_FLAG_NON_CLOSURE 0x20 -#endif /* ENABLED (JERRY_DEBUGGER) */ +#define ECMA_OBJECT_FLAG_BLOCK ECMA_OBJECT_FLAG_EXTENSIBLE /** * Bitshift index for an ecma-object reference count field @@ -747,7 +745,7 @@ typedef struct /** type : 4 bit : ecma_object_type_t or ecma_lexical_environment_type_t depending on ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV flags : 2 bit : ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV, - ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_NON_CLOSURE + ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_BLOCK refs : 10 bit (max 1022) */ uint16_t type_flags_refs; diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 1c25b5080..91298eab1 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 (26u) +#define JERRY_SNAPSHOT_VERSION (27u) /** * 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 2891f573c..d7005c6d5 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -302,12 +302,18 @@ VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, \ VM_OC_EVAL) \ - CBC_OPCODE (CBC_CREATE_VAR, CBC_HAS_LITERAL_ARG, 0, \ + CBC_OPCODE (CBC_CREATE_LOCAL, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_CREATE_LET, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_CREATE_CONST, CBC_HAS_LITERAL_ARG, 0, \ 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_CREATE_VAR_EVAL, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_NONE) \ + CBC_OPCODE (CBC_CREATE_VAR_FUNC_EVAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + VM_OC_NONE) \ 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, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index 88eface52..df4b810dd 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -1546,6 +1546,36 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ */ #define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48 +/** + * Convert an ident with escapes to a utf8 string. + */ +void +lexer_convert_ident_to_utf8 (const uint8_t *source_p, /**< source string */ + uint8_t *destination_p, /**< destination string */ + prop_length_t length) /**< length of destination string */ +{ + const uint8_t *destination_end_p = destination_p + length; + + JERRY_ASSERT (length <= PARSER_MAXIMUM_IDENT_LENGTH); + + do + { + if (*source_p == LIT_CHAR_BACKSLASH) + { + destination_p += lit_char_to_utf8_bytes (destination_p, + lexer_unchecked_hex_to_character (source_p + 2, 4)); + source_p += 6; + continue; + } + + JERRY_ASSERT (IS_UTF8_INTERMEDIATE_OCTET (*source_p) + || lit_char_is_identifier_part (source_p)); + + *destination_p++ = *source_p++; + } + while (destination_p < destination_end_p); +} /* lexer_convert_ident_to_utf8 */ + /** * Construct a literal object from an identifier. */ @@ -1580,32 +1610,7 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ if (literal_p->type == LEXER_IDENT_LITERAL) { - const uint8_t *source_end_p = context_p->source_end_p; - - JERRY_ASSERT (literal_p->length <= PARSER_MAXIMUM_IDENT_LENGTH); - - do - { - if (*source_p == LIT_CHAR_BACKSLASH) - { - destination_p += lit_char_to_utf8_bytes (destination_p, - lexer_unchecked_hex_to_character (source_p + 2, 4)); - source_p += 6; - continue; - } - - *destination_p++ = *source_p++; - - while (source_p < source_end_p - && IS_UTF8_INTERMEDIATE_OCTET (*source_p)) - { - *destination_p++ = *source_p++; - } - } - while (source_p < source_end_p - && (lit_char_is_identifier_part (source_p) || *source_p == LIT_CHAR_BACKSLASH)); - - JERRY_ASSERT (destination_p == destination_start_p + literal_p->length); + lexer_convert_ident_to_utf8 (source_p, destination_start_p, literal_p->length); } else { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 06fbd0c00..167e1b84e 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -75,11 +75,11 @@ typedef enum PARSER_CLASS_IMPLICIT_SUPER = (1u << 22), /**< class has implicit parent class */ PARSER_CLASS_STATIC_FUNCTION = (1u << 23), /**< this function is a static class method */ PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 24), /**< super property call or assignment */ + PARSER_IS_EVAL = (1u << 25), /**< eval code */ #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 25), /**< parsing a function or class default export */ - PARSER_MODULE_STORE_IDENT = (1u << 26), /**< store identifier of the current export statement */ - PARSER_IS_EVAL = (1u << 27), /**< eval code */ + PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 26), /**< parsing a function or class default export */ + PARSER_MODULE_STORE_IDENT = (1u << 27), /**< store identifier of the current export statement */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ #ifndef JERRY_NDEBUG PARSER_SCANNING_SUCCESSFUL = (1u << 30), /**< scanning process was successful */ @@ -583,6 +583,7 @@ void lexer_parse_string (parser_context_t *context_p); void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type); void lexer_scan_identifier (parser_context_t *context_p, uint32_t ident_opts); ecma_char_t lexer_hex_to_character (parser_context_t *context_p, const uint8_t *source_p, int length); +void lexer_convert_ident_to_utf8 (const uint8_t *source_p, uint8_t *destination_p, prop_length_t length); void lexer_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts); void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *literal_p, uint8_t literal_type); @@ -631,7 +632,10 @@ 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); +#if ENABLED (JERRY_ES2015) +bool scanner_is_global_context_needed (parser_context_t *context_p); +#endif /* ENABLED (JERRY_ES2015) */ +void scanner_create_variables (parser_context_t *context_p, uint32_t option_flags); void scanner_get_location (scanner_location_t *location_p, parser_context_t *context_p); void scanner_set_location (parser_context_t *context_p, scanner_location_t *location_p); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 3a71d116f..663ba512d 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -352,6 +352,10 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ || context_p->token.type == LEXER_KEYW_LET || context_p->token.type == LEXER_KEYW_CONST); +#if ENABLED (JERRY_ES2015) + bool is_const = context_p->token.type == LEXER_KEYW_CONST; +#endif /* ENABLED (JERRY_ES2015) */ + while (true) { lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); @@ -405,6 +409,12 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL); } +#if ENABLED (JERRY_ES2015) + else if (is_const) + { + parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST); + } +#endif /* ENABLED (JERRY_ES2015) */ if (context_p->token.type != LEXER_COMMA) { @@ -1281,7 +1291,7 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context * parser_stack_push_uint8 (context_p, PARSER_STATEMENT_SWITCH_BLOCK_CONTEXT); } - scanner_create_variables (context_p, sizeof (scanner_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } #endif /* ENABLED (JERRY_ES2015) */ @@ -1550,7 +1560,7 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } - scanner_create_variables (context_p, sizeof (scanner_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); @@ -1606,7 +1616,7 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } - scanner_create_variables (context_p, sizeof (scanner_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } #endif /* ENABLED (JERRY_ES2015) */ } @@ -2485,7 +2495,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ block_type = PARSER_STATEMENT_BLOCK_CONTEXT; } - scanner_create_variables (context_p, sizeof (scanner_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t)); @@ -2616,7 +2626,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV); } - scanner_create_variables (context_p, sizeof (scanner_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); } #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 833e36d4a..f8684eef9 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1081,6 +1081,14 @@ parser_error_to_string (parser_error_t error) /**< error code */ return "Non-strict argument definition."; } #if ENABLED (JERRY_ES2015) + case PARSER_ERR_VARIABLE_REDECLARED: + { + return "Local variable is redeclared."; + } + case PARSER_ERR_MISSING_ASSIGN_AFTER_CONST: + { + return "Value assignment is expected after a const declaration."; + } case PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS: { return "Multiple constructors are not allowed."; @@ -1117,10 +1125,6 @@ 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 7a0104dd3..30cf207f8 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -1617,7 +1617,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_ES2015) */ JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); - scanner_create_variables (context_p, sizeof (scanner_function_info_t)); + scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); if (context_p->token.type == end_type) { @@ -1766,14 +1766,6 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.status_flags = PARSER_IS_FUNCTION; } -#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (parse_opts & ECMA_PARSE_EVAL) - { - context.status_flags |= PARSER_IS_EVAL; - } - - context.module_current_node_p = NULL; -#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ #if ENABLED (JERRY_ES2015) context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts); @@ -1833,6 +1825,13 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ +#if ENABLED (JERRY_ES2015) + if (parse_opts & ECMA_PARSE_DIRECT_EVAL) + { + context.status_flags |= PARSER_IS_EVAL; + } +#endif /* ENABLED (JERRY_ES2015) */ + scanner_scan_all (&context, arg_list_p, arg_list_p + arg_list_size, @@ -1873,6 +1872,16 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.breakpoint_info_count = 0; #endif /* ENABLED (JERRY_DEBUGGER) */ +#if ENABLED (JERRY_ES2015_MODULE_SYSTEM) + if (parse_opts & ECMA_PARSE_EVAL) + { + /* After this point this flag is set for non-direct evals as well. */ + context.status_flags |= PARSER_IS_EVAL; + } + + context.module_current_node_p = NULL; +#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + PARSER_TRY (context.try_buffer) { /* Pushing a dummy value ensures the stack is never empty. @@ -1899,35 +1908,28 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } else { - JERRY_ASSERT (context.next_scanner_info_p->source_p == source_p - 1 + JERRY_ASSERT (context.next_scanner_info_p->source_p == source_p && 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) + if (scanner_is_global_context_needed (&context)) { - JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_BLOCK); - - if (scanner_is_context_needed (&context)) - { - parser_branch_t branch; + parser_branch_t branch; #ifndef JERRY_NDEBUG - PARSER_PLUS_EQUAL_U16 (context.context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION); + 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_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; + parser_stack_push (&context, &branch, sizeof (parser_branch_t)); + context.status_flags |= PARSER_INSIDE_BLOCK; } #endif /* ENABLED (JERRY_ES2015) */ + + scanner_create_variables (&context, SCANNER_CREATE_VARS_IS_EVAL); } parser_parse_statements (&context); diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index fba7965a3..5eba00992 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -114,9 +114,8 @@ typedef enum 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) */ + PARSER_ERR_MISSING_ASSIGN_AFTER_CONST, /**< an assignment is required after a const declaration */ -#if ENABLED (JERRY_ES2015) PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS, /**< multiple class constructor */ PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR, /**< class constructor cannot be an accessor */ PARSER_ERR_CLASS_STATIC_PROTOTYPE, /**< static method name 'prototype' is not allowed */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index 0a797a800..34a24c969 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -96,8 +96,11 @@ typedef enum SCANNER_LITERAL_POOL_FUNCTION = (1 << 0), /**< literal pool represents a function */ SCANNER_LITERAL_POOL_BLOCK = (1 << 1), /**< literal pool represents a code block */ SCANNER_LITERAL_POOL_NO_REG = (1 << 2), /**< variable declarations cannot be kept in registers */ - SCANNER_LITERAL_POOL_NO_ARGUMENTS = (1 << 3), /**< arguments object should not be constructed */ - SCANNER_LITERAL_POOL_IN_WITH = (1 << 4), /**< literal pool is in a with statement */ +#if ENABLED (JERRY_ES2015) + SCANNER_LITERAL_POOL_NO_VAR_REG = (1 << 3), /**< non let/const declarations cannot be kept in registers */ +#endif /* ENABLED (JERRY_ES2015) */ + SCANNER_LITERAL_POOL_NO_ARGUMENTS = (1 << 4), /**< arguments object should not be constructed */ + SCANNER_LITERAL_POOL_IN_WITH = (1 << 5), /**< literal pool is in a with statement */ } scanner_literal_pool_flags_t; /** @@ -157,6 +160,7 @@ lexer_lit_location_t *scanner_add_literal (parser_context_t *context_p, scanner_ void scanner_add_reference (parser_context_t *context_p, scanner_context_t *scanner_context_p); void scanner_append_argument (parser_context_t *context_p, scanner_context_t *scanner_context_p); #if ENABLED (JERRY_ES2015) +bool scanner_scope_find_let_declaration (parser_context_t *context_p, lexer_lit_location_t *literal_p); void scanner_detect_invalid_var (parser_context_t *context_p, scanner_context_t *scanner_context_p, lexer_lit_location_t *var_literal_p); #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index 565ebea39..f99855a81 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -13,6 +13,8 @@ * limitations under the License. */ +#include "ecma-helpers.h" +#include "jcontext.h" #include "js-parser-internal.h" #include "js-scanner-internal.h" #include "lit-char-helpers.h" @@ -118,9 +120,6 @@ scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ case SCANNER_STREAM_TYPE_ARG: case SCANNER_STREAM_TYPE_ARG_FUNC: case SCANNER_STREAM_TYPE_FUNC: -#if ENABLED (JERRY_ES2015) - case SCANNER_STREAM_TYPE_VAR_FUNC: -#endif /* ENABLED (JERRY_ES2015) */ { break; } @@ -391,6 +390,11 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ bool no_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_REG) != 0; bool search_arguments = is_function && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_ARGUMENTS) == 0; bool arguments_required = (no_reg && search_arguments); +#if ENABLED (JERRY_ES2015) + bool no_var_reg = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_NO_VAR_REG) != 0; +#else /* !ENABLED (JERRY_ES2015) */ + bool no_var_reg = false; +#endif /* ENABLED (JERRY_ES2015) */ if (no_reg && prev_literal_pool_p != NULL) { @@ -440,7 +444,8 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ } #if ENABLED (JERRY_ES2015) - if (is_function && (type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET)) == SCANNER_LITERAL_IS_FUNC) + if (is_function + && (type & (SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET_OR_CONST)) == SCANNER_LITERAL_IS_FUNC) { type = (uint8_t) ((type & ~SCANNER_LITERAL_IS_FUNC) | SCANNER_LITERAL_IS_VAR); literal_p->type = type; @@ -465,7 +470,7 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ no_declarations++; } - if (no_reg) + if (no_reg || (no_var_reg && (type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC)))) { type |= SCANNER_LITERAL_NO_REG; literal_p->type = type; @@ -488,7 +493,15 @@ 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 | SCANNER_LITERAL_IS_FUNC))) +#if ENABLED (JERRY_ES2015) + const uint8_t local_function_flags = SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_CONST; +#endif /* ENABLED (JERRY_ES2015) */ + + if (is_function +#if ENABLED (JERRY_ES2015) + || (type & local_function_flags) == local_function_flags +#endif /* ENABLED (JERRY_ES2015) */ + || !(type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC))) { continue; } @@ -605,6 +618,12 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { type = SCANNER_STREAM_TYPE_ARG_FUNC; } +#if ENABLED (JERRY_ES2015) + else if (literal_p->type & SCANNER_LITERAL_IS_CONST) + { + type = SCANNER_STREAM_TYPE_FUNC_LOCAL; + } +#endif /* ENABLED (JERRY_ES2015) */ } else if (literal_p->type & SCANNER_LITERAL_IS_ARG) { @@ -680,181 +699,6 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ 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. */ @@ -1110,6 +954,65 @@ scanner_detect_eval_call (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015) +/** + * Find a let/const declaration of a given literal. + * + * @return true - if the literal is found, false - otherwise + */ +bool +scanner_scope_find_let_declaration (parser_context_t *context_p, /**< context */ + lexer_lit_location_t *literal_p) /**< literal */ +{ + ecma_string_t *name_p; + + if (JERRY_LIKELY (!literal_p->has_escape)) + { + name_p = ecma_new_ecma_string_from_utf8 (literal_p->char_p, literal_p->length); + } + else + { + uint8_t *destination_p = (uint8_t *) scanner_malloc (context_p, literal_p->length); + + name_p = ecma_new_ecma_string_from_utf8 (destination_p, literal_p->length); + scanner_free (destination_p, literal_p->length); + } + + ecma_object_t *lex_env_p = JERRY_CONTEXT (vm_top_context_p)->lex_env_p; + + while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + { + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + { + ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); + + if (property_p != NULL && (*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)) + { + ecma_deref_ecma_string (name_p); + return true; + } + } + + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + } + +#if ENABLED (JERRY_ES2015) + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + { + ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); + + if (property_p != NULL && (*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)) + { + ecma_deref_ecma_string (name_p); + return true; + } + } +#endif /* ENABLED (JERRY_ES2015) */ + + ecma_deref_ecma_string (name_p); + return false; +} /* scanner_scope_find_let_declaration */ + /** * Throws an error for invalid var statements. */ @@ -1173,6 +1076,12 @@ scanner_detect_invalid_var (parser_context_t *context_p, /**< context */ } } } + + if ((context_p->status_flags & PARSER_IS_EVAL) + && scanner_scope_find_let_declaration (context_p, var_literal_p)) + { + scanner_raise_redeclaration_error (context_p); + } } /* scanner_detect_invalid_var */ #endif /* ENABLED (JERRY_ES2015) */ @@ -1354,7 +1263,7 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ || type == SCANNER_STREAM_TYPE_LET || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_FUNC - || type == SCANNER_STREAM_TYPE_VAR_FUNC); + || type == SCANNER_STREAM_TYPE_FUNC_LOCAL); #else /* !ENABLED (JERRY_ES2015) */ JERRY_ASSERT (type == SCANNER_STREAM_TYPE_VAR); #endif /* ENABLED (JERRY_ES2015) */ @@ -1418,14 +1327,7 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ } 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) @@ -1437,6 +1339,76 @@ scanner_is_context_needed (parser_context_t *context_p) /**< context */ return false; } /* scanner_is_context_needed */ +#if ENABLED (JERRY_ES2015) + +/** + * Checks whether a global context needs to be created for a script. + * + * @return true - if context is needed, + * false - otherwise + */ +bool +scanner_is_global_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_function_info_t); + uint32_t scope_stack_reg_top = 0; + + JERRY_ASSERT (info_p->type == SCANNER_TYPE_FUNCTION); + + while (data_p[0] != SCANNER_STREAM_TYPE_END) + { + uint8_t data = data_p[0]; + uint32_t type = data & SCANNER_STREAM_TYPE_MASK; + + 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_FUNC_LOCAL); + + /* Only let/const can be stored in registers */ + JERRY_ASSERT ((data & SCANNER_STREAM_NO_REG) + || type == SCANNER_STREAM_TYPE_LET + || type == SCANNER_STREAM_TYPE_CONST); + + if (!(data & SCANNER_STREAM_UINT16_DIFF)) + { + if (data_p[2] != 0) + { + data_p += 2 + 1; + } + else + { + data_p += 2 + 1 + sizeof (const uint8_t *); + } + } + else + { + data_p += 2 + 2; + } + + if (type == SCANNER_STREAM_TYPE_VAR || type == SCANNER_STREAM_TYPE_FUNC) + { + continue; + } + + if (!(data & SCANNER_STREAM_NO_REG) + && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + scope_stack_reg_top++; + } + else + { + return true; + } + } + + return false; +} /* scanner_is_global_context_needed */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Description of "arguments" literal string. */ @@ -1450,10 +1422,10 @@ const lexer_lit_location_t lexer_arguments_literal = */ void scanner_create_variables (parser_context_t *context_p, /**< context */ - size_t size) /**< size excluding the stream */ + uint32_t option_flags) /**< combination of scanner_create_variables_flags_t bits */ { scanner_info_t *info_p = context_p->next_scanner_info_p; - const uint8_t *data_p = ((const uint8_t *) info_p) + size; + const uint8_t *data_p; uint8_t info_type = info_p->type; lexer_lit_location_t literal; parser_scope_stack *scope_stack_p; @@ -1470,13 +1442,15 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ if (stack_size == 0) { - scanner_release_next (context_p, size + 1); + scanner_release_next (context_p, sizeof (scanner_function_info_t) + 1); return; } scope_stack_p = (parser_scope_stack *) parser_malloc (context_p, stack_size); context_p->scope_stack_p = scope_stack_p; scope_stack_end_p = scope_stack_p + context_p->scope_stack_size; + + data_p = ((const uint8_t *) info_p) + sizeof (scanner_function_info_t); } else { @@ -1484,6 +1458,8 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ scope_stack_p = context_p->scope_stack_p; scope_stack_end_p = scope_stack_p + context_p->scope_stack_size; scope_stack_p += context_p->scope_stack_top; + + data_p = ((const uint8_t *) info_p) + sizeof (scanner_info_t); } uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top; @@ -1588,10 +1564,6 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ 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 (func_init_opcode == CBC_INIT_LOCAL) @@ -1622,35 +1594,36 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ switch (type) { 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: +#endif /* ENABLED (JERRY_ES2015) */ { #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) + uint16_t opcode = CBC_CREATE_LOCAL; + + if (option_flags & SCANNER_CREATE_VARS_IS_EVAL) { - func_init_opcode = CBC_CREATE_VAR_FUNC; + opcode = CBC_CREATE_VAR_EVAL; } + +#if ENABLED (JERRY_ES2015) + if (type == SCANNER_STREAM_TYPE_LET) + { + opcode = CBC_CREATE_LET; + } + else if (type == SCANNER_STREAM_TYPE_CONST) + { + opcode = CBC_CREATE_CONST; + } +#endif /* ENABLED (JERRY_ES2015) */ + + parser_emit_cbc_literal (context_p, opcode, map_to); break; } -#endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_ARG: { #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) @@ -1699,6 +1672,12 @@ 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) */ + if (func_init_opcode == CBC_INIT_LOCAL + && (option_flags & SCANNER_CREATE_VARS_IS_EVAL)) + { + func_init_opcode = CBC_CREATE_VAR_FUNC_EVAL; + } + 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; diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index adad62c03..802d5887d 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -1211,18 +1211,26 @@ 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); + lexer_lit_location_t *literal_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)) + if (literal_p->type & SCANNER_LITERAL_IS_LET_OR_CONST + && !(literal_p->type & SCANNER_LITERAL_IS_FUNC)) { scanner_raise_redeclaration_error (context_p); } - location_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; + if ((context_p->status_flags & PARSER_IS_EVAL) + && scanner_scope_find_let_declaration (context_p, literal_p)) + { + literal_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_CONST; + } + else + { + literal_p->type |= SCANNER_LITERAL_IS_FUNC | SCANNER_LITERAL_IS_LET; + } #else - location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; + literal_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); @@ -1970,13 +1978,13 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ context_p->source_end_p = source_end_p; #if ENABLED (JERRY_ES2015) - uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS; + uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_VAR_REG; #else /* !ENABLED (JERRY_DEBUGGER) */ uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_REG; #endif /* ENABLED (JERRY_DEBUGGER) */ scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, status_flags); - literal_pool_p->source_p = source_p - 1; + literal_pool_p->source_p = source_p; scanner_context.mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT); @@ -2447,14 +2455,6 @@ 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 @@ -2577,9 +2577,9 @@ scan_completed: break; } #if ENABLED (JERRY_ES2015) - case SCANNER_STREAM_TYPE_VAR_FUNC: + case SCANNER_STREAM_TYPE_FUNC_LOCAL: { - JERRY_DEBUG_MSG (" VAR_FUNC "); + JERRY_DEBUG_MSG (" FUNC_LOCAL "); break; } #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index f3c5a6dac..17a782fd4 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -148,9 +148,9 @@ typedef enum 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 */ + SCANNER_STREAM_TYPE_FUNC, /**< local or global function declaration */ #if ENABLED (JERRY_ES2015) - SCANNER_STREAM_TYPE_VAR_FUNC, /**< var function declaration */ + SCANNER_STREAM_TYPE_FUNC_LOCAL, /**< always local function declaration */ #endif /* ENABLED (JERRY_ES2015) */ } scanner_compressed_stream_types_t; @@ -180,6 +180,15 @@ typedef struct scanner_info_t info; /**< header */ } scanner_function_info_t; +/** + * Option bits for scanner_create_variables function. + */ +typedef enum +{ + SCANNER_CREATE_VARS_NO_OPTS = 0, /**< no options */ + SCANNER_CREATE_VARS_IS_EVAL = (1 << 0), /**< create variables for script / direct eval */ +} scanner_create_variables_flags_t; + /** * @} * @} diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 95c576b46..be2087a69 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -25,6 +25,7 @@ #include "ecma-lex-env.h" #include "ecma-objects.h" #include "ecma-try-catch-macro.h" +#include "jcontext.h" #include "opcodes.h" #include "vm-defines.h" @@ -39,20 +40,15 @@ * 'Variable declaration' opcode handler. * * See also: ECMA-262 v5, 10.5 - Declaration binding instantiation (block 8). - * - * @return ecma value - * Returned value is simple and so need not be freed. - * However, ecma_free_value may be called for it, but it is a no-op. */ -ecma_value_t -vm_var_decl (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ - ecma_string_t *var_name_str_p) /**< variable name */ +inline void JERRY_ATTR_ALWAYS_INLINE +vm_var_decl (ecma_object_t *lex_env_p, /**< target lexical environment */ + ecma_string_t *var_name_str_p, /**< variable name */ + bool is_configurable_bindings) /**< true if the binding can be deleted */ { - if (!ecma_op_has_binding (frame_ctx_p->lex_env_p, var_name_str_p)) + if (!ecma_op_has_binding (lex_env_p, var_name_str_p)) { - const bool is_configurable_bindings = frame_ctx_p->is_eval_code; - - ecma_value_t completion_value = ecma_op_create_mutable_binding (frame_ctx_p->lex_env_p, + ecma_value_t completion_value = ecma_op_create_mutable_binding (lex_env_p, var_name_str_p, is_configurable_bindings); @@ -61,13 +57,36 @@ vm_var_decl (vm_frame_ctx_t *frame_ctx_p, /**< interpreter context */ /* Skipping SetMutableBinding as we have already checked that there were not * any binding with specified name in current lexical environment * and CreateMutableBinding sets the created binding's value to undefined */ - JERRY_ASSERT (ecma_is_value_undefined (ecma_op_get_binding_value (frame_ctx_p->lex_env_p, + JERRY_ASSERT (ecma_is_value_undefined (ecma_op_get_binding_value (lex_env_p, var_name_str_p, vm_is_strict_mode ()))); } - return ECMA_VALUE_EMPTY; } /* vm_var_decl */ +/** + * Set var binding to a function literal value. + */ +inline void JERRY_ATTR_ALWAYS_INLINE +vm_set_var (ecma_object_t *lex_env_p, /**< target lexical environment */ + ecma_string_t *var_name_str_p, /**< variable name */ + bool is_strict, /**< true, if the engine is in strict mode */ + ecma_value_t lit_value) /**< function value */ +{ + ecma_value_t put_value_result; + put_value_result = ecma_op_put_value_lex_env_base (lex_env_p, var_name_str_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); +} /* vm_set_var */ + /** * 'typeof' opcode handler. * diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index ec657c55e..3e8bb8fc0 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -51,8 +51,11 @@ typedef enum NUMBER_BITWISE_NOT, /**< bitwise NOT calculation */ } number_bitwise_logic_op; -ecma_value_t -vm_var_decl (vm_frame_ctx_t *frame_ctx_p, ecma_string_t *var_name_str_p); +void +vm_var_decl (ecma_object_t *lex_env_p, ecma_string_t *var_name_str_p, bool is_configurable_bindings); + +void +vm_set_var (ecma_object_t *lex_env_p, ecma_string_t *var_name_str_p, bool is_strict, ecma_value_t lit_value); ecma_value_t opfunc_equality (ecma_value_t left_value, ecma_value_t right_value); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index f0064f338..342683644 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -797,9 +797,15 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ while (true) { - switch (*byte_code_p) + uint8_t type = *byte_code_p; + + switch (type) { - case CBC_CREATE_VAR: + case CBC_CREATE_LOCAL: +#if ENABLED (JERRY_ES2015) + case CBC_CREATE_LET: + case CBC_CREATE_CONST: +#endif /* ENABLED (JERRY_ES2015) */ { uint32_t literal_index; @@ -807,7 +813,24 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ READ_LITERAL_INDEX (literal_index); ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); - vm_var_decl (frame_ctx_p, name_p); + + JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); + + uint8_t prop_attributes = ECMA_PROPERTY_FLAG_WRITABLE; + +#if ENABLED (JERRY_ES2015) + if (type == CBC_CREATE_LET) + { + prop_attributes = ECMA_PROPERTY_ENUMERABLE_WRITABLE; + } + else if (type == CBC_CREATE_CONST) + { + prop_attributes = ECMA_PROPERTY_FLAG_ENUMERABLE; + } +#endif /* ENABLED (JERRY_ES2015) */ + + ecma_create_named_data_property (frame_ctx_p->lex_env_p, name_p, prop_attributes, NULL); break; } @@ -825,7 +848,8 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (value_index < register_end) { - lit_value = frame_ctx_p->registers_p[value_index]; + /* Take (not copy) the reference. */ + lit_value = ecma_copy_value_if_not_object (frame_ctx_p->registers_p[value_index]); } else { @@ -842,21 +866,17 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); - vm_var_decl (frame_ctx_p, name_p); + JERRY_ASSERT (ecma_get_lex_env_type (frame_ctx_p->lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + JERRY_ASSERT (ecma_find_named_property (frame_ctx_p->lex_env_p, name_p) == NULL); - 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); + ecma_property_value_t *property_value_p; + property_value_p = ecma_create_named_data_property (frame_ctx_p->lex_env_p, + name_p, + ECMA_PROPERTY_FLAG_WRITABLE, + NULL); - 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)); - } + JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED); + property_value_p->value = lit_value; if (value_index >= register_end) { @@ -865,11 +885,64 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ break; } + case CBC_CREATE_VAR_EVAL: + case CBC_CREATE_VAR_FUNC_EVAL: + { + uint32_t literal_index; + ecma_value_t lit_value = ECMA_VALUE_UNDEFINED; + + byte_code_p++; + + if (type == CBC_CREATE_VAR_FUNC_EVAL) + { + uint32_t value_index; + READ_LITERAL_INDEX (value_index); + + lit_value = vm_construct_literal_object (frame_ctx_p, + literal_start_p[value_index]); + } + + READ_LITERAL_INDEX (literal_index); + + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + + while (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + { +#if ENABLED (JERRY_ES2015) && !(defined JERRY_NDEBUG) + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + { + ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); + + JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); + } +#endif /* ENABLED (JERRY_ES2015) && !JERRY_NDEBUG */ + + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + } + +#if ENABLED (JERRY_ES2015) && !(defined JERRY_NDEBUG) + if (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) + { + ecma_property_t *property_p = ecma_find_named_property (lex_env_p, name_p); + + JERRY_ASSERT (property_p == NULL || !(*property_p & ECMA_PROPERTY_FLAG_ENUMERABLE)); + } +#endif /* ENABLED (JERRY_ES2015) && !JERRY_NDEBUG */ + + vm_var_decl (lex_env_p, name_p, frame_ctx_p->is_eval_code); + + if (lit_value != ECMA_VALUE_UNDEFINED) + { + vm_set_var (lex_env_p, name_p, is_strict, 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; @@ -892,40 +965,8 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } 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); + vm_set_var (frame_ctx_p->lex_env_p, name_p, is_strict, lit_value); break; } #endif /* ENABLED (JERRY_ES2015) */ @@ -2868,9 +2909,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ #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->lex_env_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_BLOCK; frame_ctx_p->byte_code_p = byte_code_p; vm_init_loop (frame_ctx_p); @@ -2907,6 +2946,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ stack_top_p[-1] = VM_CREATE_CONTEXT_WITH_ENV (VM_CONTEXT_WITH, branch_offset); + with_env_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_BLOCK; frame_ctx_p->lex_env_p = with_env_p; continue; } diff --git a/tests/jerry/es2015/let2.js b/tests/jerry/es2015/let2.js index 0372c780f..9636e226a 100644 --- a/tests/jerry/es2015/let2.js +++ b/tests/jerry/es2015/let2.js @@ -29,6 +29,9 @@ 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 ("const a"); +check_syntax_error ("{ const a }"); +check_syntax_error ("const a, b"); 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;"); @@ -41,4 +44,4 @@ 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"); +check_syntax_error ("class A {}; const A = 1"); diff --git a/tests/jerry/es2015/let9.js b/tests/jerry/es2015/let9.js new file mode 100644 index 000000000..29797ed25 --- /dev/null +++ b/tests/jerry/es2015/let9.js @@ -0,0 +1,48 @@ +/* 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() { + try { + a; + assert (false); + } catch (e) { + assert (e instanceof ReferenceError); + } + + eval ("assert (a === undefined); { function a() { return 5; } }"); + assert (a() === 5); + + /* Variables created by eval can be deleted. */ + delete a; + + try { + a; + assert (false); + } catch (e) { + assert (e instanceof ReferenceError); + } +} +f(); + +function g() { + let a = 1; + + eval ("assert (a === 1);" + + "{ function a() { return 2; } assert (a() === 2) }" + + "assert (a === 1);"); + + assert (a === 1); +} +g(); diff --git a/tests/jerry/eval-with.js b/tests/jerry/eval-with.js new file mode 100644 index 000000000..594222171 --- /dev/null +++ b/tests/jerry/eval-with.js @@ -0,0 +1,53 @@ +// 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() { + try { + a; + assert (false); + } catch (e) { + assert (e instanceof ReferenceError); + } + + var o = { a:1 }; + + with (o) + { + eval("var a = 2") + } + + assert (o.a === 2); + assert (a === undefined); +} +f(); + +function g() { + try { + a; + assert (false); + } catch (e) { + assert (e instanceof ReferenceError); + } + + var o = { a:1 }; + + with (o) + { + eval("function a() { return 8; }") + } + + assert (o.a === 1); + assert (a() === 8); +} +g(); diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 65287a9e3..cc075b34f 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, 0x1A, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x1B, 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, - 0x2C, 0x00, 0xBC, 0x4A, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0xBF, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4E, 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,