From 7df87b7778124af9b0dd309570c33864a67792f7 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 17 Oct 2019 15:20:04 +0200 Subject: [PATCH] Create function variables using the data produced by the pre-scanner. (#3199) The pre-scanner now is able to track all variable declarations and produces a compressed stream which store this data for each function and catch block. When a function or catch block is parsed, this information is decoded and the appropriate variables are created. Furthermore a stack scope is created which contains the currently available local variables and their register or literal index. 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 | 6 +- jerry-core/parser/js/common.c | 9 +- jerry-core/parser/js/common.h | 14 +- jerry-core/parser/js/js-lexer.c | 153 +-- jerry-core/parser/js/js-parser-expr.c | 27 +- jerry-core/parser/js/js-parser-internal.h | 48 +- jerry-core/parser/js/js-parser-limits.h | 40 +- jerry-core/parser/js/js-parser-statm.c | 115 +- jerry-core/parser/js/js-parser-util.c | 92 +- jerry-core/parser/js/js-parser.c | 1110 +++++--------------- jerry-core/parser/js/js-parser.h | 2 +- jerry-core/parser/js/js-scanner-internal.h | 69 +- jerry-core/parser/js/js-scanner-util.c | 918 +++++++++++++++- jerry-core/parser/js/js-scanner.c | 511 +++++++-- jerry-core/parser/js/js-scanner.h | 65 +- jerry-core/vm/vm.c | 124 +-- tests/debugger/do_variables.expected | 2 +- tests/jerry/regression-test-issue-1555.js | 2 +- tests/unit-core/test-snapshot.c | 8 +- 20 files changed, 2098 insertions(+), 1219 deletions(-) diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 12f21315e..6753328ab 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 (24u) +#define JERRY_SNAPSHOT_VERSION (25u) /** * 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 57a626a03..efbc569b8 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -298,11 +298,9 @@ VM_OC_NEW | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EVAL, CBC_NO_FLAG, 0, \ VM_OC_EVAL) \ - CBC_OPCODE (CBC_DEFINE_VARS, CBC_HAS_LITERAL_ARG, 0, \ + CBC_OPCODE (CBC_CREATE_VAR, CBC_HAS_LITERAL_ARG, 0, \ VM_OC_NONE) \ - CBC_OPCODE (CBC_INITIALIZE_VAR, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ - VM_OC_NONE) \ - CBC_OPCODE (CBC_INITIALIZE_VARS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ + CBC_OPCODE (CBC_INIT_LOCAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_NONE) \ CBC_OPCODE (CBC_SET_BYTECODE_PTR, CBC_NO_FLAG, 0, \ VM_OC_NONE) \ diff --git a/jerry-core/parser/js/common.c b/jerry-core/parser/js/common.c index d3a54093e..5aeca88dd 100644 --- a/jerry-core/parser/js/common.c +++ b/jerry-core/parser/js/common.c @@ -85,14 +85,7 @@ util_print_literal (lexer_literal_t *literal_p) /**< literal */ { if (literal_p->type == LEXER_IDENT_LITERAL) { - if (literal_p->status_flags & LEXER_FLAG_VAR) - { - JERRY_DEBUG_MSG ("var_ident("); - } - else - { - JERRY_DEBUG_MSG ("ident("); - } + JERRY_DEBUG_MSG ("ident("); util_print_chars (literal_p->u.char_p, literal_p->prop.length); } else if (literal_p->type == LEXER_FUNCTION_LITERAL) diff --git a/jerry-core/parser/js/common.h b/jerry-core/parser/js/common.h index 184797722..65fe8cc25 100644 --- a/jerry-core/parser/js/common.h +++ b/jerry-core/parser/js/common.h @@ -55,6 +55,8 @@ typedef enum LEXER_REGEXP_LITERAL = 4, /**< regexp literal */ LEXER_UNUSED_LITERAL = 5, /**< unused literal, can only be used by the byte code generator. */ + LEXER_NEW_IDENT_LITERAL = 6, /**< new local variable, can only be + used by the byte code generator. */ } lexer_literal_type_t; /** @@ -67,15 +69,11 @@ typedef enum */ typedef enum { - LEXER_FLAG_VAR = (1 << 0), /**< local identifier (var, function arg) */ - LEXER_FLAG_NO_REG_STORE = (1 << 1), /**< this local identifier cannot be stored in register */ - LEXER_FLAG_INITIALIZED = (1 << 2), /**< this local identifier is initialized with a value */ - LEXER_FLAG_FUNCTION_ARGUMENT = (1 << 3), /**< this local identifier is a function argument */ - LEXER_FLAG_UNUSED_IDENT = (1 << 4), /**< this identifier is referenced by sub-functions, - * but not referenced by the currently parsed function */ - LEXER_FLAG_SOURCE_PTR = (1 << 5), /**< the literal is directly referenced in the source code + LEXER_FLAG_USED = (1 << 0), /**< this local identifier needs to be stored in the constant pool */ + LEXER_FLAG_FUNCTION_ARGUMENT = (1 << 1), /**< this local identifier is a function argument */ + 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 << 6), /**< initialize this variable after the byte code is freed */ + LEXER_FLAG_LATE_INIT = (1 << 3), /**< initialize this variable after the byte code is freed */ } lexer_literal_status_flags_t; /** diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index f5022f2de..17ae12514 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -1387,7 +1387,7 @@ lexer_next_token (parser_context_t *context_p) /**< context */ #undef LEXER_TYPE_D_TOKEN /** - * Checks whether the next token is the specified character. + * Checks whether the next token starts with the specified character. * * @return true - if the next is the specified character * false - otherwise @@ -1443,6 +1443,12 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; uint32_t literal_index = 0; + bool search_scope_stack = (literal_type == LEXER_IDENT_LITERAL); + + if (JERRY_UNLIKELY (literal_type == LEXER_NEW_IDENT_LITERAL)) + { + literal_type = LEXER_IDENT_LITERAL; + } JERRY_ASSERT (literal_type == LEXER_IDENT_LITERAL || literal_type == LEXER_STRING_LITERAL); @@ -1460,7 +1466,27 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ { context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; - literal_p->status_flags = (uint8_t) (literal_p->status_flags & ~LEXER_FLAG_UNUSED_IDENT); + + if (search_scope_stack) + { + 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) + { + JERRY_ASSERT (scope_stack_p->map_to >= PARSER_REGISTER_START + || (literal_p->status_flags & LEXER_FLAG_USED)); + context_p->lit_object.index = scope_stack_p->map_to; + return; + } + } + + literal_p->status_flags |= LEXER_FLAG_USED; + } return; } @@ -1482,10 +1508,12 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); literal_p->prop.length = (prop_length_t) length; literal_p->type = literal_type; - literal_p->status_flags = has_escape ? 0 : LEXER_FLAG_SOURCE_PTR; + + uint8_t status_flags = LEXER_FLAG_SOURCE_PTR; if (has_escape) { + status_flags = 0; literal_p->u.char_p = (uint8_t *) jmem_heap_alloc_block (length); memcpy ((uint8_t *) literal_p->u.char_p, char_p, length); } @@ -1494,6 +1522,13 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ literal_p->u.char_p = char_p; } + if (search_scope_stack) + { + status_flags |= LEXER_FLAG_USED; + } + + literal_p->status_flags = status_flags; + context_p->lit_object.literal_p = literal_p; context_p->lit_object.index = (uint16_t) literal_index; context_p->literal_count++; @@ -1509,7 +1544,7 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */ */ void lexer_construct_literal_object (parser_context_t *context_p, /**< context */ - lexer_lit_location_t *literal_p, /**< literal location */ + const lexer_lit_location_t *literal_p, /**< literal location */ uint8_t literal_type) /**< final literal type */ { uint8_t *destination_start_p; @@ -1518,7 +1553,7 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL || literal_p->type == LEXER_STRING_LITERAL); - JERRY_ASSERT (context_p->allocated_buffer_p == NULL); + JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL); destination_start_p = local_byte_array; source_p = literal_p->char_p; @@ -1530,7 +1565,7 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE) { destination_start_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length); - context_p->allocated_buffer_p = destination_start_p; + context_p->u.allocated_buffer_p = destination_start_p; context_p->allocated_buffer_size = literal_p->length; } @@ -1764,16 +1799,8 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ literal_type, literal_p->has_escape); - JERRY_ASSERT (literal_type == context_p->lit_object.literal_p->type); - context_p->lit_object.type = LEXER_LITERAL_OBJECT_ANY; - if (literal_type == LEXER_IDENT_LITERAL - && (context_p->status_flags & PARSER_INSIDE_WITH)) - { - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - } - if (literal_p->length == 4 && source_p[0] == LIT_CHAR_LOWERCASE_E && source_p[3] == LIT_CHAR_LOWERCASE_L @@ -1789,24 +1816,18 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ && memcmp (source_p + 1, "rgument", 7) == 0) { context_p->lit_object.type = LEXER_LITERAL_OBJECT_ARGUMENTS; - if (!(context_p->status_flags & PARSER_ARGUMENTS_NOT_NEEDED) - && literal_type == LEXER_IDENT_LITERAL) - { - context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED; - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - } } if (destination_start_p != local_byte_array) { - JERRY_ASSERT (context_p->allocated_buffer_p == destination_start_p); + JERRY_ASSERT (context_p->u.allocated_buffer_p == destination_start_p); - context_p->allocated_buffer_p = NULL; + context_p->u.allocated_buffer_p = NULL; parser_free_local (destination_start_p, context_p->allocated_buffer_size); } - JERRY_ASSERT (context_p->allocated_buffer_p == NULL); + JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL); } /* lexer_construct_literal_object */ #undef LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE @@ -2251,7 +2272,8 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ uint8_t literal_type) /**< literal type */ { JERRY_ASSERT (literal_type == LEXER_STRING_LITERAL - || literal_type == LEXER_IDENT_LITERAL); + || literal_type == LEXER_IDENT_LITERAL + || literal_type == LEXER_NEW_IDENT_LITERAL); lexer_skip_spaces (context_p); context_p->token.line = context_p->line; @@ -2264,11 +2286,13 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_LITERAL) { + JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL); + lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type); - if (literal_type == LEXER_IDENT_LITERAL + if (literal_type != LEXER_STRING_LITERAL && (context_p->status_flags & PARSER_IS_STRICT) && context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) { @@ -2286,8 +2310,6 @@ lexer_expect_identifier (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, error); } - - context_p->token.lit_location.type = literal_type; return; } } @@ -2300,9 +2322,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_lit_location_t *) &lexer_default_literal, - lexer_default_literal.type); + lexer_construct_literal_object (context_p, &lexer_default_literal, lexer_default_literal.type); context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC); return; } @@ -2519,43 +2539,23 @@ lexer_scan_identifier (parser_context_t *context_p, /**< context */ } /* lexer_scan_identifier */ /** - * Compares the current identifier in the context to the parameter identifier + * Compares two identifiers. * * Note: - * Escape sequences are allowed. + * Escape sequences are allowed, size must be the same. * - * @return true if the input identifiers are the same + * @return true if the two identifiers are the same */ bool -lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context */ - const lexer_lit_location_t *right_ident_p) /**< identifier */ +lexer_compare_identifiers (const uint8_t *left_p, /**< left identifier */ + const uint8_t *right_p, /**< right identifier */ + size_t size) /**< byte size of the two identifiers */ { - lexer_lit_location_t *left_ident_p = &context_p->token.lit_location; - const uint8_t *left_p; - const uint8_t *right_p; - size_t count; - - JERRY_ASSERT (left_ident_p->length > 0 && right_ident_p->length > 0); - - if (left_ident_p->length != right_ident_p->length) - { - return 0; - } - - if (!left_ident_p->has_escape && !right_ident_p->has_escape) - { - return memcmp (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length) == 0; - } - - left_p = left_ident_p->char_p; - right_p = right_ident_p->char_p; - count = left_ident_p->length; + uint8_t utf8_buf[3]; + size_t utf8_len, offset; do { - uint8_t utf8_buf[3]; - size_t utf8_len, offset; - /* Backslash cannot be part of a multibyte UTF-8 character. */ if (*left_p != LIT_CHAR_BACKSLASH && *right_p != LIT_CHAR_BACKSLASH) { @@ -2563,7 +2563,7 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * { return false; } - count--; + size--; continue; } @@ -2578,7 +2578,7 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * left_p += 6; right_p += 6; - count += lit_char_get_utf8_length (left_chr); + size -= lit_char_get_utf8_length (left_chr); continue; } @@ -2593,7 +2593,7 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * utf8_len = lit_char_to_utf8_bytes (utf8_buf, lexer_unchecked_hex_to_character (left_p + 2, 4)); JERRY_ASSERT (utf8_len > 0); - count -= utf8_len; + size -= utf8_len; offset = 0; do @@ -2608,9 +2608,38 @@ lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context * left_p += 6; } - while (count > 0); + while (size > 0); return true; +} /* lexer_compare_identifiers */ + +/** + * Compares the current identifier in the context to the parameter identifier + * + * Note: + * Escape sequences are allowed. + * + * @return true if the input identifiers are the same + */ +bool +lexer_compare_identifier_to_current (parser_context_t *context_p, /**< context */ + const lexer_lit_location_t *right_ident_p) /**< identifier */ +{ + lexer_lit_location_t *left_ident_p = &context_p->token.lit_location; + + JERRY_ASSERT (left_ident_p->length > 0 && right_ident_p->length > 0); + + if (left_ident_p->length != right_ident_p->length) + { + return 0; + } + + if (!left_ident_p->has_escape && !right_ident_p->has_escape) + { + return memcmp (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length) == 0; + } + + return lexer_compare_identifiers (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length); } /* lexer_compare_identifier_to_current */ /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 46b456df9..96d52514a 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -601,7 +601,6 @@ parser_parse_class (parser_context_t *context_p, /**< context */ && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); class_ident_index = context_p->lit_object.index; - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) @@ -1167,7 +1166,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_ARROW_FUNCTION) if (context_p->next_scanner_info_p->source_p == context_p->source_p) { - JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ARROW); + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); break; } #endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ @@ -1209,8 +1208,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_ARROW_FUNCTION) if (context_p->next_scanner_info_p->source_p == context_p->source_p) { - JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ARROW); - scanner_release_next (context_p, sizeof (scanner_info_t)); + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); parser_parse_function_expression (context_p, PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION); @@ -1218,23 +1216,24 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ - if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL - || context_p->token.lit_location.type == LEXER_STRING_LITERAL) + uint8_t type = context_p->token.lit_location.type; + + if (type == LEXER_IDENT_LITERAL || type == LEXER_STRING_LITERAL) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); + #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - if (context_p->status_flags & PARSER_MODULE_STORE_IDENT - && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + if ((context_p->status_flags & PARSER_MODULE_STORE_IDENT) + && type == LEXER_IDENT_LITERAL) { - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; context_p->module_identifier_lit_p = context_p->lit_object.literal_p; context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); } #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ } - else if (context_p->token.lit_location.type == LEXER_NUMBER_LITERAL) + else if (type == LEXER_NUMBER_LITERAL) { bool is_negative_number = false; @@ -1418,8 +1417,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ case LEXER_LEFT_PAREN: { JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p - && context_p->next_scanner_info_p->type == SCANNER_TYPE_ARROW); - scanner_release_next (context_p, sizeof (scanner_info_t)); + && context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); lexer_next_token (context_p); parser_parse_function_expression (context_p, @@ -1479,7 +1477,8 @@ parser_process_unary_expression (parser_context_t *context_p) /**< context */ lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL - && context_p->token.lit_location.type == LEXER_STRING_LITERAL); + && context_p->lit_object.literal_p->type == LEXER_STRING_LITERAL); + context_p->token.lit_location.type = LEXER_STRING_LITERAL; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { @@ -1543,7 +1542,6 @@ parser_process_unary_expression (parser_context_t *context_p) /**< context */ && context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { - context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; is_eval = true; } @@ -1796,7 +1794,6 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */ parser_stack_push_uint8 (context_p, CBC_ASSIGN); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } - else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index d756f6dac..1a4ceb5a1 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -56,8 +56,6 @@ typedef enum PARSER_ARGUMENTS_NEEDED = (1u << 8), /**< arguments object must be created */ PARSER_ARGUMENTS_NOT_NEEDED = (1u << 9), /**< arguments object must NOT be created */ PARSER_LEXICAL_ENV_NEEDED = (1u << 10), /**< lexical environment object must be created */ - PARSER_NO_REG_STORE = (1u << 11), /**< all local variables must be stored - * in the lexical environment object */ PARSER_INSIDE_WITH = (1u << 12), /**< code block is inside a with statement */ PARSER_RESOLVE_BASE_FOR_CALLS = (1u << 13), /**< the this object must be resolved when * a function without a base object is called */ @@ -313,6 +311,29 @@ typedef struct parser_branch_node_t parser_branch_t branch; /**< branch */ } parser_branch_node_t; +/** + * Items of scope stack. + */ +typedef struct +{ + uint16_t map_from; /**< original literal index */ + uint16_t map_to; /**< register or literal index */ +} parser_scope_stack; + +/** + * This item represents a function literal in the scope stack. + */ +#define PARSER_SCOPE_STACK_FUNC 0xffff + +/** + * Starting literal index for registers. + */ +#define PARSER_REGISTER_START 0x8000 + +/* Forward definitions for js-scanner-internal.h. */ +struct scanner_context_t; +typedef struct scanner_context_t scanner_context_t; + #if ENABLED (JERRY_DEBUGGER) /** * Extra information for each breakpoint. @@ -352,6 +373,10 @@ typedef struct parser_saved_context_t parser_mem_data_t byte_code; /**< byte code buffer */ uint32_t byte_code_size; /**< byte code size for branches */ 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 */ #ifndef JERRY_NDEBUG uint16_t context_stack_depth; /**< current context stack depth */ @@ -365,8 +390,13 @@ typedef struct { PARSER_TRY_CONTEXT (try_buffer); /**< try_buffer */ parser_error_t error; /**< error code */ - void *allocated_buffer_p; /**< dinamically allocated buffer + /** Union for rarely used members. */ + union + { + void *allocated_buffer_p; /**< dinamically allocated buffer * which needs to be freed on error */ + scanner_context_t *scanner_context_p; /**< scanner context for the pre-scanner */ + } u; uint32_t allocated_buffer_size; /**< size of the dinamically allocated buffer */ /* Parser members. */ @@ -409,7 +439,11 @@ typedef struct uint32_t byte_code_size; /**< current byte code size for branches */ parser_list_t literal_pool; /**< literal list */ parser_mem_data_t stack; /**< storage space */ + parser_scope_stack *scope_stack_p; /**< scope stack */ parser_mem_page_t *free_page_p; /**< space for fast allocation */ + 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 */ uint8_t stack_top_uint8; /**< top byte stored on the stack */ #ifndef JERRY_NDEBUG @@ -502,6 +536,8 @@ void parser_stack_iterator_write (parser_stack_iterator_t *iterator, const void void parser_flush_cbc (parser_context_t *context_p); void parser_emit_cbc (parser_context_t *context_p, uint16_t opcode); void parser_emit_cbc_literal (parser_context_t *context_p, uint16_t opcode, uint16_t literal_index); +void parser_emit_cbc_literal_value (parser_context_t *context_p, uint16_t opcode, uint16_t literal_index, + uint16_t value); void parser_emit_cbc_literal_from_token (parser_context_t *context_p, uint16_t opcode); void parser_emit_cbc_call (parser_context_t *context_p, uint16_t opcode, size_t call_arguments); void parser_emit_cbc_push_number (parser_context_t *context_p, bool is_negative_number); @@ -547,12 +583,13 @@ 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_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts); -void lexer_construct_literal_object (parser_context_t *context_p, lexer_lit_location_t *literal_p, +void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *literal_p, uint8_t literal_type); bool lexer_construct_number_object (parser_context_t *context_p, bool is_expr, bool is_negative_number); void lexer_convert_push_number_to_push_literal (parser_context_t *context_p); uint16_t lexer_construct_function_object (parser_context_t *context_p, uint32_t extra_status_flags); void lexer_construct_regexp_object (parser_context_t *context_p, bool parse_only); +bool lexer_compare_identifiers (const uint8_t *left_p, const uint8_t *right_p, size_t size); bool lexer_compare_identifier_to_current (parser_context_t *context_p, const lexer_lit_location_t *right_ident_p); bool lexer_compare_literal_to_identifier (parser_context_t *context_p, const char *identifier_p, size_t identifier_length); @@ -588,7 +625,6 @@ 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); -scanner_info_t *scanner_insert_info (parser_context_t *context_p, const uint8_t *source_p, 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); @@ -597,6 +633,8 @@ 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); +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); void scanner_set_location (parser_context_t *context_p, scanner_location_t *location_p); diff --git a/jerry-core/parser/js/js-parser-limits.h b/jerry-core/parser/js/js-parser-limits.h index 89ed26732..d37ccd6c7 100644 --- a/jerry-core/parser/js/js-parser-limits.h +++ b/jerry-core/parser/js/js-parser-limits.h @@ -52,22 +52,30 @@ #define PARSER_MAXIMUM_STRING_LENGTH PARSER_MAXIMUM_STRING_LIMIT #endif /* !PARSER_MAXIMUM_STRING_LENGTH */ -/** - * Maximum number of literals. - * Limit: 32767. Recommended: 510, 32767 - */ -#ifndef PARSER_MAXIMUM_NUMBER_OF_LITERALS -#define PARSER_MAXIMUM_NUMBER_OF_LITERALS 32767 -#endif /* !PARSER_MAXIMUM_NUMBER_OF_LITERALS */ - /** * Maximum number of registers. - * Limit: PARSER_MAXIMUM_NUMBER_OF_LITERALS + * Limit: min: 256, max: PARSER_MAXIMUM_NUMBER_OF_LITERALS / 2 */ #ifndef PARSER_MAXIMUM_NUMBER_OF_REGISTERS #define PARSER_MAXIMUM_NUMBER_OF_REGISTERS 256 #endif /* !PARSER_MAXIMUM_NUMBER_OF_REGISTERS */ +/** + * Maximum number of literals. + * Limit: 32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS. Recommended: 32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS. + */ +#ifndef PARSER_MAXIMUM_NUMBER_OF_LITERALS +#define PARSER_MAXIMUM_NUMBER_OF_LITERALS (32767 - PARSER_MAXIMUM_NUMBER_OF_REGISTERS) +#endif /* !PARSER_MAXIMUM_NUMBER_OF_LITERALS */ + +/** + * Maximum depth of scope stack. + * Limit: 32767. Recommended: 32767 + */ +#ifndef PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK +#define PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK 32767 +#endif /* !PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK */ + /** * Maximum code size. * Limit: 16777215. Recommended: 65535, 16777215. @@ -95,13 +103,19 @@ #error "Maximum identifier length is not within range." #endif /* (PARSER_MAXIMUM_IDENT_LENGTH < 1) || (PARSER_MAXIMUM_IDENT_LENGTH > PARSER_MAXIMUM_STRING_LENGTH) */ -#if (PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767) +#if ((PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) \ + || (PARSER_MAXIMUM_NUMBER_OF_LITERALS + PARSER_MAXIMUM_NUMBER_OF_REGISTERS > 32767)) #error "Maximum number of literals is not within range." -#endif /* (PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767) */ +#endif /* ((PARSER_MAXIMUM_NUMBER_OF_LITERALS < 1) \ + || (PARSER_MAXIMUM_NUMBER_OF_LITERALS > 32767)) */ -#if (PARSER_MAXIMUM_NUMBER_OF_REGISTERS > PARSER_MAXIMUM_NUMBER_OF_LITERALS) +#if (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK < 1) || (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK > 32767) +#error "Maximum depth of scope stack is not within range." +#endif /* (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK < 1) || (PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK > 32767) */ + +#if ((PARSER_MAXIMUM_NUMBER_OF_REGISTERS * 2) > PARSER_MAXIMUM_NUMBER_OF_LITERALS) #error "Maximum number of registers is not within range." -#endif /* (PARSER_MAXIMUM_NUMBER_OF_REGISTERS > PARSER_MAXIMUM_NUMBER_OF_LITERALS) */ +#endif /* ((PARSER_MAXIMUM_NUMBER_OF_REGISTERS * 2) > PARSER_MAXIMUM_NUMBER_OF_LITERALS) */ #if (PARSER_MAXIMUM_CODE_SIZE < 4096) || (PARSER_MAXIMUM_CODE_SIZE > 16777215) #error "Maximum code size is not within range." diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 1d96edb39..45ba780a2 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -181,6 +181,8 @@ typedef enum typedef struct { parser_try_block_type_t type; /**< current block type */ + uint16_t scope_stack_top; /**< current top of scope stack */ + uint16_t scope_stack_reg_top; /**< current top register of scope stack */ parser_branch_t branch; /**< branch to the end of the current block */ } parser_try_statement_t; @@ -310,8 +312,6 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ parser_line_counter_t ident_line_counter = context_p->token.line; #endif /* ENABLED (JERRY_DEBUGGER) || ENABLED (JERRY_LINE_INFO) */ - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; - #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) { @@ -362,9 +362,7 @@ static void parser_parse_function_statement (parser_context_t *context_p) /**< context */ { uint32_t status_flags; - lexer_literal_t *name_p; lexer_literal_t *literal_p; - uint8_t no_reg_store; JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); @@ -373,7 +371,7 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ parser_line_counter_t debugger_column = context_p->token.column; #endif /* ENABLED (JERRY_DEBUGGER) */ - lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); + lexer_expect_identifier (context_p, LEXER_NEW_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); @@ -382,8 +380,6 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; } - name_p = context_p->lit_object.literal_p; - #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) if (context_p->status_flags & PARSER_MODULE_STORE_IDENT) { @@ -403,6 +399,7 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ #if ENABLED (JERRY_DEBUGGER) if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { + lexer_literal_t *name_p = context_p->lit_object.literal_p; jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_NO_SUBTYPE, name_p->u.char_p, @@ -414,55 +411,35 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_DEBUGGER) */ - if (name_p->status_flags & LEXER_FLAG_INITIALIZED) + JERRY_ASSERT (context_p->scope_stack_top >= 2); + parser_scope_stack *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top - 2; + + uint16_t literal_index = context_p->lit_object.index; + + while (literal_index != scope_stack_p->map_from) { - if (!(name_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) - { - /* Overwrite the previous initialization. */ - ecma_compiled_code_t *compiled_code_p; + scope_stack_p--; - literal_p = PARSER_GET_LITERAL ((size_t) (context_p->lit_object.index + 1)); - - JERRY_ASSERT (literal_p->type == LEXER_FUNCTION_LITERAL - && literal_p->status_flags == 0); - - compiled_code_p = parser_parse_function (context_p, status_flags); - util_free_literal (literal_p); - - literal_p->u.bytecode_p = compiled_code_p; - lexer_next_token (context_p); - return; - } - } - else if (context_p->lit_object.index + 1 == context_p->literal_count) - { - /* The most common case: the literal is the last literal. */ - name_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED; - lexer_construct_function_object (context_p, status_flags); - lexer_next_token (context_p); - return; + JERRY_ASSERT (scope_stack_p >= context_p->scope_stack_p); } - /* Clone the literal at the end. */ - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + JERRY_ASSERT (scope_stack_p[1].map_from == PARSER_SCOPE_STACK_FUNC); + + literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to); + + JERRY_ASSERT ((literal_p->type == LEXER_UNUSED_LITERAL || literal_p->type == LEXER_FUNCTION_LITERAL) + && literal_p->status_flags == 0); + + ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, status_flags); + + if (literal_p->type == LEXER_FUNCTION_LITERAL) { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + ecma_bytecode_deref (literal_p->u.bytecode_p); } - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - *literal_p = *name_p; - no_reg_store = name_p->status_flags & (LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_SOURCE_PTR); - literal_p->status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | no_reg_store; + literal_p->u.bytecode_p = compiled_code_p; + literal_p->type = LEXER_FUNCTION_LITERAL; - name_p->type = LEXER_UNUSED_LITERAL; - name_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT | LEXER_FLAG_SOURCE_PTR; - /* Byte code references to this literal are - * redirected to the newly allocated literal. */ - name_p->prop.index = context_p->literal_count; - - context_p->literal_count++; - - lexer_construct_function_object (context_p, status_flags); lexer_next_token (context_p); } /* parser_parse_function_statement */ @@ -967,8 +944,6 @@ parser_parse_for_statement_start (parser_context_t *context_p) /**< context */ JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; - literal_index = context_p->lit_object.index; lexer_next_token (context_p); @@ -1400,6 +1375,9 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ if (try_statement.type == parser_catch_block) { + context_p->scope_stack_top = try_statement.scope_stack_top; + context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top; + if (context_p->token.type != LEXER_KEYW_FINALLY) { parser_flush_cbc (context_p); @@ -1410,6 +1388,7 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_emit_cbc (context_p, CBC_CONTEXT_END); parser_flush_cbc (context_p); + try_statement.type = parser_finally_block; } } @@ -1439,12 +1418,34 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } + try_statement.type = parser_catch_block; + parser_emit_cbc_ext_forward_branch (context_p, + CBC_EXT_CATCH, + &try_statement.branch); + + try_statement.scope_stack_top = context_p->scope_stack_top; + try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top; + +#ifndef JERRY_NDEBUG + 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) + { +#ifndef JERRY_NDEBUG + block_found = true; +#endif /* !JERRY_NDEBUG */ + scanner_create_variables (context_p, sizeof (scanner_info_t)); + } + lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; +#ifndef JERRY_NDEBUG + JERRY_ASSERT (block_found); +#endif /* !JERRY_NDEBUG */ literal_index = context_p->lit_object.index; @@ -1462,11 +1463,6 @@ parser_parse_try_statement_end (parser_context_t *context_p) /**< context */ parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } - try_statement.type = parser_catch_block; - parser_emit_cbc_ext_forward_branch (context_p, - CBC_EXT_CATCH, - &try_statement.branch); - parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index); parser_flush_cbc (context_p); } @@ -1960,10 +1956,7 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ scanner_set_location (context_p, &location); /* 15.2.3.5 Use the synthetic name '*default*' as the identifier. */ - lexer_construct_literal_object (context_p, - (lexer_lit_location_t *) &lexer_default_literal, - lexer_default_literal.type); - context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; + lexer_construct_literal_object (context_p, &lexer_default_literal, lexer_default_literal.type); context_p->token.lit_location.type = LEXER_IDENT_LITERAL; parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index aa83a0f79..1802ff5cc 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -85,6 +85,51 @@ parser_emit_two_bytes (parser_context_t *context_p, /**< context */ } \ (context_p)->byte_code.last_p->bytes[(context_p)->byte_code.last_position++] = (uint8_t) (byte) +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + +/** + * Print literal corresponding to the current index + */ +static void +parser_print_literal (parser_context_t *context_p, /**< context */ + uint16_t literal_index) /**< index of literal */ +{ + parser_scope_stack *scope_stack_p = context_p->scope_stack_p; + parser_scope_stack *scope_stack_end_p = scope_stack_p + context_p->scope_stack_top; + int32_t scope_literal_index = -1; + + while (scope_stack_p < scope_stack_end_p) + { + scope_stack_end_p--; + + if (literal_index == scope_stack_end_p->map_to) + { + scope_literal_index = scope_stack_end_p->map_from; + } + } + + if (literal_index < PARSER_REGISTER_START) + { + JERRY_DEBUG_MSG (scope_literal_index != -1 ? " IDX:%d->" : " idx:%d->", literal_index); + lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); + util_print_literal (literal_p); + return; + } + + if (scope_literal_index == -1) + { + JERRY_DEBUG_MSG (" reg:%d", (int) (literal_index - PARSER_REGISTER_START)); + return; + } + + JERRY_DEBUG_MSG (" REG:%d->", (int) (literal_index - PARSER_REGISTER_START)); + + lexer_literal_t *literal_p = PARSER_GET_LITERAL (scope_stack_end_p->map_from); + util_print_literal (literal_p); +} /* parser_print_literal */ + +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + /** * Append the current byte code to the stream */ @@ -181,26 +226,16 @@ parser_flush_cbc (parser_context_t *context_p) /**< context */ if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { - uint16_t literal_index = context_p->last_cbc.literal_index; - lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); - JERRY_DEBUG_MSG (" idx:%d->", literal_index); - util_print_literal (literal_p); + parser_print_literal (context_p, context_p->last_cbc.literal_index); } if (flags & CBC_HAS_LITERAL_ARG2) { - uint16_t literal_index = context_p->last_cbc.value; - lexer_literal_t *literal_p = PARSER_GET_LITERAL (literal_index); - JERRY_DEBUG_MSG (" idx:%d->", literal_index); - util_print_literal (literal_p); + parser_print_literal (context_p, context_p->last_cbc.value); if (!(flags & CBC_HAS_LITERAL_ARG)) { - literal_index = context_p->last_cbc.third_literal_index; - - literal_p = PARSER_GET_LITERAL (literal_index); - JERRY_DEBUG_MSG (" idx:%d->", literal_index); - util_print_literal (literal_p); + parser_print_literal (context_p, context_p->last_cbc.third_literal_index); } } @@ -276,6 +311,29 @@ parser_emit_cbc_literal (parser_context_t *context_p, /**< context */ context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; } /* parser_emit_cbc_literal */ +/** + * Append a byte code with a literal and value argument + */ +void +parser_emit_cbc_literal_value (parser_context_t *context_p, /**< context */ + uint16_t opcode, /**< opcode */ + uint16_t literal_index, /**< literal index */ + uint16_t value) /**< value */ +{ + JERRY_ASSERT (PARSER_ARGS_EQ (opcode, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); + + if (context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE) + { + parser_flush_cbc (context_p); + } + + context_p->last_cbc_opcode = opcode; + context_p->last_cbc.literal_index = literal_index; + context_p->last_cbc.literal_type = LEXER_UNUSED_LITERAL; + context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; + context_p->last_cbc.value = value; +} /* parser_emit_cbc_literal_value */ + /** * Append a byte code with the current literal argument */ @@ -754,6 +812,10 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Maximum number of literals reached."; } + case PARSER_ERR_SCOPE_STACK_LIMIT_REACHED: + { + return "Maximum depth of scope stack reached."; + } case PARSER_ERR_ARGUMENT_LIMIT_REACHED: { return "Maximum number of function arguments reached."; @@ -762,10 +824,6 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Maximum function stack size reached."; } - case PARSER_ERR_REGISTER_LIMIT_REACHED: - { - return "Maximum number of registers is reached."; - } case PARSER_ERR_INVALID_CHARACTER: { return "Invalid (unexpected) character."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 55ce06f45..e93eb6c11 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -41,122 +41,23 @@ JERRY_STATIC_ASSERT ((ECMA_PARSE_CLASS_CONSTRUCTOR << PARSER_CLASS_PARSE_OPTS_OF * @{ */ -/** - * Copy identifiers if needed. - */ -static void -parser_copy_identifiers (parser_context_t *context_p) /**< context */ -{ - parser_saved_context_t *parent_p = context_p->last_context_p; - - if (parent_p == NULL || !(parent_p->status_flags & PARSER_IS_FUNCTION)) - { - /* Return if this function is not a nested function. */ - return; - } - - if (context_p->status_flags & PARSER_NO_REG_STORE) - { - /* This flag must affect all parent functions. */ - parent_p->status_flags |= PARSER_NO_REG_STORE; - return; - } - - parser_list_iterator_t literal_iterator; - lexer_literal_t *literal_p; - - parser_list_t parent_literal_pool; - - /* Accessing the parent literal pool requires all data. */ - parent_literal_pool.data = parent_p->literal_pool_data; - parent_literal_pool.page_size = context_p->literal_pool.page_size; - parent_literal_pool.item_size = context_p->literal_pool.item_size; - parent_literal_pool.item_count = context_p->literal_pool.item_count; - - parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); - while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) - { - if (literal_p->type != LEXER_IDENT_LITERAL || (literal_p->status_flags & LEXER_FLAG_VAR)) - { - continue; - } - - parser_list_iterator_t parent_literal_iterator; - parser_list_iterator_init (&parent_literal_pool, &parent_literal_iterator); - - lexer_literal_t *parent_literal_p; - const uint8_t *char_p = literal_p->u.char_p; - size_t length = literal_p->prop.length; - - while ((parent_literal_p = (lexer_literal_t *) parser_list_iterator_next (&parent_literal_iterator)) != NULL) - { - if (parent_literal_p->type == LEXER_IDENT_LITERAL - && parent_literal_p->prop.length == length - && memcmp (parent_literal_p->u.char_p, char_p, length) == 0) - { - /* This literal is known by the parent. */ - parent_literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - break; - } - } - - if (parent_literal_p != NULL) - { - continue; - } - - if (parent_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - - parent_literal_p = (lexer_literal_t *) parser_list_append (context_p, &parent_literal_pool); - - /* The literal data is updated at every iteration to handle out-of memory. */ - parent_p->literal_pool_data = parent_literal_pool.data; - - parent_literal_p->prop.length = (prop_length_t) length; - parent_literal_p->type = LEXER_IDENT_LITERAL; - parent_literal_p->status_flags = (uint8_t) (literal_p->status_flags & LEXER_FLAG_SOURCE_PTR); - parent_literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_UNUSED_IDENT; - parent_literal_p->u.char_p = char_p; - - /* The buffer ownership is passed to the parent by - * setting this flag which prevents freeing the buffer. */ - literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; - - parent_p->literal_count++; - } -} /* parser_copy_identifiers */ - /** * Compute real literal indicies. * * @return length of the prefix opcodes */ -static size_t +static void parser_compute_indicies (parser_context_t *context_p, /**< context */ uint16_t *ident_end, /**< end of the identifier group */ - uint16_t *uninitialized_var_end, /**< end of the uninitialized var group */ - uint16_t *initialized_var_end, /**< end of the initialized var group */ uint16_t *const_literal_end) /**< end of the const literal group */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; - size_t length = 0; - uint16_t literal_one_byte_limit; uint32_t status_flags = context_p->status_flags; - uint16_t argument_count; - uint16_t register_count = context_p->register_count; - uint16_t uninitialized_var_count = 0; - uint16_t initialized_var_count = 0; uint16_t ident_count = 0; uint16_t const_literal_count = 0; - uint16_t register_index; - uint16_t uninitialized_var_index; - uint16_t initialized_var_index; uint16_t ident_index; uint16_t const_literal_index; uint16_t literal_index; @@ -171,478 +72,172 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { - if (literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT) - { -#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) - { - jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); - } -#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - - context_p->literal_count--; - continue; - } - -#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - if (literal_p->type == LEXER_IDENT_LITERAL - || literal_p->type == LEXER_STRING_LITERAL) - { - const uint8_t *char_p = literal_p->u.char_p; - - if ((literal_p->status_flags & LEXER_FLAG_SOURCE_PTR) - && literal_p->prop.length < 0xfff) - { - size_t bytes_to_end = (size_t) (context_p->source_end_p - char_p); - - if (bytes_to_end < 0xfffff) - { - literal_p->u.source_data = ((uint32_t) bytes_to_end) | (((uint32_t) literal_p->prop.length) << 20); - literal_p->status_flags |= LEXER_FLAG_LATE_INIT; - status_flags |= PARSER_HAS_LATE_LIT_INIT; - context_p->status_flags = status_flags; - char_p = NULL; - } - } - - if (char_p != NULL) - { - literal_p->u.value = ecma_find_or_create_literal_string (char_p, - literal_p->prop.length); - - if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) - { - jmem_heap_free_block ((void *) char_p, literal_p->prop.length); - /* This literal should not be freed even if an error is encountered later. */ - literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; - } - } - } -#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - switch (literal_p->type) { case LEXER_IDENT_LITERAL: { - if (literal_p->status_flags & LEXER_FLAG_VAR) - { - if (status_flags & (PARSER_NO_REG_STORE | PARSER_ARGUMENTS_NEEDED)) - { - literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - } - - if (literal_p->status_flags & LEXER_FLAG_INITIALIZED) - { - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - if ((status_flags & PARSER_ARGUMENTS_NEEDED) - && !(status_flags & PARSER_IS_STRICT)) - { - literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - } - - /* Arguments are bound to their position, or - * moved to the initialized var section. */ - if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) - { - initialized_var_count++; - context_p->literal_count++; - } - } - else if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) - && register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) - { - register_count++; - } - else - { - literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - initialized_var_count++; - } - - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - } - else if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) - && register_count < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) - { - register_count++; - } - else - { - literal_p->status_flags |= LEXER_FLAG_NO_REG_STORE; - uninitialized_var_count++; - } - - if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) - { - status_flags |= PARSER_LEXICAL_ENV_NEEDED; - context_p->status_flags = status_flags; - } - } - else + if (literal_p->status_flags & LEXER_FLAG_USED) { ident_count++; + break; + } +#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + else if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); + /* This literal should not be freed even if an error is encountered later. */ + literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; + } +#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + continue; + } + case LEXER_STRING_LITERAL: + { + const_literal_count++; + break; + } + case LEXER_NUMBER_LITERAL: + { + const_literal_count++; + continue; + } + case LEXER_FUNCTION_LITERAL: + case LEXER_REGEXP_LITERAL: + { + continue; + } + default: + { + JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL); + continue; + } + } + +#if !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + const uint8_t *char_p = literal_p->u.char_p; + + if ((literal_p->status_flags & LEXER_FLAG_SOURCE_PTR) + && literal_p->prop.length < 0xfff) + { + size_t bytes_to_end = (size_t) (context_p->source_end_p - char_p); + + if (bytes_to_end < 0xfffff) + { + literal_p->u.source_data = ((uint32_t) bytes_to_end) | (((uint32_t) literal_p->prop.length) << 20); + literal_p->status_flags |= LEXER_FLAG_LATE_INIT; + status_flags |= PARSER_HAS_LATE_LIT_INIT; + context_p->status_flags = status_flags; + char_p = NULL; + } + } + + if (char_p != NULL) + { + literal_p->u.value = ecma_find_or_create_literal_string (char_p, + literal_p->prop.length); + + if (!(literal_p->status_flags & LEXER_FLAG_SOURCE_PTR)) + { + jmem_heap_free_block ((void *) char_p, literal_p->prop.length); + /* This literal should not be freed even if an error is encountered later. */ + literal_p->status_flags |= LEXER_FLAG_SOURCE_PTR; + } + } +#endif /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + } + + ident_index = context_p->register_count; + const_literal_index = (uint16_t) (ident_index + ident_count); + literal_index = (uint16_t) (const_literal_index + const_literal_count); + + /* Second phase: Assign an index to each literal. */ + parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); + + while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) + { + switch (literal_p->type) + { + case LEXER_IDENT_LITERAL: + { + if (literal_p->status_flags & LEXER_FLAG_USED) + { + literal_p->prop.index = ident_index; + ident_index++; } break; } case LEXER_STRING_LITERAL: case LEXER_NUMBER_LITERAL: - { - const_literal_count++; - break; - } - case LEXER_UNUSED_LITERAL: - { - if (!(literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) - { - context_p->literal_count--; - } - break; - } - } - } - - if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) - { - literal_one_byte_limit = CBC_MAXIMUM_BYTE_VALUE - 1; - } - else - { - literal_one_byte_limit = CBC_LOWER_SEVEN_BIT_MASK; - } - - if (uninitialized_var_count > 0) - { - /* Opcode byte and a literal argument. */ - length += 2; - if ((register_count + uninitialized_var_count - 1) > literal_one_byte_limit) - { - length++; - } - } - - register_index = context_p->register_count; - uninitialized_var_index = register_count; - initialized_var_index = (uint16_t) (uninitialized_var_index + uninitialized_var_count); - ident_index = (uint16_t) (initialized_var_index + initialized_var_count); - const_literal_index = (uint16_t) (ident_index + ident_count); - literal_index = (uint16_t) (const_literal_index + const_literal_count); - - if (initialized_var_count > 2) - { - status_flags |= PARSER_HAS_INITIALIZED_VARS; - context_p->status_flags = status_flags; - - /* Opcode byte and two literal arguments. */ - length += 3; - if (initialized_var_index > literal_one_byte_limit) - { - length++; - } - if (ident_index - 1 > literal_one_byte_limit) - { - length++; - } - } - - /* Second phase: Assign an index to each literal. */ - parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); - argument_count = 0; - - while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) - { - uint16_t init_index; - - if (literal_p->type != LEXER_IDENT_LITERAL) - { - if (literal_p->type == LEXER_STRING_LITERAL - || literal_p->type == LEXER_NUMBER_LITERAL) { JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_SOURCE_PTR | LEXER_FLAG_LATE_INIT)) == 0); literal_p->prop.index = const_literal_index; const_literal_index++; - continue; + break; } - - if (literal_p->type != LEXER_UNUSED_LITERAL) + case LEXER_FUNCTION_LITERAL: + case LEXER_REGEXP_LITERAL: { JERRY_ASSERT (literal_p->status_flags == 0); - JERRY_ASSERT (literal_p->type == LEXER_FUNCTION_LITERAL - || literal_p->type == LEXER_REGEXP_LITERAL); literal_p->prop.index = literal_index; literal_index++; - continue; + break; } - - JERRY_ASSERT ((literal_p->status_flags & ~(LEXER_FLAG_FUNCTION_ARGUMENT | LEXER_FLAG_SOURCE_PTR)) == 0); - - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) + default: { - argument_count++; + JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL + && literal_p->status_flags == LEXER_FLAG_FUNCTION_ARGUMENT); + break; } - continue; - } - - if (literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT) - { - continue; - } - - if (!(literal_p->status_flags & LEXER_FLAG_VAR)) - { - literal_p->prop.index = ident_index; - ident_index++; - continue; - } - - if (!(literal_p->status_flags & LEXER_FLAG_INITIALIZED)) - { - if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) - { - JERRY_ASSERT (register_count <= PARSER_MAXIMUM_NUMBER_OF_REGISTERS); - /* This var literal can be stored in a register. */ - literal_p->prop.index = register_index; - register_index++; - } - else - { - literal_p->prop.index = uninitialized_var_index; - uninitialized_var_index++; - } - continue; - } - - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - if (literal_p->status_flags & LEXER_FLAG_NO_REG_STORE) - { - literal_p->prop.index = initialized_var_index; - initialized_var_index++; - init_index = argument_count; - argument_count++; - } - else - { - literal_p->prop.index = argument_count; - argument_count++; - continue; - } - } - else - { - if (!(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) - { - JERRY_ASSERT (register_count <= PARSER_MAXIMUM_NUMBER_OF_REGISTERS); - /* This var literal can be stored in a register. */ - literal_p->prop.index = register_index; - register_index++; - } - else - { - literal_p->prop.index = initialized_var_index; - initialized_var_index++; - } - - init_index = literal_index; - literal_index++; - - lexer_literal_t *func_literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); - - JERRY_ASSERT (func_literal_p != NULL - && func_literal_p->type == LEXER_FUNCTION_LITERAL); - func_literal_p->prop.index = init_index; - } - - /* A CBC_INITIALIZE_VAR instruction or part of a CBC_INITIALIZE_VARS instruction. */ - if (!(status_flags & PARSER_HAS_INITIALIZED_VARS) - || !(literal_p->status_flags & LEXER_FLAG_NO_REG_STORE)) - { - length += 2; - if (literal_p->prop.index > literal_one_byte_limit) - { - length++; - } - } - - length++; - if (init_index > literal_one_byte_limit) - { - length++; } } - JERRY_ASSERT (argument_count == context_p->argument_count); - JERRY_ASSERT (register_index == register_count); - JERRY_ASSERT (uninitialized_var_index == register_count + uninitialized_var_count); - JERRY_ASSERT (initialized_var_index == uninitialized_var_index + initialized_var_count); - JERRY_ASSERT (ident_index == initialized_var_index + ident_count); + JERRY_ASSERT (ident_index == context_p->register_count + ident_count); JERRY_ASSERT (const_literal_index == ident_index + const_literal_count); - JERRY_ASSERT (literal_index == context_p->literal_count); + JERRY_ASSERT (literal_index <= context_p->register_count + context_p->literal_count); + + context_p->literal_count = literal_index; *ident_end = ident_index; - *uninitialized_var_end = uninitialized_var_index; - *initialized_var_end = initialized_var_index; *const_literal_end = const_literal_index; - context_p->register_count = register_index; - - return length; } /* parser_compute_indicies */ /** - * Encode a literal argument. - * - * @return position after the encoded values + * Initialize literal pool. */ -static inline uint8_t * -parser_encode_literal (uint8_t *dst_p, /**< destination buffer */ - uint16_t literal_index, /**< literal index */ - uint16_t literal_one_byte_limit) /**< maximum value of a literal - * encoded in one byte */ -{ - if (literal_index <= literal_one_byte_limit) - { - *dst_p++ = (uint8_t) (literal_index); - } - else - { - if (literal_one_byte_limit == CBC_MAXIMUM_BYTE_VALUE - 1) - { - *dst_p++ = (uint8_t) (CBC_MAXIMUM_BYTE_VALUE); - *dst_p++ = (uint8_t) (literal_index - CBC_MAXIMUM_BYTE_VALUE); - } - else - { - *dst_p++ = (uint8_t) ((literal_index >> 8) | CBC_HIGHEST_BIT_MASK); - *dst_p++ = (uint8_t) (literal_index & CBC_MAXIMUM_BYTE_VALUE); - } - } - return dst_p; -} /* parser_encode_literal */ - -/** - * Generate initializer byte codes. - * - * @return the end of the initializer stream - */ -static uint8_t * -parser_generate_initializers (parser_context_t *context_p, /**< context */ - uint8_t *dst_p, /**< destination buffer */ - ecma_value_t *literal_pool_p, /**< start of literal pool */ - uint16_t uninitialized_var_end, /**< end of the uninitialized var group */ - uint16_t initialized_var_end, /**< end of the initialized var group */ - uint16_t literal_one_byte_limit) /**< maximum value of a literal - * encoded in one byte */ +static void +parser_init_literal_pool (parser_context_t *context_p, /**< context */ + ecma_value_t *literal_pool_p) /**< start of literal pool */ { parser_list_iterator_t literal_iterator; lexer_literal_t *literal_p; - uint16_t argument_count, register_count; - - if (uninitialized_var_end > context_p->register_count) - { - *dst_p++ = CBC_DEFINE_VARS; - dst_p = parser_encode_literal (dst_p, - (uint16_t) (uninitialized_var_end - 1), - literal_one_byte_limit); - } - - if (context_p->status_flags & PARSER_HAS_INITIALIZED_VARS) - { - const uint8_t expected_status_flags = LEXER_FLAG_VAR | LEXER_FLAG_NO_REG_STORE | LEXER_FLAG_INITIALIZED; -#ifndef JERRY_NDEBUG - uint16_t next_index = uninitialized_var_end; -#endif /* !JERRY_NDEBUG */ - - *dst_p++ = CBC_INITIALIZE_VARS; - dst_p = parser_encode_literal (dst_p, - (uint16_t) uninitialized_var_end, - literal_one_byte_limit); - dst_p = parser_encode_literal (dst_p, - (uint16_t) (initialized_var_end - 1), - literal_one_byte_limit); - - parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); - argument_count = 0; - - while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) - { - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - argument_count++; - } - - if ((literal_p->status_flags & expected_status_flags) == expected_status_flags) - { - uint16_t init_index; - - JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); -#ifndef JERRY_NDEBUG - JERRY_ASSERT (literal_p->prop.index == next_index); - next_index++; -#endif /* !JERRY_NDEBUG */ - literal_p->status_flags = (uint8_t) (literal_p->status_flags & ~LEXER_FLAG_INITIALIZED); - - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - init_index = (uint16_t) (argument_count - 1); - } - else - { - literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); - - JERRY_ASSERT (literal_p != NULL - && literal_p->type == LEXER_FUNCTION_LITERAL); - init_index = literal_p->prop.index; - } - - dst_p = parser_encode_literal (dst_p, init_index, literal_one_byte_limit); - } - } - -#if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) - if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) - { - JERRY_ASSERT ((argument_count - 1) == context_p->argument_count); - } - else - { -#endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ - JERRY_ASSERT (argument_count == context_p->argument_count); -#if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) - } -#endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ - } parser_list_iterator_init (&context_p->literal_pool, &literal_iterator); - argument_count = 0; - register_count = context_p->register_count; while ((literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator))) { - const uint8_t expected_status_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED; - - if (literal_p->type != LEXER_UNUSED_LITERAL) + switch (literal_p->type) { - if (literal_p->type == LEXER_IDENT_LITERAL - || literal_p->type == LEXER_STRING_LITERAL) + case LEXER_IDENT_LITERAL: { - if (literal_p->prop.index >= register_count) + if (!(literal_p->status_flags & LEXER_FLAG_USED)) { - if (!(literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT)) - { - ecma_value_t lit_value; -#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) - lit_value = ecma_find_or_create_literal_string (literal_p->u.char_p, - literal_p->prop.length); -#else /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - lit_value = literal_p->u.value; -#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ - literal_pool_p[literal_p->prop.index] = lit_value; - } + break; } + /* FALLTHRU */ + } + case LEXER_STRING_LITERAL: + { + ecma_value_t lit_value; +#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) + lit_value = ecma_find_or_create_literal_string (literal_p->u.char_p, + literal_p->prop.length); +#else /* !ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + lit_value = literal_p->u.value; +#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + + JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); + literal_pool_p[literal_p->prop.index] = lit_value; #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) if (!context_p->is_show_opcodes @@ -651,76 +246,32 @@ parser_generate_initializers (parser_context_t *context_p, /**< context */ jmem_heap_free_block ((void *) literal_p->u.char_p, literal_p->prop.length); } #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ + break; } - else if ((literal_p->type == LEXER_FUNCTION_LITERAL) - || (literal_p->type == LEXER_REGEXP_LITERAL)) + case LEXER_NUMBER_LITERAL: { - JERRY_ASSERT (literal_p->prop.index >= register_count); + JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); + + literal_pool_p[literal_p->prop.index] = literal_p->u.value; + break; + } + case LEXER_FUNCTION_LITERAL: + case LEXER_REGEXP_LITERAL: + { + JERRY_ASSERT (literal_p->prop.index >= context_p->register_count); ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[literal_p->prop.index], literal_p->u.bytecode_p); + break; } - else + default: { - JERRY_ASSERT (literal_p->type == LEXER_NUMBER_LITERAL - && literal_p->prop.index >= register_count); - - literal_pool_p[literal_p->prop.index] = literal_p->u.value; + JERRY_ASSERT (literal_p->type == LEXER_UNUSED_LITERAL); + break; } } - - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - argument_count++; - } - - if ((literal_p->status_flags & expected_status_flags) == expected_status_flags) - { - uint16_t index = literal_p->prop.index; - uint16_t init_index; - - JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); - - if (literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) - { - init_index = (uint16_t) (argument_count - 1); - - if (init_index == literal_p->prop.index) - { - continue; - } - } - else - { - literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); - - JERRY_ASSERT (literal_p != NULL - && literal_p->type == LEXER_FUNCTION_LITERAL); - - init_index = literal_p->prop.index; - JERRY_ASSERT (init_index >= register_count); - - ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[init_index], - literal_p->u.bytecode_p); - } - - *dst_p++ = CBC_INITIALIZE_VAR; - dst_p = parser_encode_literal (dst_p, index, literal_one_byte_limit); - dst_p = parser_encode_literal (dst_p, init_index, literal_one_byte_limit); - } } - -#if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) - if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) - { - JERRY_ASSERT ((argument_count - 1) == context_p->argument_count); - return dst_p; - } -#endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ - - JERRY_ASSERT (argument_count == context_p->argument_count); - return dst_p; -} /* parser_generate_initializers */ +} /* parser_init_literal_pool */ /* * During byte code post processing certain bytes are not @@ -973,7 +524,6 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * uint16_t argument_end; uint16_t register_end; uint16_t ident_end; - uint16_t const_literal_end; if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { @@ -981,7 +531,6 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * argument_end = args_p->argument_end; register_end = args_p->register_end; ident_end = args_p->ident_end; - const_literal_end = args_p->const_literal_end; } else { @@ -989,7 +538,6 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * argument_end = args_p->argument_end; register_end = args_p->register_end; ident_end = args_p->ident_end; - const_literal_end = args_p->const_literal_end; } #if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) @@ -999,46 +547,35 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * } #endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ + if (literal_index < argument_end) + { + JERRY_DEBUG_MSG (" arg:%d", literal_index); + return; + } + + if (literal_index < register_end) + { + JERRY_DEBUG_MSG (" reg:%d", literal_index); + return; + } + parser_list_iterator_init (literal_pool_p, &literal_iterator); while (true) { lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_iterator_next (&literal_iterator); - if (literal_p == NULL) + JERRY_ASSERT (literal_p != NULL); + + if (literal_p->prop.index == literal_index) { - if (literal_index == const_literal_end) + if (literal_index < ident_end) { - JERRY_DEBUG_MSG (" idx:%d(self)->function", literal_index); - break; - } - - JERRY_ASSERT (literal_index < argument_end); - JERRY_DEBUG_MSG (" idx:%d(arg)->undefined", literal_index); - break; - } - - if (literal_p->prop.index == literal_index - && literal_p->type != LEXER_UNUSED_LITERAL - && !(literal_p->status_flags & LEXER_FLAG_UNUSED_IDENT)) - { - JERRY_DEBUG_MSG (" idx:%d", literal_index); - - if (literal_index < argument_end) - { - JERRY_DEBUG_MSG ("(arg)->"); - } - else if (literal_index < register_end) - { - JERRY_DEBUG_MSG ("(reg)->"); - } - else if (literal_index < ident_end) - { - JERRY_DEBUG_MSG ("(ident)->"); + JERRY_DEBUG_MSG (" ident:%d->", literal_index); } else { - JERRY_DEBUG_MSG ("(lit)->"); + JERRY_DEBUG_MSG (" lit:%d->", literal_index); } util_print_literal (literal_p); @@ -1055,85 +592,6 @@ parse_print_literal (ecma_compiled_code_t *compiled_code_p, /**< compiled code * byte_code_p++; \ } -/** - * Print CBC_DEFINE_VARS instruction. - * - * @return next byte code position - */ -static uint8_t * -parse_print_define_vars (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ - uint8_t *byte_code_p, /**< byte code position */ - uint16_t encoding_limit, /**< literal encoding limit */ - uint16_t encoding_delta, /**< literal encoding delta */ - parser_list_t *literal_pool_p) /**< literal pool */ -{ - uint16_t identifier_index; - uint16_t identifier_end; - - if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) - { - cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) compiled_code_p; - identifier_index = args_p->register_end; - } - else - { - cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) compiled_code_p; - identifier_index = args_p->register_end; - } - - PARSER_READ_IDENTIFIER_INDEX (identifier_end); - - JERRY_DEBUG_MSG (" from: %d to: %d\n", identifier_index, identifier_end); - - while (identifier_index <= identifier_end) - { - JERRY_DEBUG_MSG (" "); - parse_print_literal (compiled_code_p, identifier_index, literal_pool_p); - identifier_index++; - JERRY_DEBUG_MSG ("\n"); - } - - return byte_code_p; -} /* parse_print_define_vars */ - -/** - * Print CBC_INITIALIZE_VARS instruction. - * - * @return next byte code position - */ -static uint8_t * -parse_print_initialize_vars (ecma_compiled_code_t *compiled_code_p, /**< compiled code */ - uint8_t *byte_code_p, /**< byte code position */ - uint16_t encoding_limit, /**< literal encoding limit */ - uint16_t encoding_delta, /**< literal encoding delta */ - parser_list_t *literal_pool_p) /**< literal pool */ -{ - uint16_t identifier_index; - uint16_t identifier_end; - - PARSER_READ_IDENTIFIER_INDEX (identifier_index); - PARSER_READ_IDENTIFIER_INDEX (identifier_end); - - JERRY_DEBUG_MSG (" from: %d to: %d\n", identifier_index, identifier_end); - - while (identifier_index <= identifier_end) - { - uint16_t literal_index; - - JERRY_DEBUG_MSG (" "); - parse_print_literal (compiled_code_p, identifier_index, literal_pool_p); - JERRY_DEBUG_MSG (" ="); - - PARSER_READ_IDENTIFIER_INDEX (literal_index); - - parse_print_literal (compiled_code_p, literal_index, literal_pool_p); - identifier_index++; - JERRY_DEBUG_MSG ("\n"); - } - - return byte_code_p; -} /* parse_print_initialize_vars */ - /** * Print byte code. */ @@ -1261,26 +719,6 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code flags = cbc_flags[opcode]; JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_names[opcode]); byte_code_p++; - - if (opcode == CBC_INITIALIZE_VARS) - { - byte_code_p = parse_print_initialize_vars (compiled_code_p, - byte_code_p, - encoding_limit, - encoding_delta, - literal_pool_p); - continue; - } - - if (opcode == CBC_DEFINE_VARS) - { - byte_code_p = parse_print_define_vars (compiled_code_p, - byte_code_p, - encoding_limit, - encoding_delta, - literal_pool_p); - continue; - } } else { @@ -1459,8 +897,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */ { uint16_t literal_one_byte_limit; uint16_t ident_end; - uint16_t uninitialized_var_end; - uint16_t initialized_var_end; uint16_t const_literal_end; parser_mem_page_t *page_p; parser_mem_page_t *last_page_p; @@ -1472,7 +908,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */ #if ENABLED (JERRY_SNAPSHOT_SAVE) size_t total_size_used; #endif /* ENABLED (JERRY_SNAPSHOT_SAVE) */ - size_t initializers_length; uint8_t real_offset; uint8_t *byte_code_p; bool needs_uint16_arguments; @@ -1509,14 +944,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_DEBUGGER) */ - parser_copy_identifiers (context_p); - - initializers_length = parser_compute_indicies (context_p, - &ident_end, - &uninitialized_var_end, - &initialized_var_end, - &const_literal_end); - length = initializers_length; + parser_compute_indicies (context_p, &ident_end, &const_literal_end); if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) { @@ -1538,6 +966,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ page_p = context_p->byte_code.first_p; offset = 0; + length = 0; while (page_p != last_page_p || offset < last_position) { @@ -1590,43 +1019,40 @@ parser_post_processing (parser_context_t *context_p) /**< context */ while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)) { uint8_t *first_byte = page_p->bytes + offset; - size_t literal_index = *first_byte; - lexer_literal_t *literal_p; + uint32_t literal_index = *first_byte; PARSER_NEXT_BYTE (page_p, offset); length++; - literal_index |= ((size_t) page_p->bytes[offset]) << 8; - literal_p = PARSER_GET_LITERAL (literal_index); + literal_index |= ((uint32_t) page_p->bytes[offset]) << 8; - if (literal_p->type == LEXER_UNUSED_LITERAL) + if (literal_index >= PARSER_REGISTER_START) { - /* In a few cases uninitialized literals may have been converted to initialized - * literals later. Byte code references to the old (uninitialized) literals - * must be redirected to the new instance of the literal. */ - literal_p = PARSER_GET_LITERAL (literal_p->prop.index); - - JERRY_ASSERT (literal_p != NULL && literal_p->type != LEXER_UNUSED_LITERAL); + literal_index -= PARSER_REGISTER_START; + } + else + { + literal_index = (PARSER_GET_LITERAL (literal_index))->prop.index; } - if (literal_p->prop.index <= literal_one_byte_limit) + if (literal_index <= literal_one_byte_limit) { - *first_byte = (uint8_t) literal_p->prop.index; + *first_byte = (uint8_t) literal_index; } else { if (context_p->literal_count <= CBC_MAXIMUM_SMALL_VALUE) { - JERRY_ASSERT (literal_p->prop.index <= CBC_MAXIMUM_SMALL_VALUE); + JERRY_ASSERT (literal_index <= CBC_MAXIMUM_SMALL_VALUE); *first_byte = CBC_MAXIMUM_BYTE_VALUE; - page_p->bytes[offset] = (uint8_t) (literal_p->prop.index - CBC_MAXIMUM_BYTE_VALUE); + page_p->bytes[offset] = (uint8_t) (literal_index - CBC_MAXIMUM_BYTE_VALUE); length++; } else { - JERRY_ASSERT (literal_p->prop.index <= CBC_MAXIMUM_FULL_VALUE); - *first_byte = (uint8_t) ((literal_p->prop.index >> 8) | CBC_HIGHEST_BIT_MASK); - page_p->bytes[offset] = (uint8_t) (literal_p->prop.index & 0xff); + JERRY_ASSERT (literal_index <= CBC_MAXIMUM_FULL_VALUE); + *first_byte = (uint8_t) ((literal_index >> 8) | CBC_HIGHEST_BIT_MASK); + page_p->bytes[offset] = (uint8_t) (literal_index & 0xff); length++; } } @@ -1857,18 +1283,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */ } #endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ - literal_pool_p = (ecma_value_t *) byte_code_p; - literal_pool_p -= context_p->register_count; + literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count; byte_code_p += literal_length; + dst_p = byte_code_p; - dst_p = parser_generate_initializers (context_p, - byte_code_p, - literal_pool_p, - uninitialized_var_end, - initialized_var_end, - literal_one_byte_limit); - - JERRY_ASSERT (dst_p == byte_code_p + initializers_length); + parser_init_literal_pool (context_p, literal_pool_p); page_p = context_p->byte_code.first_p; offset = 0; @@ -2059,8 +1478,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ } JERRY_ASSERT (dst_p == byte_code_p + length); - parse_update_branches (context_p, - byte_code_p + initializers_length); + parse_update_branches (context_p, byte_code_p); parser_cbc_stream_free (&context_p->byte_code); @@ -2132,20 +1550,12 @@ parser_post_processing (parser_context_t *context_p) /**< context */ /* All arguments must be moved to initialized registers. */ if (literal_p->type == LEXER_UNUSED_LITERAL) { - if (literal_p->u.char_p == NULL) - { - argument_base_p[argument_count] = ECMA_VALUE_EMPTY; - argument_count++; - continue; - } - - literal_p = PARSER_GET_LITERAL (literal_p->prop.index); - - JERRY_ASSERT (literal_p != NULL); + argument_base_p[argument_count] = ECMA_VALUE_EMPTY; + argument_count++; + continue; } - JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL - && (literal_p->status_flags & LEXER_FLAG_VAR)); + JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL); JERRY_ASSERT (literal_p->prop.index >= register_count); @@ -2207,11 +1617,16 @@ static void parser_parse_function_arguments (parser_context_t *context_p, /**< context */ lexer_token_type_t end_type) /**< expected end type */ { -#if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) +#if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) || ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) bool duplicated_argument_names = false; +#endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) || ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ +#if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) bool initializer_found = false; #endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */ + JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); + scanner_create_variables (context_p, sizeof (scanner_function_info_t)); + if (context_p->token.type == end_type) { return; @@ -2219,8 +1634,6 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ while (true) { - uint16_t literal_count = context_p->literal_count; - #if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { @@ -2230,7 +1643,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ { lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL); - if (context_p->literal_count == literal_count) + if (duplicated_argument_names) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } @@ -2249,71 +1662,35 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ &context_p->token.lit_location, LEXER_IDENT_LITERAL); - if (literal_count == context_p->literal_count - || context_p->token.literal_is_reserved + if (context_p->token.literal_is_reserved || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) { context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; } - if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) + if (JERRY_UNLIKELY (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) { - uint8_t literal_status_flags = context_p->lit_object.literal_p->status_flags; - - literal_status_flags = (uint8_t) (literal_status_flags & ~LEXER_FLAG_NO_REG_STORE); - context_p->lit_object.literal_p->status_flags = literal_status_flags; - - context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; - context_p->status_flags &= (uint32_t) ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); - } - - if (context_p->literal_count == literal_count) - { - lexer_literal_t *literal_p; - #if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) - if (initializer_found && (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT)) + if (initializer_found) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES); } +#endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */ +#if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) || ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) duplicated_argument_names = true; -#endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */ +#endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) || ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - *literal_p = *context_p->lit_object.literal_p; - - literal_p->status_flags &= LEXER_FLAG_SOURCE_PTR; - literal_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; - - context_p->literal_count++; - - /* There cannot be references from the byte code to these literals - * since no byte code has been emitted yet. Therefore there is no - * need to set the index field. */ - context_p->lit_object.literal_p->type = LEXER_UNUSED_LITERAL; -#if ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) - context_p->lit_object.literal_p->prop.index = context_p->literal_count; -#endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */ - - /* Only the LEXER_FLAG_FUNCTION_ARGUMENT flag is kept. */ - context_p->lit_object.literal_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT; - context_p->lit_object.literal_p->u.char_p = NULL; + context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; } else { - uint8_t lexer_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; - context_p->lit_object.literal_p->status_flags |= lexer_flags; + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } context_p->argument_count++; if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) { - parser_raise_error (context_p, PARSER_ERR_REGISTER_LIMIT_REACHED); + parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } lexer_next_token (context_p); @@ -2363,8 +1740,6 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, error); } - - context_p->register_count = context_p->argument_count; } /* parser_parse_function_arguments */ /** @@ -2386,7 +1761,6 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ ecma_compiled_code_t *compiled_code_p; context.error = PARSER_ERR_NO_ERROR; - context.allocated_buffer_p = NULL; if (error_location_p != NULL) { @@ -2395,19 +1769,11 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ if (arg_list_p == NULL) { - context.status_flags = PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NOT_NEEDED; + context.status_flags = PARSER_ARGUMENTS_NOT_NEEDED; } else { context.status_flags = PARSER_IS_FUNCTION; -#if ENABLED (JERRY_DEBUGGER) - if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - { - /* This option has a high memory and performance costs, - * but it is necessary for executing eval operations by the debugger. */ - context.status_flags |= PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; - } -#endif /* ENABLED (JERRY_DEBUGGER) */ } #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) @@ -2453,6 +1819,10 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ parser_list_init (&context.literal_pool, sizeof (lexer_literal_t), (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_literal_t))); + context.scope_stack_p = NULL; + context.scope_stack_size = 0; + context.scope_stack_top = 0; + context.scope_stack_reg_top = 0; #ifndef JERRY_NDEBUG context.context_stack_depth = 0; @@ -2500,6 +1870,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.source_end_p = arg_list_p + arg_list_size; } + context.u.allocated_buffer_p = NULL; context.line = 1; context.column = 1; @@ -2533,6 +1904,11 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ lexer_next_token (&context); } + else + { + JERRY_ASSERT (context.next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); + scanner_create_variables (&context, sizeof (scanner_function_info_t)); + } parser_parse_statements (&context); @@ -2546,7 +1922,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ JERRY_ASSERT (context.last_statement.current_p == NULL); JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE); - JERRY_ASSERT (context.allocated_buffer_p == NULL); + JERRY_ASSERT (context.u.allocated_buffer_p == NULL); compiled_code_p = parser_post_processing (&context); parser_list_free (&context.literal_pool); @@ -2572,9 +1948,9 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ parser_free_jumps (context.last_statement); } - if (context.allocated_buffer_p != NULL) + if (context.u.allocated_buffer_p != NULL) { - parser_free_local (context.allocated_buffer_p, + parser_free_local (context.u.allocated_buffer_p, context.allocated_buffer_size); } @@ -2600,6 +1976,11 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } PARSER_TRY_END + if (context.scope_stack_p != NULL) + { + parser_free (context.scope_stack_p, context.scope_stack_size * sizeof (parser_scope_stack)); + } + #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) if (context.is_show_opcodes) { @@ -2647,6 +2028,10 @@ parser_save_context (parser_context_t *context_p, /**< context */ saved_context_p->byte_code = context_p->byte_code; saved_context_p->byte_code_size = context_p->byte_code_size; saved_context_p->literal_pool_data = context_p->literal_pool.data; + saved_context_p->scope_stack_p = context_p->scope_stack_p; + 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; #ifndef JERRY_NDEBUG saved_context_p->context_stack_depth = context_p->context_stack_depth; @@ -2667,6 +2052,10 @@ parser_save_context (parser_context_t *context_p, /**< context */ parser_cbc_stream_init (&context_p->byte_code); context_p->byte_code_size = 0; parser_list_reset (&context_p->literal_pool); + context_p->scope_stack_p = NULL; + context_p->scope_stack_size = 0; + context_p->scope_stack_top = 0; + context_p->scope_stack_reg_top = 0; #ifndef JERRY_NDEBUG context_p->context_stack_depth = 0; @@ -2682,6 +2071,11 @@ parser_restore_context (parser_context_t *context_p, /**< context */ { parser_list_free (&context_p->literal_pool); + if (context_p->scope_stack_p != NULL) + { + parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack)); + } + /* Restore private part of the context. */ JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); @@ -2699,6 +2093,10 @@ parser_restore_context (parser_context_t *context_p, /**< context */ context_p->byte_code = saved_context_p->byte_code; context_p->byte_code_size = saved_context_p->byte_code_size; context_p->literal_pool.data = saved_context_p->literal_pool_data; + context_p->scope_stack_p = saved_context_p->scope_stack_p; + 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; #ifndef JERRY_NDEBUG context_p->context_stack_depth = saved_context_p->context_stack_depth; @@ -2735,12 +2133,9 @@ parser_parse_function (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ #if ENABLED (JERRY_DEBUGGER) - if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - && jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column)) + if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { - /* This option has a high memory and performance costs, - * but it is necessary for executing eval operations by the debugger. */ - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; + jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column); } #endif /* ENABLED (JERRY_DEBUGGER) */ @@ -2827,7 +2222,7 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ JERRY_ASSERT ((status_flags & PARSER_IS_FUNCTION) && (status_flags & PARSER_IS_ARROW_FUNCTION)); parser_save_context (context_p, &saved_context); - context_p->status_flags |= status_flags | PARSER_ARGUMENTS_NOT_NEEDED; + context_p->status_flags |= status_flags; #if ENABLED (JERRY_ES2015_CLASS) context_p->status_flags |= saved_context.status_flags & PARSER_CLASS_HAS_SUPER; #endif /* ENABLED (JERRY_ES2015_CLASS) */ @@ -2840,44 +2235,22 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ #if ENABLED (JERRY_DEBUGGER) - if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) - && jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column)) + if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { - /* This option has a high memory and performance costs, - * but it is necessary for executing eval operations by the debugger. */ - context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; + jerry_debugger_send_parse_function (context_p->token.line, context_p->token.column); } #endif /* ENABLED (JERRY_DEBUGGER) */ if (status_flags & PARSER_ARROW_PARSE_ARGS) { parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN); + lexer_next_token (context_p); } else { - JERRY_ASSERT (context_p->token.type == LEXER_LITERAL - && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); - - lexer_construct_literal_object (context_p, - &context_p->token.lit_location, - LEXER_IDENT_LITERAL); - - JERRY_ASSERT (context_p->argument_count == 0 && context_p->literal_count == 1); - - if (context_p->token.literal_is_reserved - || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) - { - context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; - } - - uint8_t lexer_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; - context_p->lit_object.literal_p->status_flags |= lexer_flags; - - context_p->argument_count = 1; - context_p->register_count = 1; + parser_parse_function_arguments (context_p, LEXER_ARROW); } - lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_ARROW); lexer_next_token (context_p); @@ -2953,6 +2326,13 @@ parser_raise_error (parser_context_t *context_p, /**< context */ parser_free_literals (&context_p->literal_pool); context_p->literal_pool.data = saved_context_p->literal_pool_data; + if (context_p->scope_stack_p != NULL) + { + parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack)); + } + context_p->scope_stack_p = saved_context_p->scope_stack_p; + context_p->scope_stack_size = saved_context_p->scope_stack_size; + if (saved_context_p->last_statement.current_p != NULL) { parser_free_jumps (saved_context_p->last_statement); diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 599abeddc..1bcac2d86 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -37,9 +37,9 @@ typedef enum PARSER_ERR_OUT_OF_MEMORY, /**< out of memory */ PARSER_ERR_LITERAL_LIMIT_REACHED, /**< maximum number of literals reached */ + PARSER_ERR_SCOPE_STACK_LIMIT_REACHED, /**< maximum depth of scope stack reached */ PARSER_ERR_ARGUMENT_LIMIT_REACHED, /**< maximum number of function arguments reached */ PARSER_ERR_STACK_LIMIT_REACHED, /**< maximum function stack size reached */ - PARSER_ERR_REGISTER_LIMIT_REACHED, /**< maximum register size reached */ PARSER_ERR_INVALID_CHARACTER, /**< unexpected character */ PARSER_ERR_INVALID_HEX_DIGIT, /**< invalid hexadecimal digit */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index ab1942c03..3bf6d4426 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -16,7 +16,7 @@ #ifndef JS_SCANNER_INTERNAL_H #define JS_SCANNER_INTERNAL_H -/** \addtogroup parser Parser +/* \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript @@ -34,6 +34,19 @@ typedef struct const uint8_t *source_p; /**< start source byte */ } scanner_source_start_t; +/** + * Flags for type member of lexer_lit_location_t structure in the literal pool. + */ +typedef enum +{ + SCANNER_LITERAL_IS_ARG = (1 << 0), /**< literal is argument */ + SCANNER_LITERAL_IS_LOCAL = (1 << 1), /**< literal is local (similar to let, + * but var is allowed with the same identifier) */ + SCANNER_LITERAL_IS_VAR = (1 << 2), /**< literal is var */ + SCANNER_LITERAL_IS_FUNC = (1 << 3), /**< literal is function */ + SCANNER_LITERAL_NO_REG = (1 << 4), /**< literal cannot be stored in register */ +} scanner_literal_type_flags_t; + /** * For statement descriptor. */ @@ -55,14 +68,64 @@ typedef struct scanner_case_info_t **last_case_p; /**< last case info */ } scanner_switch_statement_t; +/** + * Flags for scanner_literal_pool_t structure. + */ +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 */ +} scanner_literal_pool_flags_t; + +/** + * Define a function where no arguments are allowed. + */ +#define SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS \ + (SCANNER_LITERAL_POOL_FUNCTION | SCANNER_LITERAL_POOL_NO_ARGUMENTS) + +/** + * Local literal pool. + */ +typedef struct scanner_literal_pool_t +{ + struct scanner_literal_pool_t *prev_p; /**< previous literal pool */ + const uint8_t *source_p; /**< source position where the final data needs to be inserted */ + parser_list_t literal_pool; /**< list of literal */ + uint16_t status_flags; /**< combination of scanner_literal_pool_flags_t flags */ + uint16_t no_declarations; /**< size of scope stack required during parsing */ +} scanner_literal_pool_t; + /** * Scanner context. */ -typedef struct +struct scanner_context_t { uint8_t mode; /**< scanner mode */ +#if ENABLED (JERRY_DEBUGGER) + uint8_t debugger_enabled; /**< debugger is enabled */ +#endif /* ENABLED (JERRY_DEBUGGER) */ + scanner_literal_pool_t *active_literal_pool_p; /**< currently active literal pool */ scanner_switch_statement_t active_switch_statement; /**< currently active switch statement */ -} scanner_context_t; + scanner_info_t *end_arguments_p; /**< position of end arguments */ +}; + +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, + scanner_info_t *start_info_p, size_t size); +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); +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); +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 e0655c938..24d6b8eb9 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -15,6 +15,7 @@ #include "js-parser-internal.h" #include "js-scanner-internal.h" +#include "lit-char-helpers.h" #if ENABLED (JERRY_PARSER) @@ -28,6 +29,9 @@ * @{ */ +JERRY_STATIC_ASSERT (PARSER_MAXIMUM_NUMBER_OF_LITERALS + PARSER_MAXIMUM_NUMBER_OF_REGISTERS < PARSER_REGISTER_START, + maximum_number_of_literals_plus_registers_must_be_less_than_register_start); + /** * Raise a scanner error. */ @@ -74,6 +78,52 @@ scanner_free (void *ptr, /**< pointer to free */ jmem_heap_free_block (ptr, size); } /* scanner_free */ +/** + * Count the size of a stream after an info block. + * + * @return the size in bytes + */ +size_t +scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ + size_t size) /**< size excluding the stream */ +{ + const uint8_t *data_p = ((const uint8_t *) info_p) + size; + const uint8_t *data_p_start = data_p; + + while (data_p[0] != SCANNER_STREAM_TYPE_END) + { + switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) + { + case SCANNER_STREAM_TYPE_ARG: + case SCANNER_STREAM_TYPE_ARG_FUNC: + case SCANNER_STREAM_TYPE_VAR: + case SCANNER_STREAM_TYPE_FUNC: + { + break; + } + default: + { + JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE); + data_p++; + continue; + } + } + + data_p += 3; + + if (data_p[-3] & SCANNER_STREAM_UINT16_DIFF) + { + data_p++; + } + else if (data_p[-1] == 0) + { + data_p += sizeof (const uint8_t *); + } + } + + return size + 1 + (size_t) (data_p - data_p_start); +} /* scanner_get_stream_size */ + /** * Insert a scanner info block into the scanner info chain. * @@ -89,6 +139,7 @@ scanner_insert_info (parser_context_t *context_p, /**< context */ scanner_info_t *prev_scanner_info_p = NULL; JERRY_ASSERT (scanner_info_p != NULL); + JERRY_ASSERT (source_p != NULL); new_scanner_info_p->source_p = source_p; @@ -117,6 +168,42 @@ scanner_insert_info (parser_context_t *context_p, /**< context */ return new_scanner_info_p; } /* scanner_insert_info */ +/** + * Insert a scanner info block into the scanner info chain before a given info block. + * + * @return newly allocated scanner info + */ +scanner_info_t * +scanner_insert_info_before (parser_context_t *context_p, /**< context */ + const uint8_t *source_p, /**< triggering position */ + scanner_info_t *start_info_p, /**< first info position */ + size_t size) /**< size of the memory block */ +{ + JERRY_ASSERT (start_info_p != NULL); + + scanner_info_t *new_scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, size); + scanner_info_t *scanner_info_p = start_info_p->next_p; + scanner_info_t *prev_scanner_info_p = start_info_p; + + new_scanner_info_p->source_p = source_p; + + while (source_p < scanner_info_p->source_p) + { + prev_scanner_info_p = scanner_info_p; + scanner_info_p = scanner_info_p->next_p; + + JERRY_ASSERT (scanner_info_p != NULL); + } + + /* Multiple scanner info blocks cannot be assigned to the same position. */ + JERRY_ASSERT (source_p != scanner_info_p->source_p); + + new_scanner_info_p->next_p = scanner_info_p; + + prev_scanner_info_p->next_p = new_scanner_info_p; + return new_scanner_info_p; +} /* scanner_insert_info_before */ + /** * Release the next scanner info. */ @@ -225,6 +312,577 @@ scanner_seek (parser_context_t *context_p) /**< context */ context_p->next_scanner_info_p = prev_p->next_p; } /* scanner_seek */ +/** + * Push a new literal pool. + * + * @return the newly created literal pool + */ +scanner_literal_pool_t * +scanner_push_literal_pool (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p, /**< scanner context */ + uint16_t status_flags) /**< combination of scanner_literal_pool_flags_t flags */ +{ + scanner_literal_pool_t *prev_literal_pool_p = scanner_context_p->active_literal_pool_p; + scanner_literal_pool_t *literal_pool_p; + + literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t)); + + if (!(status_flags & SCANNER_LITERAL_POOL_FUNCTION)) + { + JERRY_ASSERT (prev_literal_pool_p != NULL); + + if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + status_flags |= SCANNER_LITERAL_POOL_IN_WITH; + } + } + + parser_list_init (&literal_pool_p->literal_pool, + sizeof (lexer_lit_location_t), + (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t))); + literal_pool_p->source_p = NULL; + literal_pool_p->status_flags = status_flags; + literal_pool_p->no_declarations = 0; + + literal_pool_p->prev_p = prev_literal_pool_p; + scanner_context_p->active_literal_pool_p = literal_pool_p; + + return literal_pool_p; +} /* scanner_push_literal_pool */ + +JERRY_STATIC_ASSERT (PARSER_MAXIMUM_IDENT_LENGTH <= UINT8_MAX, + maximum_ident_length_must_fit_in_a_byte); + +/** + * Pop the last literal pool from the end. + */ +void +scanner_pop_literal_pool (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; + scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_p; + parser_list_iterator_t literal_iterator; + lexer_lit_location_t *literal_p; + bool is_function = (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION) != 0; + 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 (no_reg && prev_literal_pool_p != NULL) + { + prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_REG; + } + +#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) */ + + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + + const uint8_t *prev_source_p = literal_pool_p->source_p - 1; + 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)) + { + continue; + } + + if (search_arguments + && literal_p->length == 9 + && lexer_compare_identifiers (literal_p->char_p, (const uint8_t *) "arguments", 9)) + { + search_arguments = false; + + if (type & (SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_FUNC)) + { + arguments_required = false; + } + else + { + literal_p->type = 0; + arguments_required = true; + continue; + } + } + + if (((type & (SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_ARG)) && is_function) + || (type & SCANNER_LITERAL_IS_LOCAL)) + { + JERRY_ASSERT (is_function || !(literal_p->type & SCANNER_LITERAL_IS_ARG)); + + if (literal_p->length == 0) + { + compressed_size += 1; + continue; + } + + no_declarations++; + + if (type & SCANNER_LITERAL_IS_FUNC) + { + no_declarations++; + } + + 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 (is_function || !(type & SCANNER_LITERAL_IS_VAR)) + { + continue; + } + } + + if (prev_literal_pool_p != NULL && literal_p->length > 0) + { + /* Propagate literal to upper level. */ + lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, + prev_literal_pool_p, + literal_p); + + if (is_function || (type & SCANNER_LITERAL_NO_REG)) + { + literal_location_p->type |= SCANNER_LITERAL_NO_REG; + } + + type = (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); + } + } + + if (is_function || (compressed_size > 1)) + { + compressed_size += is_function ? sizeof (scanner_function_info_t) : sizeof (scanner_info_t); + + scanner_info_t *info_p; + + if (prev_literal_pool_p != NULL || scanner_context_p->end_arguments_p == NULL) + { + info_p = scanner_insert_info (context_p, literal_pool_p->source_p, compressed_size); + } + else + { + scanner_info_t *start_info_p = scanner_context_p->end_arguments_p; + info_p = scanner_insert_info_before (context_p, literal_pool_p->source_p, start_info_p, compressed_size); + } + + if (no_declarations > PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) + { + no_declarations = PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK; + } + + uint8_t *data_p = (uint8_t *) info_p; + + if (is_function) + { + info_p->type = SCANNER_TYPE_FUNCTION; + data_p += sizeof (scanner_function_info_t); + + scanner_function_info_t *function_info_p = (scanner_function_info_t *) info_p; + uint8_t status_flags = 0; + + if (arguments_required) + { + status_flags |= SCANNER_FUNCTION_ARGUMENTS_NEEDED; + + if (no_declarations < PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK) + { + no_declarations++; + } + } + + function_info_p->info.u8_arg = status_flags; + function_info_p->info.u16_arg = (uint16_t) no_declarations; + } + else + { + info_p->type = SCANNER_TYPE_BLOCK; + 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; + + 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))) + { + continue; + } + + if (literal_p->length == 0) + { + *data_p++ = SCANNER_STREAM_TYPE_HOLE; + continue; + } + + no_declarations++; + + uint8_t type = SCANNER_STREAM_TYPE_VAR; + + if (literal_p->type & SCANNER_LITERAL_IS_FUNC) + { + no_declarations++; + type = SCANNER_STREAM_TYPE_FUNC; + + if (literal_p->type & SCANNER_LITERAL_IS_ARG) + { + type = SCANNER_STREAM_TYPE_ARG_FUNC; + } + } + else if (literal_p->type & SCANNER_LITERAL_IS_ARG) + { + type = SCANNER_STREAM_TYPE_ARG; + } + + if (literal_p->has_escape) + { + type |= SCANNER_STREAM_HAS_ESCAPE; + } + + if ((literal_p->type & SCANNER_LITERAL_NO_REG) + || (arguments_required && (literal_p->type & SCANNER_LITERAL_IS_ARG))) + { + type |= SCANNER_STREAM_NO_REG; + } + + 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); + } + + 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 */ + +/** + * Filter out the arguments from a literal pool. + */ +void +scanner_filter_arguments (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + /* Fast case: check whether all literals are arguments. */ + scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; + scanner_literal_pool_t *prev_literal_pool_p = literal_pool_p->prev_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 (no_reg && prev_literal_pool_p != NULL) + { + prev_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_REG; + } + + literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_NO_REG; + + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (no_reg) + { + literal_p->type |= SCANNER_LITERAL_NO_REG; + } + + if (!(literal_p->type & SCANNER_LITERAL_IS_ARG)) + { + break; + } + } + + if (literal_p == NULL) + { + return; + } + + scanner_literal_pool_t *new_literal_pool_p; + + new_literal_pool_p = (scanner_literal_pool_t *) scanner_malloc (context_p, sizeof (scanner_literal_pool_t)); + + new_literal_pool_p->prev_p = literal_pool_p; + scanner_context_p->active_literal_pool_p = new_literal_pool_p; + + *new_literal_pool_p = *literal_pool_p; + parser_list_init (&new_literal_pool_p->literal_pool, + sizeof (lexer_lit_location_t), + (uint32_t) ((128 - sizeof (void *)) / sizeof (lexer_lit_location_t))); + + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->type & SCANNER_LITERAL_IS_ARG) + { + lexer_lit_location_t *new_literal_p; + new_literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &new_literal_pool_p->literal_pool); + *new_literal_p = *literal_p; + + if (no_reg) + { + new_literal_p->type |= SCANNER_LITERAL_NO_REG; + } + } + else + { + /* Propagate literal to upper level. */ + lexer_lit_location_t *literal_location_p = scanner_add_custom_literal (context_p, + prev_literal_pool_p, + literal_p); + if (literal_p->type & SCANNER_LITERAL_NO_REG) + { + literal_location_p->type |= SCANNER_LITERAL_NO_REG; + } + } + } + + new_literal_pool_p->prev_p = prev_literal_pool_p; + + parser_list_free (&literal_pool_p->literal_pool); + scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t)); +} /* scanner_filter_arguments */ + +/** + * Add any literal to the specified literal pool. + * + * @return pointer to the literal + */ +lexer_lit_location_t * +scanner_add_custom_literal (parser_context_t *context_p, /**< context */ + scanner_literal_pool_t *literal_pool_p, /**< literal pool */ + const lexer_lit_location_t *literal_location_p) /**< literal */ +{ + parser_list_iterator_t literal_iterator; + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + lexer_lit_location_t *literal_p; + + const uint8_t *char_p = literal_location_p->char_p; + prop_length_t length = literal_location_p->length; + + if (JERRY_LIKELY (!literal_location_p->has_escape)) + { + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->length == length) + { + if (JERRY_LIKELY (!literal_p->has_escape)) + { + if (memcmp (literal_p->char_p, char_p, length) == 0) + { + return literal_p; + } + } + else if (lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + /* The non-escaped version is preferred. */ + literal_p->char_p = char_p; + literal_p->has_escape = 0; + return literal_p; + } + } + } + } + else + { + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->length == length + && lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + return literal_p; + } + } + } + + literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool); + *literal_p = *literal_location_p; + + literal_p->type = 0; + + return literal_p; +} /* scanner_add_custom_literal */ + +/** + * Add the current literal token to the current literal pool. + * + * @return pointer to the literal + */ +inline lexer_lit_location_t * JERRY_ATTR_ALWAYS_INLINE +scanner_add_literal (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + return scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &context_p->token.lit_location); +} /* scanner_add_literal */ + +/** + * Add the current literal token to the current literal pool and + * set SCANNER_LITERAL_NO_REG if it is inside a with statement. + * + * @return pointer to the literal + */ +inline void JERRY_ATTR_ALWAYS_INLINE +scanner_add_reference (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + lexer_lit_location_t *lit_location_p = scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &context_p->token.lit_location); + if (scanner_context_p->active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + lit_location_p->type |= SCANNER_LITERAL_NO_REG; + } + + scanner_detect_eval_call (context_p, scanner_context_p); +} /* scanner_add_reference */ + +/** + * Append an argument to the literal pool. If the argument is already present, make it a "hole". + */ +void +scanner_append_argument (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; + parser_list_iterator_init (&literal_pool_p->literal_pool, &literal_iterator); + lexer_lit_location_t *literal_p; + + const uint8_t *char_p = context_p->token.lit_location.char_p; + prop_length_t length = context_p->token.lit_location.length; + + 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->length == length) + { + if (JERRY_LIKELY (!literal_p->has_escape)) + { + if (memcmp (literal_p->char_p, char_p, length) == 0) + { + literal_p->length = 0; + break; + } + } + else if (lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + literal_p->length = 0; + break; + } + } + } + } + else + { + while ((literal_p = (lexer_lit_location_t *) parser_list_iterator_next (&literal_iterator)) != NULL) + { + if (literal_p->length == length + && lexer_compare_identifiers (literal_p->char_p, char_p, length)) + { + literal_p->length = 0; + break; + } + } + } + + literal_p = (lexer_lit_location_t *) parser_list_append (context_p, &literal_pool_p->literal_pool); + *literal_p = context_p->token.lit_location; + + literal_p->type = SCANNER_LITERAL_IS_ARG; +} /* scanner_append_argument */ + +/** + * Check whether an eval call is performed and update the status flags accordingly. + */ +void +scanner_detect_eval_call (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + if (context_p->token.lit_location.length == 4 + && lexer_compare_identifiers (context_p->token.lit_location.char_p, (const uint8_t *) "eval", 4) + && lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) + { + scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_NO_REG; + } +} /* scanner_detect_eval_call */ + /** * Reverse the scanner info chain after the scanning is completed. */ @@ -282,6 +940,16 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ scanner_info_p = context_p->active_scanner_info_p; continue; } + case SCANNER_TYPE_FUNCTION: + { + size = scanner_get_stream_size (scanner_info_p, sizeof (scanner_function_info_t)); + break; + } + case SCANNER_TYPE_BLOCK: + { + size = scanner_get_stream_size (scanner_info_p, sizeof (scanner_info_t)); + break; + } case SCANNER_TYPE_WHILE: case SCANNER_TYPE_FOR_IN: #if ENABLED (JERRY_ES2015_FOR_OF) @@ -305,12 +973,7 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ } default: { -#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) - JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS - || scanner_info_p->type == SCANNER_TYPE_ARROW); -#else /* !ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ JERRY_ASSERT (scanner_info_p->type == SCANNER_TYPE_END_ARGUMENTS); -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ break; } } @@ -323,6 +986,251 @@ scanner_cleanup (parser_context_t *context_p) /**< context */ context_p->active_scanner_info_p = NULL; } /* scanner_cleanup */ +/** + * Description of "arguments" literal string. + */ +const lexer_lit_location_t lexer_arguments_literal = +{ + (const uint8_t *) "arguments", 9, LEXER_IDENT_LITERAL, false +}; + +/** + * Create and/or initialize var/let/const/function/etc. variables. + */ +void +scanner_create_variables (parser_context_t *context_p, /**< context */ + size_t size) /**< size excluding the stream */ +{ + scanner_info_t *info_p = context_p->next_scanner_info_p; + const uint8_t *data_p = ((const uint8_t *) info_p) + size; + uint8_t info_type = info_p->type; + lexer_lit_location_t literal; + parser_scope_stack *scope_stack_p; + parser_scope_stack *scope_stack_end_p; + + JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION || info_type == SCANNER_TYPE_BLOCK); + + if (info_type == SCANNER_TYPE_FUNCTION) + { + JERRY_ASSERT (context_p->scope_stack_p == NULL); + + size_t stack_size = info_p->u16_arg * sizeof (parser_scope_stack); + context_p->scope_stack_size = info_p->u16_arg; + + if (stack_size == 0) + { + scanner_release_next (context_p, size + 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; + } + else + { + JERRY_ASSERT (context_p->scope_stack_p != NULL); + 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; + } + + uint32_t scope_stack_reg_top = context_p->scope_stack_reg_top; + + literal.char_p = info_p->source_p - 1; + + while (data_p[0] != SCANNER_STREAM_TYPE_END) + { + uint32_t type = data_p[0] & SCANNER_STREAM_TYPE_MASK; + + if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) + { + JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); + parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); + } + + if (type == SCANNER_STREAM_TYPE_HOLE) + { + data_p++; + + JERRY_ASSERT (info_type == SCANNER_TYPE_FUNCTION); + + if (info_p->u8_arg & SCANNER_FUNCTION_ARGUMENTS_NEEDED) + { + if (JERRY_UNLIKELY (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS)) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = LEXER_FLAG_FUNCTION_ARGUMENT; + + context_p->literal_count++; + } + + if (scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + scope_stack_reg_top++; + } + continue; + } + + size_t length; + + if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) + { + if (data_p[2] != 0) + { + literal.char_p += data_p[2]; + length = 2 + 1; + } + else + { + memcpy (&literal.char_p, data_p + 2 + 1, sizeof (const uint8_t *)); + length = 2 + 1 + sizeof (const uint8_t *); + } + } + else + { + int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; + + if (diff <= UINT8_MAX) + { + diff = -diff; + } + + literal.char_p += diff; + length = 2 + 2; + } + + literal.length = data_p[1]; + literal.type = LEXER_IDENT_LITERAL; + literal.has_escape = (data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? 1 : 0; + + lexer_construct_literal_object (context_p, &literal, LEXER_NEW_IDENT_LITERAL); + + uint16_t map_to; + + if (!(data_p[0] & SCANNER_STREAM_NO_REG) + && scope_stack_reg_top < PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + map_to = (uint16_t) (PARSER_REGISTER_START + scope_stack_reg_top); + + scope_stack_p->map_from = context_p->lit_object.index; + scope_stack_p->map_to = map_to; + scope_stack_reg_top++; + } + else + { + context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; + map_to = context_p->lit_object.index; + + scope_stack_p->map_from = map_to; + scope_stack_p->map_to = map_to; + + if (info_type == SCANNER_TYPE_FUNCTION) + { + context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; + } + + if (type == 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); + } + else if (type == SCANNER_STREAM_TYPE_ARG || type == SCANNER_STREAM_TYPE_ARG_FUNC) + { + if (type == 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); + } + + 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) + { + continue; + } + + if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) + { + JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); + parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); + } + + if (JERRY_UNLIKELY (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS)) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + +#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, context_p->literal_count, map_to); + + scope_stack_p->map_from = PARSER_SCOPE_STACK_FUNC; + scope_stack_p->map_to = context_p->literal_count; + scope_stack_p++; + + lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + + literal_p->type = LEXER_UNUSED_LITERAL; + literal_p->status_flags = 0; + + context_p->literal_count++; + } + + if (info_type == SCANNER_TYPE_FUNCTION && (info_p->u8_arg & SCANNER_FUNCTION_ARGUMENTS_NEEDED)) + { + if (JERRY_UNLIKELY (scope_stack_p >= scope_stack_end_p)) + { + JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); + parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); + } + + context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED; + + lexer_construct_literal_object (context_p, &lexer_arguments_literal, lexer_arguments_literal.type); + + scope_stack_p->map_from = context_p->lit_object.index; + scope_stack_p->map_to = context_p->lit_object.index; + scope_stack_p++; + } + + 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 (context_p->register_count < scope_stack_reg_top) + { + context_p->register_count = (uint16_t) scope_stack_reg_top; + } + + scanner_release_next (context_p, (size_t) (data_p + 1 - ((const uint8_t *) info_p))); + parser_flush_cbc (context_p); +} /* scanner_create_variables */ + /** * Get location from context. */ diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 92cae0b30..38f1ab8b5 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "jcontext.h" #include "js-parser-internal.h" #include "js-scanner-internal.h" #include "lit-char-helpers.h" @@ -59,13 +60,15 @@ typedef enum typedef enum { SCAN_STACK_SCRIPT, /**< script */ - SCAN_STACK_EVAL_FUNCTION, /**< evaluated function */ + SCAN_STACK_SCRIPT_FUNCTION, /**< script is a function body */ SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ SCAN_STACK_FUNCTION_STATEMENT, /**< function statement */ SCAN_STACK_FUNCTION_EXPRESSION, /**< function expression */ SCAN_STACK_FUNCTION_PROPERTY, /**< function expression in an object literal or class */ SCAN_STACK_SWITCH_BLOCK, /**< block part of "switch" statement */ SCAN_STACK_IF_STATEMENT, /**< statement part of "if" statements */ + SCAN_STACK_WITH_STATEMENT, /**< statement part of "with" statements */ + SCAN_STACK_WITH_EXPRESSION, /**< expression part of "with" statements */ SCAN_STACK_DO_STATEMENT, /**< statement part of "do" statements */ SCAN_STACK_DO_EXPRESSION, /**< expression part of "do" statements */ SCAN_STACK_WHILE_EXPRESSION, /**< expression part of "while" iterator */ @@ -158,10 +161,7 @@ static void scanner_process_arrow (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p) /**< scanner context */ { - scanner_source_start_t source_start; - parser_stack_pop_uint8 (context_p); - parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t)); lexer_next_token (context_p); @@ -169,11 +169,16 @@ scanner_process_arrow (parser_context_t *context_p, /**< context */ || (context_p->token.flags & LEXER_WAS_NEWLINE)) { scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + scanner_pop_literal_pool (context_p, scanner_context_p); return; } - scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t)); - info_p->type = SCANNER_TYPE_ARROW; + scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; + + literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS; + literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_WITH; + + scanner_filter_arguments (context_p, scanner_context_p); scanner_check_arrow_body (context_p, scanner_context_p); } /* scanner_process_arrow */ @@ -186,8 +191,14 @@ scanner_process_simple_arrow (parser_context_t *context_p, /**< context */ scanner_context_t *scanner_context_p, /**< scanner context */ const uint8_t *source_p) /**< identifier end position */ { - scanner_info_t *info_p = scanner_insert_info (context_p, source_p, sizeof (scanner_info_t)); - info_p->type = SCANNER_TYPE_ARROW; + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, + scanner_context_p, + SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS); + literal_pool_p->source_p = source_p; + + lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_ARG; /* Skip the => token, which size is two. */ context_p->source_p += 2; @@ -197,6 +208,64 @@ scanner_process_simple_arrow (parser_context_t *context_p, /**< context */ scanner_check_arrow_body (context_p, scanner_context_p); } /* scanner_process_simple_arrow */ +/** + * Process the next argument of a might-be arrow function. + */ +static void +scanner_process_arrow_arg (parser_context_t *context_p, /**< context */ + scanner_context_t *scanner_context_p) /**< scanner context */ +{ + JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS); + + const uint8_t *source_p = context_p->source_p; + bool process_arrow = false; + + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; + +#if ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) + if (context_p->token.type == LEXER_THREE_DOTS) + { + lexer_next_token (context_p); + } +#endif /* ENABLED (JERRY_ES2015_FUNCTION_REST_PARAMETER) */ + + if (context_p->token.type == LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + + if (lexer_check_arrow (context_p)) + { + process_arrow = true; + } + else + { + scanner_append_argument (context_p, scanner_context_p); + + scanner_detect_eval_call (context_p, scanner_context_p); + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_ASSIGN + || context_p->token.type == LEXER_COMMA + || context_p->token.type == LEXER_RIGHT_PAREN) + { + return; + } + } + } + + scanner_pop_literal_pool (context_p, scanner_context_p); + + parser_stack_pop_uint8 (context_p); + parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); + + if (process_arrow) + { + scanner_process_simple_arrow (context_p, scanner_context_p, source_p); + } +} /* scanner_process_arrow_arg */ + #endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ /** @@ -226,6 +295,8 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ } case LEXER_KEYW_FUNCTION: { + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + lexer_next_token (context_p); if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) @@ -240,15 +311,26 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ case LEXER_LEFT_PAREN: { #if ENABLED (JERRY_ES2015_ARROW_FUNCTION) - scanner_source_start_t source_start; - source_start.source_p = context_p->source_p; - - parser_stack_push (context_p, &source_start, sizeof (scanner_source_start_t)); parser_stack_push_uint8 (context_p, SCAN_STACK_ARROW_ARGUMENTS); + + scanner_literal_pool_t *literal_pool_p; + literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, 0); + literal_pool_p->source_p = context_p->source_p; + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_RIGHT_PAREN) + { + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return SCAN_KEEP_TOKEN; + } + + scanner_process_arrow_arg (context_p, scanner_context_p); + return SCAN_KEEP_TOKEN; #else parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; +#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ break; } case LEXER_LEFT_SQUARE: @@ -278,8 +360,8 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015_TEMPLATE_STRINGS) */ case LEXER_LITERAL: -#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) { +#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) const uint8_t *source_p = context_p->source_p; if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL @@ -288,9 +370,14 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ scanner_process_simple_arrow (context_p, scanner_context_p, source_p); return SCAN_KEEP_TOKEN; } +#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ + + if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + scanner_add_reference (context_p, scanner_context_p); + } /* FALLTHRU */ } -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ case LEXER_KEYW_THIS: case LEXER_KEYW_SUPER: case LEXER_LIT_TRUE: @@ -305,6 +392,13 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ { parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_EXPRESSION); scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + return SCAN_KEEP_TOKEN; + } break; } #endif /* ENABLED (JERRY_ES2015_CLASS) */ @@ -335,13 +429,6 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ parser_stack_pop_uint8 (context_p); break; } -#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) - if (stack_top == SCAN_STACK_ARROW_ARGUMENTS) - { - scanner_process_arrow (context_p, scanner_context_p); - return SCAN_KEEP_TOKEN; - } -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ /* FALLTHRU */ } default: @@ -440,6 +527,12 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * break; } #if ENABLED (JERRY_ES2015_ARROW_FUNCTION) + case SCAN_STACK_ARROW_ARGUMENTS: + { + lexer_next_token (context_p); + scanner_process_arrow_arg (context_p, scanner_context_p); + return SCAN_KEEP_TOKEN; + } case SCAN_STACK_ARROW_EXPRESSION: { break; @@ -476,30 +569,22 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * switch (stack_top) { - case SCAN_STACK_VAR: - { - parser_stack_pop_uint8 (context_p); - return SCAN_KEEP_TOKEN; - } - case SCAN_STACK_PAREN_EXPRESSION: - { - if (type != LEXER_RIGHT_PAREN) - { - break; - } - scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; - parser_stack_pop_uint8 (context_p); - return SCAN_NEXT_TOKEN; - } - case SCAN_STACK_STATEMENT_WITH_EXPR: + case SCAN_STACK_WITH_EXPRESSION: { if (type != LEXER_RIGHT_PAREN) { break; } - scanner_context_p->mode = SCAN_MODE_STATEMENT; parser_stack_pop_uint8 (context_p); + + uint16_t status_flags = scanner_context_p->active_literal_pool_p->status_flags; + parser_stack_push_uint8 (context_p, (status_flags & SCANNER_LITERAL_POOL_IN_WITH) ? 1 : 0); + parser_stack_push_uint8 (context_p, SCAN_STACK_WITH_STATEMENT); + status_flags |= SCANNER_LITERAL_POOL_IN_WITH; + scanner_context_p->active_literal_pool_p->status_flags = status_flags; + + scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } case SCAN_STACK_DO_EXPRESSION: @@ -535,6 +620,32 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * scanner_context_p->mode = SCAN_MODE_STATEMENT; return SCAN_NEXT_TOKEN; } + case SCAN_STACK_PAREN_EXPRESSION: + { + if (type != LEXER_RIGHT_PAREN) + { + break; + } + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + parser_stack_pop_uint8 (context_p); + return SCAN_NEXT_TOKEN; + } + case SCAN_STACK_STATEMENT_WITH_EXPR: + { + if (type != LEXER_RIGHT_PAREN) + { + break; + } + + scanner_context_p->mode = SCAN_MODE_STATEMENT; + parser_stack_pop_uint8 (context_p); + return SCAN_NEXT_TOKEN; + } + case SCAN_STACK_VAR: + { + parser_stack_pop_uint8 (context_p); + return SCAN_KEEP_TOKEN; + } case SCAN_STACK_FOR_VAR_START: case SCAN_STACK_FOR_START: { @@ -750,6 +861,8 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * if (stack_top == SCAN_STACK_FUNCTION_PROPERTY) { + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; } @@ -758,6 +871,8 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * if (context_p->token.type == LEXER_LEFT_PAREN) { + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; return SCAN_KEEP_TOKEN; @@ -809,6 +924,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * } case SCAN_STACK_ARROW_EXPRESSION: { + scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_KEEP_TOKEN; @@ -832,7 +948,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context * parser_stack_pop_uint8 (context_p); if (type != LEXER_RIGHT_PAREN - && (type != LEXER_EOS || context_p->stack_top_uint8 != SCAN_STACK_EVAL_FUNCTION)) + && (type != LEXER_EOS || context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION)) { break; } @@ -916,6 +1032,10 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ { parser_stack_push_uint8 (context_p, SCAN_STACK_IF_STATEMENT); } + else if (type == LEXER_KEYW_WITH) + { + mode = SCAN_STACK_WITH_EXPRESSION; + } else if (type == LEXER_KEYW_SWITCH) { mode = SCAN_STACK_SWITCH_EXPRESSION; @@ -1065,6 +1185,11 @@ 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 | SCANNER_LITERAL_IS_FUNC; + + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_STATEMENT); return SCAN_NEXT_TOKEN; @@ -1072,6 +1197,16 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_CLASS) case LEXER_KEYW_CLASS: { + 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; + scanner_context_p->mode = SCAN_MODE_CLASS_DECLARATION; parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_STATEMENT); return SCAN_NEXT_TOKEN; @@ -1099,6 +1234,9 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { + lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_VAR; + lexer_next_token (context_p); if (context_p->token.type == LEXER_COMMA) @@ -1129,6 +1267,9 @@ 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_next_token (context_p); } else if (context_p->token.type == LEXER_LEFT_BRACE) @@ -1143,21 +1284,29 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } - lexer_next_token (context_p); - - if (lexer_compare_literal_to_identifier (context_p, "as", 2)) + if (lexer_check_next_character (context_p, LIT_CHAR_LOWERCASE_A)) { lexer_next_token (context_p); + if (!lexer_compare_literal_to_identifier (context_p, "as", 2)) + { + scanner_raise_error (context_p); + } + + 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_next_token (context_p); } + lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_VAR; + + lexer_next_token (context_p); + if (context_p->token.type != LEXER_RIGHT_BRACE) { if (context_p->token.type != LEXER_COMMA) @@ -1211,8 +1360,21 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { + lexer_lit_location_t *location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; + lexer_next_token (context_p); } + else + { + lexer_lit_location_t *location_p; + location_p = scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &lexer_default_literal); + location_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; + } + + scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_STATEMENT); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; @@ -1221,13 +1383,44 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_CLASS) if (context_p->token.type == LEXER_KEYW_CLASS) { - scanner_context_p->mode = SCAN_MODE_STATEMENT; + 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) + { + 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; + } + + lexer_lit_location_t *location_p; + location_p = scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &lexer_default_literal); + location_p->type |= SCANNER_LITERAL_IS_VAR; return SCAN_KEEP_TOKEN; } #endif /* ENABLED (JERRY_ES2015_CLASS) */ + /* Assignment expression. */ + lexer_lit_location_t *location_p; + location_p = scanner_add_custom_literal (context_p, + scanner_context_p->active_literal_pool_p, + &lexer_default_literal); + location_p->type |= SCANNER_LITERAL_IS_VAR; scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION; - return SCAN_KEEP_TOKEN; + + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + return SCAN_KEEP_TOKEN; + } + + location_p = scanner_add_literal (context_p, scanner_context_p); + location_p->type |= SCANNER_LITERAL_IS_VAR; + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + return SCAN_NEXT_TOKEN; } scanner_context_p->mode = SCAN_MODE_STATEMENT_END; @@ -1342,6 +1535,8 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ + scanner_add_reference (context_p, scanner_context_p); + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return SCAN_NEXT_TOKEN; } @@ -1374,7 +1569,7 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ switch (context_p->stack_top_uint8) { case SCAN_STACK_SCRIPT: - case SCAN_STACK_EVAL_FUNCTION: + case SCAN_STACK_SCRIPT_FUNCTION: { if (type == LEXER_EOS) { @@ -1393,6 +1588,11 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ break; } + if (context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_STATEMENT) + { + scanner_pop_literal_pool (context_p, scanner_context_p); + } + terminator_found = true; parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); @@ -1405,6 +1605,8 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ break; } + scanner_pop_literal_pool (context_p, scanner_context_p); + scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; parser_stack_pop_uint8 (context_p); return SCAN_NEXT_TOKEN; @@ -1416,6 +1618,7 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ break; } + scanner_pop_literal_pool (context_p, scanner_context_p); parser_stack_pop_uint8 (context_p); #if ENABLED (JERRY_ES2015_CLASS) @@ -1475,6 +1678,22 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ } continue; } + case SCAN_STACK_WITH_STATEMENT: + { + scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; + + JERRY_ASSERT (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH); + + parser_stack_pop_uint8 (context_p); + + if (context_p->stack_top_uint8 == 0) + { + literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_WITH; + } + + parser_stack_pop_uint8 (context_p); + continue; + } case SCAN_STACK_DO_STATEMENT: { parser_stack_pop_uint8 (context_p); @@ -1515,6 +1734,11 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); + if (stack_top == SCAN_STACK_CATCH_STATEMENT) + { + scanner_pop_literal_pool (context_p, scanner_context_p); + } + /* A finally statement is optional after a try or catch statement. */ if (context_p->token.type == LEXER_KEYW_FINALLY) { @@ -1549,6 +1773,8 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } + const uint8_t *source_p = context_p->source_p; + lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL @@ -1557,6 +1783,13 @@ scanner_scan_statement_end (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } + 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 = source_p; + + lexer_lit_location_t *lit_location_p = scanner_add_literal (context_p, scanner_context_p); + lit_location_p->type |= SCANNER_LITERAL_IS_LOCAL; + lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) @@ -1606,7 +1839,15 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */ +#if ENABLED (JERRY_DEBUGGER) + scanner_context.debugger_enabled = (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) ? 1 : 0; +#endif /* ENABLED (JERRY_DEBUGGER) */ + scanner_context.active_literal_pool_p = NULL; scanner_context.active_switch_statement.last_case_p = NULL; + scanner_context.end_arguments_p = NULL; + + /* This assignment must be here because of Apple compilers. */ + context_p->u.scanner_context_p = &scanner_context; parser_stack_init (context_p); @@ -1620,6 +1861,10 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ context_p->source_p = source_p; context_p->source_end_p = source_end_p; + uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_NO_REG; + scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, status_flags); + literal_pool_p->source_p = source_p; + scanner_context.mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT); @@ -1629,8 +1874,10 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ { context_p->source_p = arg_list_p; context_p->source_end_p = arg_list_end_p; + + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; - parser_stack_push_uint8 (context_p, SCAN_STACK_EVAL_FUNCTION); + parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT_FUNCTION); /* Faking the first token. */ context_p->token.type = LEXER_LEFT_PAREN; @@ -1664,11 +1911,6 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_CLASS) case SCAN_MODE_CLASS_DECLARATION: { - if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) - { - lexer_next_token (context_p); - } - if (context_p->token.type == LEXER_KEYW_EXTENDS) { parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_EXTENDS); @@ -1719,6 +1961,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_LEFT_PAREN) { + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); continue; } } @@ -1732,6 +1975,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015_OBJECT_INITIALIZER) */ + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); lexer_next_token (context_p); continue; } @@ -1791,6 +2035,14 @@ 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; + + if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) + { + location_p->type |= SCANNER_LITERAL_NO_REG; + } + lexer_next_token (context_p); switch (context_p->token.type) @@ -1828,11 +2080,16 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } case SCAN_MODE_FUNCTION_ARGUMENTS: { - JERRY_ASSERT (stack_top == SCAN_STACK_EVAL_FUNCTION + JERRY_ASSERT (stack_top == SCAN_STACK_SCRIPT_FUNCTION || stack_top == SCAN_STACK_FUNCTION_STATEMENT || stack_top == SCAN_STACK_FUNCTION_EXPRESSION || stack_top == SCAN_STACK_FUNCTION_PROPERTY); + JERRY_ASSERT (scanner_context.active_literal_pool_p != NULL + && (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION)); + + scanner_context.active_literal_pool_p->source_p = context_p->source_p; + if (type != LEXER_LEFT_PAREN) { scanner_raise_error (context_p); @@ -1862,6 +2119,9 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ { scanner_raise_error (context_p); } + + scanner_append_argument (context_p, &scanner_context); + lexer_next_token (context_p); if (context_p->token.type != LEXER_COMMA) @@ -1881,13 +2141,14 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ } #endif /* ENABLED (JERRY_ES2015_FUNCTION_PARAMETER_INITIALIZER) */ - if (context_p->token.type == LEXER_EOS && stack_top == SCAN_STACK_EVAL_FUNCTION) + if (context_p->token.type == LEXER_EOS && stack_top == SCAN_STACK_SCRIPT_FUNCTION) { /* End of argument parsing. */ scanner_info_t *scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, sizeof (scanner_info_t)); scanner_info_p->next_p = context_p->next_scanner_info_p; scanner_info_p->source_p = NULL; scanner_info_p->type = SCANNER_TYPE_END_ARGUMENTS; + scanner_context.end_arguments_p = scanner_info_p; context_p->next_scanner_info_p = scanner_info_p; context_p->source_p = source_p; @@ -1911,6 +2172,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ { scanner_raise_error (context_p); } + + scanner_filter_arguments (context_p, &scanner_context); scanner_context.mode = SCAN_MODE_STATEMENT_OR_TERMINATOR; break; } @@ -1957,6 +2220,7 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; break; } @@ -1974,6 +2238,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ES2015_OBJECT_INITIALIZER) if (context_p->token.type == LEXER_LEFT_PAREN) { + scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION); + parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY); scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS; continue; @@ -1996,6 +2262,8 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_raise_error (context_p); } + scanner_add_literal (context_p, &scanner_context); + lexer_next_token (context_p); if (context_p->token.type == LEXER_COMMA) @@ -2026,11 +2294,13 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scan_completed: if (context_p->stack_top_uint8 != SCAN_STACK_SCRIPT - && context_p->stack_top_uint8 != SCAN_STACK_EVAL_FUNCTION) + && context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION) { scanner_raise_error (context_p); } + scanner_pop_literal_pool (context_p, &scanner_context); + #ifndef JERRY_NDEBUG context_p->status_flags |= PARSER_SCANNING_SUCCESSFUL; #endif /* !JERRY_NDEBUG */ @@ -2042,6 +2312,30 @@ scan_completed: { context_p->error = PARSER_ERR_NO_ERROR; } + + /* The following loop may allocate memory, so it is enclosed in a try/catch. */ + PARSER_TRY (context_p->try_buffer) + { + while (scanner_context.active_literal_pool_p != NULL) + { + scanner_pop_literal_pool (context_p, &scanner_context); + } + } + PARSER_CATCH + { + JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR); + + while (scanner_context.active_literal_pool_p != NULL) + { + scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p; + + scanner_context.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)); + } + } + PARSER_TRY_END } PARSER_TRY_END @@ -2066,6 +2360,102 @@ scan_completed: source_start_p = source_p; break; } + case SCANNER_TYPE_FUNCTION: + case SCANNER_TYPE_BLOCK: + { + const uint8_t *prev_source_p = info_p->source_p - 1; + const uint8_t *data_p; + + if (info_p->type == SCANNER_TYPE_FUNCTION) + { + scanner_function_info_t *function_info_p = (scanner_function_info_t *) info_p; + data_p = (const uint8_t *) (function_info_p + 1); + + JERRY_DEBUG_MSG (" FUNCTION: flags: 0x%x declarations: %d", + (int) function_info_p->info.u8_arg, + (int) function_info_p->info.u16_arg); + } + else + { + data_p = (const uint8_t *) (info_p + 1); + + JERRY_DEBUG_MSG (" BLOCK:"); + } + + JERRY_DEBUG_MSG (" source:%d\n", (int) (info_p->source_p - source_start_p)); + + while (data_p[0] != SCANNER_STREAM_TYPE_END) + { + switch (data_p[0] & SCANNER_STREAM_TYPE_MASK) + { + case SCANNER_STREAM_TYPE_ARG: + { + JERRY_DEBUG_MSG (" ARG "); + break; + } + case SCANNER_STREAM_TYPE_ARG_FUNC: + { + 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; + } + default: + { + JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE); + JERRY_DEBUG_MSG (" HOLE\n"); + data_p++; + continue; + } + } + + size_t length; + + if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF)) + { + if (data_p[2] != 0) + { + prev_source_p += data_p[2]; + length = 2 + 1; + } + else + { + memcpy (&prev_source_p, data_p + 2 + 1, sizeof (const uint8_t *)); + length = 2 + 1 + sizeof (const uint8_t *); + } + } + else + { + int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8; + + if (diff <= UINT8_MAX) + { + diff = -diff; + } + + prev_source_p += diff; + length = 2 + 2; + } + + if (data_p[0] & SCANNER_STREAM_NO_REG) + { + JERRY_DEBUG_MSG ("* "); + } + + JERRY_DEBUG_MSG ("'%.*s'\n", data_p[1], (char *) prev_source_p); + prev_source_p += data_p[1]; + data_p += length; + } + break; + } case SCANNER_TYPE_WHILE: { name_p = "WHILE"; @@ -2123,13 +2513,6 @@ scan_completed: print_location = true; break; } -#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) - case SCANNER_TYPE_ARROW: - { - JERRY_DEBUG_MSG (" ARROW: source:%d\n", (int) (info_p->source_p - source_start_p)); - break; - } -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ } if (print_location) diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index b44d0ebb6..071e0612a 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -34,6 +34,8 @@ typedef enum SCANNER_TYPE_END, /**< mark the last info block */ SCANNER_TYPE_END_ARGUMENTS, /**< mark the end of function arguments * (only present if a function script is parsed) */ + SCANNER_TYPE_FUNCTION, /**< declarations in a function */ + SCANNER_TYPE_BLOCK, /**< declarations in a code block (usually enclosed in {}) */ SCANNER_TYPE_WHILE, /**< while statement */ SCANNER_TYPE_FOR, /**< for statement */ SCANNER_TYPE_FOR_IN, /**< for-in statement */ @@ -42,9 +44,6 @@ typedef enum #endif /* ENABLED (JERRY_ES2015_FOR_OF) */ SCANNER_TYPE_SWITCH, /**< switch statement */ SCANNER_TYPE_CASE, /**< case statement */ -#if ENABLED (JERRY_ES2015_ARROW_FUNCTION) - SCANNER_TYPE_ARROW, /**< arrow function */ -#endif /* ENABLED (JERRY_ES2015_ARROW_FUNCTION) */ } scanner_info_type_t; /** @@ -65,6 +64,8 @@ typedef struct scanner_info_t struct scanner_info_t *next_p; /**< next info structure */ const uint8_t *source_p; /**< triggering position of this scanner info */ uint8_t type; /**< type of the scanner info */ + uint8_t u8_arg; /**< custom 8-bit value */ + uint16_t u16_arg; /**< custom 16-bit value */ } scanner_info_t; /** @@ -104,6 +105,64 @@ typedef struct scanner_case_info_t *case_p; /**< list of switch cases */ } scanner_switch_info_t; +/* + * Description of compressed streams. + * + * The stream is a sequence of commands which encoded as bytes. The first byte + * contains the type of the command (see scanner_function_compressed_stream_types_t). + * + * The variable declaration commands has two arguments: + * - The first represents the length of the declared identifier + * - The second contains the relative distance from the end of the previous declaration + * Usually the distance is between 1 and 255, and represented as a single byte + * Distances between -256 and 65535 are encoded as two bytes + * Larger distances are encoded as pointers + */ + +/** + * Constants for compressed streams. + */ +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 */ +} scanner_compressed_stream_flags_t; + +/** + * Types for compressed streams. + */ +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 */ +} scanner_compressed_stream_types_t; + +/** + * Mask for decoding the type from the compressed stream. + */ +#define SCANNER_STREAM_TYPE_MASK 0xf + +/** + * Constants for u8_arg flags in scanner_function_info_t. + */ +typedef enum +{ + SCANNER_FUNCTION_ARGUMENTS_NEEDED = (1 << 0), /**< arguments object needs to be created */ +} scanner_function_flags_t; + +/** + * Scanner info for function statements. + */ +typedef struct +{ + scanner_info_t info; /**< header */ +} scanner_function_info_t; + /** * @} * @} diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 0cfac9212..8bd76930e 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -797,90 +797,69 @@ vm_init_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { switch (*byte_code_p) { - case CBC_DEFINE_VARS: + case CBC_CREATE_VAR: { - uint32_t literal_index_end; - uint32_t literal_index = register_end; - - byte_code_p++; - READ_LITERAL_INDEX (literal_index_end); - - while (literal_index <= literal_index_end) - { - ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); - vm_var_decl (frame_ctx_p, name_p); - literal_index++; - } - break; - } - - case CBC_INITIALIZE_VAR: - case CBC_INITIALIZE_VARS: - { - uint8_t type = *byte_code_p; uint32_t literal_index; - uint32_t literal_index_end; byte_code_p++; READ_LITERAL_INDEX (literal_index); - if (type == CBC_INITIALIZE_VAR) + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + vm_var_decl (frame_ctx_p, name_p); + break; + } + + case CBC_INIT_LOCAL: + { + 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); + JERRY_ASSERT (value_index >= register_end || literal_index >= register_end); + + if (value_index < register_end) { - literal_index_end = literal_index; + lit_value = frame_ctx_p->registers_p[value_index]; } else { - READ_LITERAL_INDEX (literal_index_end); + lit_value = vm_construct_literal_object (frame_ctx_p, + literal_start_p[value_index]); } - while (literal_index <= literal_index_end) + if (literal_index < register_end) { - uint32_t value_index; - ecma_value_t lit_value; + ecma_fast_free_value (frame_ctx_p->registers_p[literal_index]); + frame_ctx_p->registers_p[literal_index] = lit_value; + } + else + { + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); - READ_LITERAL_INDEX (value_index); + vm_var_decl (frame_ctx_p, name_p); - if (value_index < register_end) + 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)) { - lit_value = frame_ctx_p->registers_p[value_index]; - } - else - { - lit_value = vm_construct_literal_object (frame_ctx_p, - literal_start_p[value_index]); + ecma_free_value (JERRY_CONTEXT (error_value)); } - if (literal_index < register_end) + if (value_index >= register_end) { - frame_ctx_p->registers_p[literal_index] = lit_value; + ecma_free_value (lit_value); } - 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_free_value (JERRY_CONTEXT (error_value)); - } - - if (value_index >= register_end) - { - ecma_free_value (lit_value); - } - } - - literal_index++; } break; } @@ -3492,30 +3471,18 @@ error: JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); #endif /* ENABLED (JERRY_DEBUGGER) */ - byte_code_p = frame_ctx_p->byte_code_p; - if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_CATCH) { *stack_top_p++ = JERRY_CONTEXT (error_value); - JERRY_ASSERT (byte_code_p[0] == CBC_ASSIGN_SET_IDENT); - - uint32_t literal_index = byte_code_p[1]; - - if (literal_index >= encoding_limit) - { - literal_index = ((literal_index << 8) | byte_code_p[2]) - encoding_delta; - } - 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) */ - ecma_string_t *catch_name_p = ecma_get_string_from_value (literal_start_p[literal_index]); - ecma_op_create_mutable_binding (catch_env_p, catch_name_p, false); - frame_ctx_p->lex_env_p = catch_env_p; + + vm_init_loop (frame_ctx_p); } else { @@ -3523,6 +3490,7 @@ error: stack_top_p[-2] = JERRY_CONTEXT (error_value); } + byte_code_p = frame_ctx_p->byte_code_p; continue; } } diff --git a/tests/debugger/do_variables.expected b/tests/debugger/do_variables.expected index b450d2058..06f67fc7f 100644 --- a/tests/debugger/do_variables.expected +++ b/tests/debugger/do_variables.expected @@ -34,8 +34,8 @@ level | type 2 | global (jerry-debugger) variables 0 name | type | value -n | Number | 9 b | undefined | undefined +n | Number | 9 (jerry-debugger) variables 1 name | type | value x | Number | 3 diff --git a/tests/jerry/regression-test-issue-1555.js b/tests/jerry/regression-test-issue-1555.js index 6e2db192e..507d75f3a 100644 --- a/tests/jerry/regression-test-issue-1555.js +++ b/tests/jerry/regression-test-issue-1555.js @@ -14,7 +14,7 @@ var src = "var a = 0; while(a) { switch(a) {"; /* The += operation has a longer opcode. */ -for (var i = 0; i < 3500; i++) +for (var i = 0; i < 3300; i++) src += "case " + i + ": a += a += a; break; "; src += "} }"; diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 46c143cf0..98556fc99 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, 0x18, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x28, 0x00, 0xB8, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0xB7, 0x45, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 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