/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "js-parser-internal.h" #if JERRY_PARSER #include "jcontext.h" #include "ecma-helpers.h" #include "lit-char-helpers.h" #include "js-parser-tagged-template-literal.h" /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_expr Expression parser * @{ */ /** * Maximum precedence for right-to-left binary operation evaluation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 7 /** * Precedence for ternary operation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE 4 /** * Precedence for exponentiation operation. */ #define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 16 /** * Value of grouping level increase and decrease. */ #define PARSER_GROUPING_LEVEL_INCREASE 2 /** * Precedence of the binary tokens. * * See also: * lexer_token_type_t */ static const uint8_t parser_binary_precedence_table[] = { 3, /**< "=" */ 3, /**< "+=" */ 3, /**< "-=" */ 3, /**< "*=" */ 3, /**< "/=" */ 3, /**< "=" */ 3, /**< "<<=" */ 3, /**< ">>=" */ 3, /**< ">>>=" */ 3, /**< "&=" */ 3, /**< "|=" */ 3, /**< "^=" */ #if JERRY_ESNEXT 3, /**< "**=" */ #endif /* JERRY_ESNEXT */ 4, /**< "?"*/ #if JERRY_ESNEXT 5, /**< "??" */ #endif /* JERRY_ESNEXT */ 6, /**< "||" */ 7, /**< "&&" */ 8, /**< "|" */ 9, /**< "^" */ 10, /**< "&" */ 11, /**< "==" */ 11, /**< "!=" */ 11, /**< "===" */ 11, /**< "!==" */ 12, /**< "<" */ 12, /**< ">" */ 12, /**< "<=" */ 12, /**< ">=" */ 12, /**< in */ 12, /**< instanceof */ 13, /**< "<<" */ 13, /**< ">>" */ 13, /**< ">>>" */ 14, /**< "+" */ 14, /**< "-" */ 15, /**< "*" */ 15, /**< "/" */ 15, /**< "%" */ #if JERRY_ESNEXT 16, /**< "**" */ #endif /* JERRY_ESNEXT */ }; #if JERRY_ESNEXT JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 39, parser_binary_precedence_table_should_have_39_values_in_es2015); #else /* !JERRY_ESNEXT */ JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 36, parser_binary_precedence_table_should_have_36_values_in_es51); #endif /* JERRY_ESNEXT */ /** * Generate byte code for operators with lvalue. */ static inline void parser_push_result (parser_context_t *context_p) /**< context */ { if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); if ((context_p->last_cbc_opcode == CBC_POST_INCR || context_p->last_cbc_opcode == CBC_POST_DECR) && context_p->stack_depth >= context_p->stack_limit) { /* Stack limit is increased for CBC_POST_INCR_PUSH_RESULT * and CBC_POST_DECR_PUSH_RESULT opcodes. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } context_p->last_cbc_opcode++; parser_flush_cbc (context_p); } } /* parser_push_result */ /** * Check for invalid assignment for "eval" and "arguments" */ static void parser_check_invalid_assign (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); if (JERRY_UNLIKELY (context_p->status_flags & PARSER_IS_STRICT)) { if (context_p->last_cbc.literal_keyword_type == LEXER_KEYW_EVAL) { parser_raise_error (context_p, PARSER_ERR_EVAL_CANNOT_ASSIGNED); } else if (context_p->last_cbc.literal_keyword_type == LEXER_KEYW_ARGUMENTS) { parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED); } } } /* parser_check_invalid_assign */ #if JERRY_ESNEXT /** * Check and throw an error if the "new.target" is invalid as a left-hand side expression. */ static void parser_check_invalid_new_target (parser_context_t *context_p, /**< parser context */ cbc_opcode_t opcode) /**< current opcode under parsing */ { /* new.target is an invalid left-hand side target */ if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NEW_TARGET)) { /* Make sure that the call side is a post/pre increment or an assignment expression. * There should be no other ways the "new.target" expression should be here. */ JERRY_ASSERT ((opcode >= CBC_PRE_INCR && opcode <= CBC_POST_DECR) || (opcode == CBC_ASSIGN && (context_p->token.type == LEXER_ASSIGN || LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->token.type)))); parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED); } } /* parser_check_invalid_new_target */ #endif /* JERRY_ESNEXT */ /** * Emit identifier reference */ static void parser_emit_ident_reference (parser_context_t *context_p, /**< context */ uint16_t opcode) /* opcode */ { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = opcode; return; } uint16_t literal_index; if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; literal_index = context_p->last_cbc.value; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_THIS; literal_index = context_p->last_cbc.literal_index; } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; literal_index = context_p->last_cbc.third_literal_index; } parser_emit_cbc_literal (context_p, opcode, literal_index); } /* parser_emit_ident_reference */ /** * Generate byte code for operators with lvalue. */ static void parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ cbc_opcode_t opcode) /**< opcode */ { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); uint16_t unary_opcode; if (opcode == CBC_DELETE_PUSH_RESULT) { if (JERRY_UNLIKELY (context_p->status_flags & PARSER_IS_STRICT)) { parser_raise_error (context_p, PARSER_ERR_DELETE_IDENT_NOT_ALLOWED); } unary_opcode = CBC_DELETE_IDENT_PUSH_RESULT; } else { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, opcode + CBC_UNARY_LVALUE_WITH_IDENT)); unary_opcode = (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT); } parser_emit_ident_reference (context_p, unary_opcode); #if JERRY_ESNEXT if (unary_opcode != CBC_DELETE_IDENT_PUSH_RESULT && scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) { /* The current value must be read, but it cannot be changed. */ context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); } #endif /* JERRY_ESNEXT */ return; } if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, opcode)); context_p->last_cbc_opcode = (uint16_t) opcode; return; } if (PARSER_IS_PUSH_PROP_LITERAL (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_PROP_LITERAL_TO_PUSH_LITERAL (context_p->last_cbc_opcode); } else { /* Invalid LeftHandSide expression. */ if (opcode == CBC_DELETE_PUSH_RESULT) { #if JERRY_ESNEXT if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL) || context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); parser_emit_cbc (context_p, CBC_POP); return; } #endif /* JERRY_ESNEXT */ parser_emit_cbc (context_p, CBC_POP); parser_emit_cbc (context_p, CBC_PUSH_TRUE); return; } #if JERRY_ESNEXT parser_check_invalid_new_target (context_p, opcode); if (opcode == CBC_PRE_INCR || opcode == CBC_PRE_DECR) { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_PREFIX_OP); } else { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_POSTFIX_OP); } #else /* JERRY_ESNEXT */ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); #endif /* JERRY_ESNEXT */ } parser_emit_cbc (context_p, (uint16_t) opcode); } /* parser_emit_unary_lvalue_opcode */ /** * Parse array literal. */ static void parser_parse_array_literal (parser_context_t *context_p) /**< context */ { uint32_t pushed_items = 0; uint16_t opcode = (uint16_t) CBC_ARRAY_APPEND; JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); parser_emit_cbc (context_p, CBC_CREATE_ARRAY); lexer_next_token (context_p); while (true) { if (context_p->token.type == LEXER_RIGHT_SQUARE) { if (pushed_items > 0) { parser_emit_cbc_call (context_p, opcode, pushed_items); } return; } pushed_items++; if (context_p->token.type == LEXER_COMMA) { parser_emit_cbc (context_p, CBC_PUSH_ELISION); lexer_next_token (context_p); } else { #if JERRY_ESNEXT if (context_p->token.type == LEXER_THREE_DOTS) { opcode = (uint16_t) (PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND)); pushed_items++; lexer_next_token (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); } #endif /* JERRY_ESNEXT */ parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { parser_flush_cbc (context_p); } if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED); } } if (pushed_items >= 64) { parser_emit_cbc_call (context_p, opcode, pushed_items); #if JERRY_ESNEXT opcode = (uint16_t) CBC_ARRAY_APPEND; #endif /* JERRY_ESNEXT */ pushed_items = 0; } } } /* parser_parse_array_literal */ #if !JERRY_ESNEXT /** * Object literal item types. */ typedef enum { PARSER_OBJECT_PROPERTY_START, /**< marks the start of the property list */ PARSER_OBJECT_PROPERTY_VALUE, /**< value property */ PARSER_OBJECT_PROPERTY_GETTER, /**< getter property */ PARSER_OBJECT_PROPERTY_SETTER, /**< setter property */ PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS, /**< both getter and setter properties are set */ } parser_object_literal_item_types_t; /** * Parse object literal. */ static void parser_append_object_literal_item (parser_context_t *context_p, /**< context */ uint16_t item_index, /**< index of the item name */ parser_object_literal_item_types_t item_type) /**< type of the item */ { parser_stack_iterator_t iterator; uint8_t *current_item_type_p; iterator.current_p = context_p->stack.first_p; iterator.current_position = context_p->stack.last_position; while (true) { current_item_type_p = iterator.current_p->bytes + iterator.current_position - 1; if (*current_item_type_p == PARSER_OBJECT_PROPERTY_START) { parser_stack_push_uint16 (context_p, item_index); parser_stack_push_uint8 (context_p, (uint8_t) item_type); return; } iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } uint32_t current_item_index = iterator.current_p->bytes[iterator.current_position - 1]; iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } current_item_index |= ((uint32_t) iterator.current_p->bytes[iterator.current_position - 1]) << 8; iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } if (current_item_index == item_index) { if (item_type == PARSER_OBJECT_PROPERTY_VALUE && *current_item_type_p == PARSER_OBJECT_PROPERTY_VALUE && !(context_p->status_flags & PARSER_IS_STRICT)) { return; } if (item_type == PARSER_OBJECT_PROPERTY_GETTER && *current_item_type_p == PARSER_OBJECT_PROPERTY_SETTER) { break; } if (item_type == PARSER_OBJECT_PROPERTY_SETTER && *current_item_type_p == PARSER_OBJECT_PROPERTY_GETTER) { break; } parser_raise_error (context_p, PARSER_ERR_OBJECT_PROPERTY_REDEFINED); } } uint8_t *last_page_p = context_p->stack.first_p->bytes; *current_item_type_p = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; if (current_item_type_p == (last_page_p + context_p->stack.last_position - 1)) { context_p->stack_top_uint8 = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; } } /* parser_append_object_literal_item */ #endif /* !JERRY_ESNEXT */ #if JERRY_ESNEXT /** Forward definition of parse array initializer. */ static void parser_parse_array_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); /** Forward definition of parse object initializer. */ static void parser_parse_object_initializer (parser_context_t *context_p, parser_pattern_flags_t flags); /** * Class literal parsing options. */ typedef enum { PARSER_CLASS_LITERAL_NO_OPTS = 0, /**< no options are provided */ PARSER_CLASS_LITERAL_CTOR_PRESENT = (1 << 0), /**< class constructor is present */ PARSER_CLASS_LITERAL_HERTIAGE_PRESENT = (1 << 1), /**< class heritage is present */ } parser_class_literal_opts_t; /** * Checks whether the current string or identifier literal is constructor * * @return true, if constructor and false otherwise */ static inline bool JERRY_ATTR_ALWAYS_INLINE parser_is_constructor_literal (parser_context_t *context_p) /**< context */ { return (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_literal_to_string (context_p, "constructor", 11)); } /* parser_is_constructor_literal */ /** * Parse class literal. * * @return true - if the class has static fields, false - otherwise */ static bool parser_parse_class_body (parser_context_t *context_p, /**< context */ parser_class_literal_opts_t opts) /**< class literal parsing options */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); lexer_literal_t *ctor_literal_p = NULL; lexer_literal_t *static_fields_literal_p = NULL; if (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT) { ctor_literal_p = lexer_construct_unused_literal (context_p); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (context_p->literal_count++)); } else if (opts & PARSER_CLASS_LITERAL_HERTIAGE_PRESENT) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR_HERITAGE); } else { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_IMPLICIT_CONSTRUCTOR); } parser_emit_cbc_ext (context_p, CBC_EXT_INIT_CLASS); bool is_static = false; size_t fields_size = 0; uint32_t computed_field_count = 0; while (true) { if (!is_static) { lexer_skip_empty_statements (context_p); } lexer_expect_object_literal_id (context_p, (LEXER_OBJ_IDENT_CLASS_IDENTIFIER | (is_static ? 0 : LEXER_OBJ_IDENT_CLASS_NO_STATIC))); if (context_p->token.type == LEXER_RIGHT_BRACE) { JERRY_ASSERT (!is_static); break; } if (context_p->token.type == LEXER_KEYW_STATIC) { JERRY_ASSERT (!is_static); is_static = true; continue; } if (!is_static && context_p->token.type == LEXER_LITERAL && parser_is_constructor_literal (context_p)) { JERRY_ASSERT (!is_static); JERRY_ASSERT (opts & PARSER_CLASS_LITERAL_CTOR_PRESENT); JERRY_ASSERT (ctor_literal_p != NULL); if (ctor_literal_p->type == LEXER_FUNCTION_LITERAL) { /* 14.5.1 */ parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS); } uint32_t constructor_status_flags = (PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER | PARSER_CLASS_CONSTRUCTOR | PARSER_LEXICAL_ENV_NEEDED); if (opts & PARSER_CLASS_LITERAL_HERTIAGE_PRESENT) { constructor_status_flags |= PARSER_ALLOW_SUPER_CALL; } if (context_p->status_flags & PARSER_INSIDE_WITH) { constructor_status_flags |= PARSER_INSIDE_WITH; } parser_flush_cbc (context_p); ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, constructor_status_flags); ctor_literal_p->u.bytecode_p = compiled_code_p; ctor_literal_p->type = LEXER_FUNCTION_LITERAL; continue; } bool is_computed = false; if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER) { uint16_t literal_index, function_literal_index; bool is_getter = (context_p->token.type == LEXER_PROPERTY_GETTER); uint32_t accessor_status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; accessor_status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER); lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); literal_index = context_p->lit_object.index; if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } else if (is_static) { if (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type) && lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) { parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } } else if (parser_is_constructor_literal (context_p)) { JERRY_ASSERT (!is_static); parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR); } function_literal_index = lexer_construct_function_object (context_p, accessor_status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); cbc_ext_opcode_t opcode; if (is_computed) { context_p->last_cbc.literal_index = function_literal_index; if (is_getter) { opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_GETTER; } else { opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_SETTER : CBC_EXT_SET_COMPUTED_SETTER; } } else { context_p->last_cbc.value = function_literal_index; if (is_getter) { opcode = is_static ? CBC_EXT_SET_STATIC_GETTER : CBC_EXT_SET_GETTER; } else { opcode = is_static ? CBC_EXT_SET_STATIC_SETTER : CBC_EXT_SET_SETTER; } } if (is_computed) { parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER_NAME : CBC_EXT_SET_COMPUTED_SETTER_NAME); parser_emit_cbc_ext (context_p, opcode); } else { parser_set_function_name (context_p, function_literal_index, literal_index, accessor_status_flags); context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); } is_static = false; continue; } uint32_t status_flags = PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER; if (context_p->token.type == LEXER_KEYW_ASYNC) { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; if (!lexer_consume_generator (context_p)) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); } } if (context_p->token.type == LEXER_MULTIPLY) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (context_p->token.type == LEXER_RIGHT_SQUARE) { is_computed = true; } else if (LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type)) { if (is_static) { if (lexer_compare_identifier_to_string (&context_p->token.lit_location, (uint8_t *) "prototype", 9)) { parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE); } } else if ((status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION)) && lexer_compare_literal_to_string (context_p, "constructor", 11)) { parser_raise_error (context_p, PARSER_ERR_INVALID_CLASS_CONSTRUCTOR); } } if (!(status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION))) { if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { /* Class field. */ if (fields_size == 0) { parser_stack_push_uint8 (context_p, PARSER_CLASS_FIELD_END); } scanner_range_t range; uint8_t class_field_type = is_static ? PARSER_CLASS_FIELD_STATIC : 0; if (!is_computed) { if (is_static && parser_is_constructor_literal (context_p)) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); } range.start_location.source_p = context_p->token.lit_location.char_p; range.start_location.line = context_p->token.line; range.start_location.column = context_p->token.column; class_field_type |= PARSER_CLASS_FIELD_NORMAL; if (context_p->token.lit_location.type == LEXER_STRING_LITERAL) { range.start_location.source_p--; } } else { if (++computed_field_count > ECMA_INTEGER_NUMBER_MAX) { parser_raise_error (context_p, PARSER_ERR_TOO_MANY_CLASS_FIELDS); } if (is_static && static_fields_literal_p == NULL) { static_fields_literal_p = lexer_construct_unused_literal (context_p); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_STATIC_COMPUTED_FIELD_FUNC, (uint16_t) (context_p->literal_count++)); } else { parser_emit_cbc_ext (context_p, (is_static ? CBC_EXT_ADD_STATIC_COMPUTED_FIELD : CBC_EXT_ADD_COMPUTED_FIELD)); } } if (lexer_consume_assign (context_p)) { class_field_type |= PARSER_CLASS_FIELD_INITIALIZED; if (context_p->next_scanner_info_p->source_p != context_p->source_p) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } if (is_computed) { scanner_get_location (&range.start_location, context_p); } JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_FIELD_INITIALIZER_END); range.source_end_p = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location.source_p; scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); parser_stack_push (context_p, &range, sizeof (scanner_range_t)); fields_size += sizeof (scanner_range_t); } else { if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && !lexer_check_next_characters (context_p, LIT_CHAR_SEMICOLON, LIT_CHAR_RIGHT_BRACE)) { lexer_next_token (context_p); parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED); } if (!is_computed) { parser_stack_push (context_p, &range.start_location, sizeof (scanner_location_t)); fields_size += sizeof (scanner_location_t); } } parser_stack_push_uint8 (context_p, class_field_type); fields_size++; is_static = false; continue; } if (!is_computed) { if (context_p->token.lit_location.type != LEXER_NUMBER_LITERAL) { JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); } else { lexer_construct_number_object (context_p, false, false); } } } uint16_t literal_index = context_p->lit_object.index; uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags | PARSER_IS_METHOD); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); if (is_computed) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); parser_emit_cbc_ext (context_p, is_static ? CBC_EXT_SET_STATIC_COMPUTED_PROPERTY : CBC_EXT_SET_COMPUTED_PROPERTY); is_static = false; continue; } parser_set_function_name (context_p, function_literal_index, literal_index, 0); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); context_p->last_cbc.value = literal_index; if (is_static) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_STATIC_PROPERTY_LITERAL); is_static = false; } else { context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; } } if (fields_size == 0) { return false; } parser_reverse_class_fields (context_p, fields_size); /* Since PARSER_IS_ARROW_FUNCTION and PARSER_CLASS_CONSTRUCTOR bits cannot * be set at the same time, this bit combination triggers class field parsing. */ if (!(context_p->stack_top_uint8 & PARSER_CLASS_FIELD_STATIC)) { lexer_literal_t *literal_p = lexer_construct_unused_literal (context_p); uint16_t function_literal_index = (uint16_t) (context_p->literal_count++); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_FIELD_INIT, function_literal_index); parser_flush_cbc (context_p); literal_p->u.bytecode_p = parser_parse_class_fields (context_p); literal_p->type = LEXER_FUNCTION_LITERAL; } bool has_static_field = false; if (context_p->stack_top_uint8 & PARSER_CLASS_FIELD_STATIC) { if (static_fields_literal_p == NULL) { static_fields_literal_p = lexer_construct_unused_literal (context_p); uint16_t function_literal_index = (uint16_t) (context_p->literal_count++); parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_STATIC_FIELD_FUNC, function_literal_index); } parser_flush_cbc (context_p); static_fields_literal_p->u.bytecode_p = parser_parse_class_fields (context_p); static_fields_literal_p->type = LEXER_FUNCTION_LITERAL; has_static_field = true; } parser_stack_pop_uint8 (context_p); return has_static_field; } /* parser_parse_class_body */ /** * Parse class statement or expression. */ void parser_parse_class (parser_context_t *context_p, /**< context */ bool is_statement) /**< true - if class is parsed as a statement * false - otherwise (as an expression) */ { JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS); uint16_t class_ident_index = PARSER_INVALID_LITERAL_INDEX; uint16_t class_name_index = PARSER_INVALID_LITERAL_INDEX; parser_class_literal_opts_t opts = PARSER_CLASS_LITERAL_NO_OPTS; if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CLASS_CONSTRUCTOR); scanner_release_next (context_p, sizeof (scanner_info_t)); opts |= PARSER_CLASS_LITERAL_CTOR_PRESENT; } if (is_statement) { /* Class statement must contain an identifier. */ 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); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } class_ident_index = context_p->lit_object.index; lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; class_name_index = context_p->lit_object.index; #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); context_p->status_flags &= (uint32_t) ~PARSER_MODULE_STORE_IDENT; #endif /* JERRY_MODULE_SYSTEM */ lexer_next_token (context_p); } else { lexer_next_token (context_p); /* Class expression may contain an identifier. */ if (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_NEW_IDENT_LITERAL); context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_USED; class_name_index = context_p->lit_object.index; lexer_next_token (context_p); } } if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { if (JERRY_UNLIKELY (context_p->scope_stack_top >= context_p->scope_stack_size)) { JERRY_ASSERT (context_p->scope_stack_size == PARSER_MAXIMUM_DEPTH_OF_SCOPE_STACK); parser_raise_error (context_p, PARSER_ERR_SCOPE_STACK_LIMIT_REACHED); } parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top; PARSER_PLUS_EQUAL_U16 (context_p->scope_stack_top, 1); scope_stack_p->map_from = class_name_index; scope_stack_p->map_to = 0; parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_NAMED_CLASS_ENV, class_name_index); } else { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } bool is_strict = (context_p->status_flags & PARSER_IS_STRICT) != 0; /* 14.5. A ClassBody is always strict code. */ context_p->status_flags |= PARSER_IS_STRICT; if (context_p->token.type == LEXER_KEYW_EXTENDS) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR | PARSE_EXPR_LEFT_HAND_SIDE); opts |= PARSER_CLASS_LITERAL_HERTIAGE_PRESENT; } else { /* Elisions represents that the classHeritage is not present */ parser_emit_cbc (context_p, CBC_PUSH_ELISION); } if (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } /* ClassDeclaration is parsed. Continue with class body. */ bool has_static_field = parser_parse_class_body (context_p, opts); if (class_name_index != PARSER_INVALID_LITERAL_INDEX) { parser_emit_cbc_ext_literal (context_p, CBC_EXT_FINALIZE_NAMED_CLASS, class_name_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, class_name_index); PARSER_MINUS_EQUAL_U16 (context_p->scope_stack_top, 1); } else { parser_emit_cbc_ext (context_p, CBC_EXT_FINALIZE_ANONYMOUS_CLASS); } if (has_static_field) { parser_emit_cbc_ext (context_p, CBC_EXT_RUN_STATIC_FIELD_INIT); } if (is_statement) { cbc_opcode_t opcode = CBC_MOV_IDENT; if (class_ident_index < PARSER_REGISTER_START) { opcode = (scanner_literal_is_created (context_p, class_ident_index) ? CBC_ASSIGN_LET_CONST : CBC_INIT_LET); } parser_emit_cbc_literal (context_p, (uint16_t) opcode, class_ident_index); parser_flush_cbc (context_p); } if (!is_strict) { /* Restore flag */ context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT; } context_p->status_flags &= (uint32_t) ~PARSER_ALLOW_SUPER; lexer_next_token (context_p); } /* parser_parse_class */ #endif /* JERRY_ESNEXT */ #if JERRY_ESNEXT /** * Parse object initializer method definition. * * See also: ES2015 14.3 */ static void parser_parse_object_method (parser_context_t *context_p) /**< context */ { context_p->source_p--; context_p->column--; uint16_t function_literal_index = lexer_construct_function_object (context_p, (PARSER_FUNCTION_CLOSURE | PARSER_ALLOW_SUPER | PARSER_IS_METHOD)); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; lexer_next_token (context_p); } /* parser_parse_object_method */ /** * Reparse the current literal as a common identifier. */ static void parser_reparse_as_common_identifier (parser_context_t *context_p, /**< context */ parser_line_counter_t start_line, /**< start line */ parser_line_counter_t start_column) /**< start column */ { /* context_p->token.lit_location.char_p is showing the character after the string start, so it is not suitable for reparsing as identifier. e.g.: { 'foo' } */ if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } context_p->source_p = context_p->token.lit_location.char_p; context_p->line = start_line; context_p->column = start_column; lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); } /* parser_reparse_as_common_identifier */ #endif /* JERRY_ESNEXT */ /** * Parse object literal. */ static void parser_parse_object_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); parser_emit_cbc (context_p, CBC_CREATE_OBJECT); #if !JERRY_ESNEXT parser_stack_push_uint8 (context_p, PARSER_OBJECT_PROPERTY_START); #endif /* !JERRY_ESNEXT */ #if JERRY_ESNEXT bool proto_seen = false; bool has_super_env = false; if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_SUPER) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_OBJECT_SUPER_ENVIRONMENT); has_super_env = true; } scanner_release_next (context_p, sizeof (scanner_info_t)); } #endif /* JERRY_ESNEXT */ while (true) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS); switch (context_p->token.type) { case LEXER_RIGHT_BRACE: { break; } case LEXER_PROPERTY_GETTER: case LEXER_PROPERTY_SETTER: { uint32_t status_flags; cbc_ext_opcode_t opcode; #if !JERRY_ESNEXT parser_object_literal_item_types_t item_type; #endif /* !JERRY_ESNEXT */ bool is_getter = context_p->token.type == LEXER_PROPERTY_GETTER; if (is_getter) { status_flags = PARSER_FUNCTION_CLOSURE | PARSER_IS_PROPERTY_GETTER; opcode = CBC_EXT_SET_GETTER; #if !JERRY_ESNEXT item_type = PARSER_OBJECT_PROPERTY_GETTER; #endif /* !JERRY_ESNEXT */ } else { status_flags = PARSER_FUNCTION_CLOSURE | PARSER_IS_PROPERTY_SETTER; opcode = CBC_EXT_SET_SETTER; #if !JERRY_ESNEXT item_type = PARSER_OBJECT_PROPERTY_SETTER; #endif /* !JERRY_ESNEXT */ } #if JERRY_ESNEXT status_flags |= PARSER_ALLOW_SUPER; #endif /* !JERRY_ESNEXT */ lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); /* This assignment is a nop for computed getters/setters. */ uint16_t literal_index = context_p->lit_object.index; #if JERRY_ESNEXT bool is_computed = context_p->token.type == LEXER_RIGHT_SQUARE; if (is_computed) { opcode = ((opcode == CBC_EXT_SET_GETTER) ? CBC_EXT_SET_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_SETTER); } #else /* !JERRY_ESNEXT */ parser_append_object_literal_item (context_p, literal_index, item_type); #endif /* JERRY_ESNEXT */ uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags); #if JERRY_ESNEXT if (opcode >= CBC_EXT_SET_COMPUTED_GETTER) { literal_index = function_literal_index; } #endif /* JERRY_ESNEXT */ parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); #if JERRY_ESNEXT if (is_computed) { parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER_NAME : CBC_EXT_SET_COMPUTED_SETTER_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, opcode); lexer_next_token (context_p); break; } parser_set_function_name (context_p, function_literal_index, literal_index, status_flags); if (has_super_env) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = function_literal_index; parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); parser_emit_cbc_ext (context_p, is_getter ? CBC_EXT_SET_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_SETTER); } else #endif /* JERRY_ESNEXT */ { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); context_p->last_cbc.value = function_literal_index; } lexer_next_token (context_p); break; } #if JERRY_ESNEXT case LEXER_RIGHT_SQUARE: { lexer_next_token (context_p); if (context_p->token.type == LEXER_LEFT_PAREN) { parser_parse_object_method (context_p); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); if (parser_check_anonymous_function_declaration (context_p) < PARSER_NAMED_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_PROPERTY); } else { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL); } break; } if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (parser_check_anonymous_function_declaration (context_p) < PARSER_NAMED_FUNCTION) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_PROPERTY); } break; } case LEXER_THREE_DOTS: { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_ext (context_p, CBC_EXT_COPY_DATA_PROPERTIES); break; } case LEXER_KEYW_ASYNC: case LEXER_MULTIPLY: { uint32_t status_flags = PARSER_FUNCTION_CLOSURE; if (context_p->token.type == LEXER_KEYW_ASYNC) { status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; lexer_consume_generator (context_p); } if (context_p->token.type == LEXER_MULTIPLY) { status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (has_super_env) { status_flags |= PARSER_ALLOW_SUPER; } lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS); uint16_t opcode = CBC_SET_LITERAL_PROPERTY; /* This assignment is a nop for CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL. */ uint16_t literal_index = context_p->lit_object.index; bool is_computed = context_p->token.type == LEXER_RIGHT_SQUARE; if (is_computed) { opcode = CBC_EXT_SET_COMPUTED_PROPERTY; } uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); if (is_computed) { parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_FUNCTION_NAME); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT_COMPUTED); } parser_emit_cbc_ext (context_p, opcode); lexer_next_token (context_p); break; } parser_set_function_name (context_p, function_literal_index, literal_index, status_flags); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT); parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); } else { context_p->last_cbc_opcode = opcode; context_p->last_cbc.value = literal_index; } lexer_next_token (context_p); break; } #endif /* JERRY_ESNEXT */ default: { #if JERRY_ESNEXT const lexer_lit_location_t *literal_p = (const lexer_lit_location_t *) context_p->lit_object.literal_p; bool is_proto = ((context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL) && lexer_compare_identifier_to_string (literal_p, (uint8_t *) "__proto__", 9) && lexer_check_next_character (context_p, LIT_CHAR_COLON)); if (is_proto) { if (proto_seen) { parser_raise_error (context_p, PARSER_ERR_DUPLICATED_PROTO); } proto_seen = true; } #endif /* JERRY_ESNEXT */ uint16_t literal_index = context_p->lit_object.index; #if JERRY_ESNEXT parser_line_counter_t start_line = context_p->token.line; parser_line_counter_t start_column = context_p->token.column; #else /* !JERRY_ESNEXT */ parser_append_object_literal_item (context_p, literal_index, PARSER_OBJECT_PROPERTY_VALUE); #endif /* JERRY_ESNEXT */ lexer_next_token (context_p); #if JERRY_ESNEXT if (context_p->token.type == LEXER_LEFT_PAREN && !is_proto) { parser_parse_object_method (context_p); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); parser_set_function_name (context_p, context_p->last_cbc.literal_index, literal_index, 0); if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_OBJECT_LITERAL_SET_HOME_OBJECT); parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); break; } context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; break; } if ((context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_COMMA) && !is_proto) { parser_reparse_as_common_identifier (context_p, start_line, start_column); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; lexer_next_token (context_p); break; } #endif /* JERRY_ESNEXT */ if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); #if JERRY_ESNEXT if (is_proto) { parser_emit_cbc_ext (context_p, CBC_EXT_SET__PROTO__); break; } #endif /* JERRY_ESNEXT */ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { #if JERRY_ESNEXT if (context_p->last_cbc.literal_type == LEXER_FUNCTION_LITERAL) { parser_set_function_name (context_p, context_p->last_cbc.literal_index, literal_index, 0); } #endif /* JERRY_ESNEXT */ context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY; context_p->last_cbc.value = literal_index; } else { #if JERRY_ESNEXT if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_FINALIZE_ANONYMOUS_CLASS)) { uint16_t name_index = scanner_save_literal (context_p, literal_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } #endif /* JERRY_ESNEXT */ parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); } break; } } if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } else if (context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } } #if !JERRY_ESNEXT while (context_p->stack_top_uint8 != PARSER_OBJECT_PROPERTY_START) { parser_stack_pop (context_p, NULL, 3); } parser_stack_pop_uint8 (context_p); #else /* JERRY_ESNEXT */ if (has_super_env) { parser_emit_cbc_ext (context_p, CBC_EXT_POP_OBJECT_SUPER_ENVIRONMENT); } #endif /* !JERRY_ESNEXT */ } /* parser_parse_object_literal */ /** * Parse function literal. */ static void parser_parse_function_expression (parser_context_t *context_p, /**< context */ uint32_t status_flags) /**< function status flags */ { int literals = 0; uint16_t literal1 = 0; uint16_t literal2 = 0; uint16_t function_literal_index; int32_t function_name_index = -1; #if !JERRY_ESNEXT JERRY_ASSERT (status_flags & PARSER_IS_FUNC_EXPRESSION); #endif /* !JERRY_ESNEXT */ #if JERRY_ESNEXT if (status_flags & PARSER_IS_FUNC_EXPRESSION) { #endif /* !JERRY_ESNEXT */ #if JERRY_DEBUGGER parser_line_counter_t debugger_line = context_p->token.line; parser_line_counter_t debugger_column = context_p->token.column; #endif /* JERRY_DEBUGGER */ #if JERRY_ESNEXT uint32_t parent_status_flags = context_p->status_flags; context_p->status_flags &= (uint32_t) ~(PARSER_IS_ASYNC_FUNCTION | PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); if (status_flags & PARSER_IS_ASYNC_FUNCTION) { /* The name of the function cannot be await. */ context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } if (lexer_consume_generator (context_p)) { /* The name of the function cannot be yield. */ context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; } #endif /* JERRY_ESNEXT */ if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { lexer_next_token (context_p); if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } parser_flush_cbc (context_p); lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); #if JERRY_DEBUGGER if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME, JERRY_DEBUGGER_NO_SUBTYPE, context_p->lit_object.literal_p->u.char_p, context_p->lit_object.literal_p->prop.length); /* Reset token position for the function. */ context_p->token.line = debugger_line; context_p->token.column = debugger_column; } #endif /* JERRY_DEBUGGER */ if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS) { status_flags |= PARSER_HAS_NON_STRICT_ARG; } function_name_index = context_p->lit_object.index; } #if JERRY_ESNEXT context_p->status_flags = parent_status_flags; } #endif /* JERRY_ESNEXT */ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { literals = 1; literal1 = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { literals = 2; literal1 = context_p->last_cbc.literal_index; literal2 = context_p->last_cbc.value; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } function_literal_index = lexer_construct_function_object (context_p, status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); #if JERRY_ESNEXT if (function_name_index != -1) { parser_set_function_name (context_p, function_literal_index, (uint16_t) function_name_index, 0); } #endif /* JERRY_ESNEXT */ if (literals == 1) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = function_literal_index; } else if (literals == 2) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = literal2; context_p->last_cbc.third_literal_index = function_literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); if (function_name_index != -1) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION); context_p->last_cbc.value = (uint16_t) function_name_index; } } context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; } /* parser_parse_function_expression */ #if JERRY_ESNEXT /** * Parse template literal. */ static void parser_parse_template_literal (parser_context_t *context_p) /**< context */ { bool is_empty_head = true; if (context_p->token.lit_location.length > 0) { is_empty_head = false; lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } if (!is_empty_head) { if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS); } else if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT); } } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); if (is_empty_head || context_p->token.lit_location.length > 0) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS); context_p->last_cbc.value = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; } else { parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } } while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } else { parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT); } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); if (context_p->token.lit_location.length > 0) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL); } } } /* parser_parse_template_literal */ /** * Parse tagged template literal. */ static size_t parser_parse_tagged_template_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_TEMPLATE_LITERAL); uint32_t call_arguments = 0; ecma_collection_t *collection_p; if (context_p->tagged_template_literal_cp == JMEM_CP_NULL) { collection_p = ecma_new_collection (); ECMA_SET_INTERNAL_VALUE_POINTER (context_p->tagged_template_literal_cp, collection_p); } else { collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_p->tagged_template_literal_cp); if (collection_p->item_count > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } } const uint32_t tagged_id = collection_p->item_count; uint32_t prop_idx = 0; ecma_object_t *raw_strings_p; ecma_object_t *template_obj_p = parser_new_tagged_template_literal (&raw_strings_p); ecma_collection_push_back (collection_p, ecma_make_object_value (template_obj_p)); parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++); call_arguments++; parser_emit_cbc_ext_call (context_p, CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, tagged_id); while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LEFT_BRACE); lexer_next_token (context_p); if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } context_p->source_p--; context_p->column--; lexer_parse_string (context_p, LEXER_STRING_NO_OPTS); parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++); } parser_tagged_template_literal_finalize (template_obj_p, raw_strings_p); return call_arguments; } /* parser_parse_tagged_template_literal */ /** * Checks wheteher the current expression can be an assignment expression. * * @return true if the current expression can be an assignment expression, false otherwise */ static inline bool JERRY_ATTR_ALWAYS_INLINE parser_is_assignment_expr (parser_context_t *context_p) { return (context_p->stack_top_uint8 == LEXER_EXPRESSION_START || context_p->stack_top_uint8 == LEXER_LEFT_PAREN || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST || LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->stack_top_uint8)); } /* parser_is_assignment_expr */ /** * Throws an error if the current expression is not an assignment expression. */ static inline void JERRY_ATTR_ALWAYS_INLINE parser_check_assignment_expr (parser_context_t *context_p) { if (!parser_is_assignment_expr (context_p)) { parser_raise_error (context_p, PARSER_ERR_ASSIGNMENT_EXPECTED); } } /* parser_check_assignment_expr */ /** * Checks whether the next token is a valid continuation token after an AssignmentExpression. */ static inline bool JERRY_ATTR_ALWAYS_INLINE parser_abort_parsing_after_assignment_expression (parser_context_t *context_p) { return (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_COMMA); } /* parser_abort_parsing_after_assignment_expression */ #endif /* JERRY_ESNEXT */ /** * Parse and record unary operators, and parse the primary literal. * * @return true if parsing should be aborted, true otherwise */ static bool parser_parse_unary_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { bool new_was_seen = false; /* Collect unary operators. */ while (true) { /* Convert plus and minus binary operators to unary operators. */ switch (context_p->token.type) { case LEXER_ADD: { context_p->token.type = LEXER_PLUS; break; } case LEXER_SUBTRACT: { context_p->token.type = LEXER_NEGATE; break; } #if JERRY_ESNEXT case LEXER_KEYW_AWAIT: { #if JERRY_MODULE_SYSTEM if ((context_p->global_status_flags & ECMA_PARSE_MODULE) && !(context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)) { parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED); } #endif /* JERRY_MODULE_SYSTEM */ if (JERRY_UNLIKELY (context_p->token.lit_location.has_escape)) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } break; } #endif /* JERRY_ESNEXT */ } /* Bracketed expressions are primary expressions. At this * point their left paren is pushed onto the stack and * they are processed when their closing paren is reached. */ if (context_p->token.type == LEXER_LEFT_PAREN) { #if JERRY_ESNEXT if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); break; } #endif /* JERRY_ESNEXT */ (*grouping_level_p) += PARSER_GROUPING_LEVEL_INCREASE; new_was_seen = false; } else if (context_p->token.type == LEXER_KEYW_NEW) { /* After 'new' unary operators are not allowed. */ new_was_seen = true; #if JERRY_ESNEXT /* Check if "new.target" is written here. */ if (scanner_try_scan_new_target (context_p)) { if (!(context_p->status_flags & PARSER_ALLOW_NEW_TARGET)) { parser_raise_error (context_p, PARSER_ERR_NEW_TARGET_NOT_ALLOWED); } parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_NEW_TARGET); lexer_next_token (context_p); /* Found "new.target" return here */ return false; } #endif /* JERRY_ESNEXT */ } else if (new_was_seen || (*grouping_level_p == PARSE_EXPR_LEFT_HAND_SIDE) || !LEXER_IS_UNARY_OP_TOKEN (context_p->token.type)) { break; } parser_stack_push_uint8 (context_p, context_p->token.type); lexer_next_token (context_p); } /* Parse primary expression. */ switch (context_p->token.type) { #if JERRY_ESNEXT case LEXER_TEMPLATE_LITERAL: { if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) { parser_parse_template_literal (context_p); break; } /* The string is a normal string literal. */ /* FALLTHRU */ } #endif /* JERRY_ESNEXT */ case LEXER_LITERAL: { #if JERRY_ESNEXT if (JERRY_UNLIKELY (context_p->next_scanner_info_p->source_p == context_p->source_p)) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); if (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC) { JERRY_ASSERT (lexer_token_is_async (context_p)); JERRY_ASSERT (!(context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_STATEMENT)); uint32_t saved_status_flags = context_p->status_flags; context_p->status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD; lexer_next_token (context_p); context_p->status_flags = saved_status_flags; if (context_p->token.type == LEXER_KEYW_FUNCTION) { uint32_t status_flags = (PARSER_FUNCTION_CLOSURE | PARSER_IS_FUNC_EXPRESSION | PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); parser_parse_function_expression (context_p, status_flags); break; } arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD); } parser_check_assignment_expr (context_p); parser_parse_function_expression (context_p, arrow_status_flags); return parser_abort_parsing_after_assignment_expression (context_p); } #endif /* JERRY_ESNEXT */ 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); } else if (type == LEXER_NUMBER_LITERAL) { bool is_negative_number = false; if ((context_p->stack_top_uint8 == LEXER_PLUS || context_p->stack_top_uint8 == LEXER_NEGATE) && !lexer_check_post_primary_exp (context_p)) { do { if (context_p->stack_top_uint8 == LEXER_NEGATE) { is_negative_number = !is_negative_number; } #if JERRY_BUILTIN_BIGINT else if (JERRY_LIKELY (context_p->token.extra_value == LEXER_NUMBER_BIGINT)) { break; } #endif /* JERRY_BUILTIN_BIGINT */ parser_stack_pop_uint8 (context_p); } while (context_p->stack_top_uint8 == LEXER_PLUS || context_p->stack_top_uint8 == LEXER_NEGATE); } if (lexer_construct_number_object (context_p, true, is_negative_number)) { JERRY_ASSERT (context_p->lit_object.index <= CBC_PUSH_NUMBER_BYTE_RANGE_END); parser_emit_cbc_push_number (context_p, is_negative_number); break; } } cbc_opcode_t opcode = CBC_PUSH_LITERAL; if (context_p->token.keyword_type != LEXER_KEYW_EVAL) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_PUSH_THIS_LITERAL; } } parser_emit_cbc_literal_from_token (context_p, (uint16_t) opcode); break; } case LEXER_KEYW_FUNCTION: { parser_parse_function_expression (context_p, PARSER_FUNCTION_CLOSURE | PARSER_IS_FUNC_EXPRESSION); break; } case LEXER_LEFT_BRACE: { #if JERRY_ESNEXT if (context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (parser_is_assignment_expr (context_p)) { uint32_t flags = PARSER_PATTERN_NO_OPTS; if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } parser_parse_object_initializer (context_p, flags); return parser_abort_parsing_after_assignment_expression (context_p); } scanner_release_next (context_p, sizeof (scanner_location_info_t)); } #endif /* JERRY_ESNEXT */ parser_parse_object_literal (context_p); break; } case LEXER_LEFT_SQUARE: { #if JERRY_ESNEXT if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (parser_is_assignment_expr (context_p)) { parser_parse_array_initializer (context_p, PARSER_PATTERN_NO_OPTS); return parser_abort_parsing_after_assignment_expression (context_p); } scanner_release_next (context_p, sizeof (scanner_location_info_t)); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS && context_p->next_scanner_info_p->u8_arg == SCANNER_LITERAL_NO_DESTRUCTURING); scanner_release_next (context_p, sizeof (scanner_info_t)); } } #endif /* JERRY_ESNEXT */ parser_parse_array_literal (context_p); break; } case LEXER_DIVIDE: case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, false); uint16_t literal_index = (uint16_t) (context_p->literal_count - 1); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = literal_index; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); } context_p->last_cbc.literal_type = LEXER_REGEXP_LITERAL; context_p->last_cbc.literal_keyword_type = LEXER_EOS; break; } case LEXER_KEYW_THIS: { #if JERRY_ESNEXT if (context_p->status_flags & PARSER_ALLOW_SUPER_CALL) { parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_LEXICAL_THIS); } else { #endif /* JERRY_ESNEXT */ parser_emit_cbc (context_p, CBC_PUSH_THIS); #if JERRY_ESNEXT } #endif /* JERRY_ESNEXT */ break; } case LEXER_LIT_TRUE: { parser_emit_cbc (context_p, CBC_PUSH_TRUE); break; } case LEXER_LIT_FALSE: { parser_emit_cbc (context_p, CBC_PUSH_FALSE); break; } case LEXER_LIT_NULL: { parser_emit_cbc (context_p, CBC_PUSH_NULL); break; } #if JERRY_ESNEXT case LEXER_KEYW_CLASS: { parser_parse_class (context_p, false); return false; } case LEXER_KEYW_SUPER: { if (context_p->status_flags & PARSER_ALLOW_SUPER) { if (lexer_check_next_characters (context_p, LIT_CHAR_DOT, LIT_CHAR_LEFT_SQUARE)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER); break; } if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN) && (context_p->status_flags & PARSER_ALLOW_SUPER_CALL)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER_CONSTRUCTOR); break; } } parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_SUPER_KEYWORD); } 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_FUNCTION); parser_check_assignment_expr (context_p); uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); parser_parse_function_expression (context_p, arrow_status_flags); return parser_abort_parsing_after_assignment_expression (context_p); } case LEXER_KEYW_YIELD: { JERRY_ASSERT ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) && !(context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD)); if (context_p->token.lit_location.has_escape) { parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD); } parser_check_assignment_expr (context_p); lexer_next_token (context_p); cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) ? CBC_EXT_ASYNC_YIELD : CBC_EXT_YIELD); if (!lexer_check_yield_no_arg (context_p)) { if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); opcode = ((context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) ? CBC_EXT_ASYNC_YIELD_ITERATOR : CBC_EXT_YIELD_ITERATOR); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); } else { parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); } parser_emit_cbc_ext (context_p, opcode); return (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_COMMA); } #endif /* JERRY_ESNEXT */ default: { bool is_left_hand_side = (*grouping_level_p == PARSE_EXPR_LEFT_HAND_SIDE); parser_raise_error (context_p, (is_left_hand_side ? PARSER_ERR_LEFT_HAND_SIDE_EXP_EXPECTED : PARSER_ERR_PRIMARY_EXP_EXPECTED)); break; } } lexer_next_token (context_p); return false; } /* parser_parse_unary_expression */ /** * Parse the postfix part of unary operators, and * generate byte code for the whole expression. */ static void parser_process_unary_expression (parser_context_t *context_p, /**< context */ size_t grouping_level) /**< grouping level */ { /* Parse postfix part of a primary expression. */ while (true) { /* Since break would only break the switch, we use * continue to continue this loop. Without continue, * the code abandons the loop. */ switch (context_p->token.type) { case LEXER_DOT: { parser_push_result (context_p); lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_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) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; context_p->last_cbc.value = context_p->lit_object.index; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL); } #if JERRY_ESNEXT else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL); context_p->last_cbc.literal_index = context_p->lit_object.index; } #endif /* JERRY_ESNEXT */ else { parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL); } lexer_next_token (context_p); continue; } case LEXER_LEFT_SQUARE: { parser_push_result (context_p); #if JERRY_ESNEXT uint16_t last_cbc_opcode = context_p->last_cbc_opcode; if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } #endif /* JERRY_ESNEXT */ lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); } lexer_next_token (context_p); #if JERRY_ESNEXT if (last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER)) { parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SUPER_PROP); continue; } #endif /* JERRY_ESNEXT */ if (PARSER_IS_MUTABLE_PUSH_LITERAL (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_LITERAL_TO_PUSH_PROP_LITERAL (context_p->last_cbc_opcode); } else { parser_emit_cbc (context_p, CBC_PUSH_PROP); } continue; } #if JERRY_ESNEXT case LEXER_TEMPLATE_LITERAL: #endif /* JERRY_ESNEXT */ case LEXER_LEFT_PAREN: { size_t call_arguments = 0; uint16_t opcode = CBC_CALL; bool is_eval = false; parser_push_result (context_p); if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { #if JERRY_ESNEXT if (context_p->token.type == LEXER_LEFT_PAREN) #endif /* JERRY_ESNEXT */ { parser_stack_pop_uint8 (context_p); opcode = CBC_NEW; } } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_keyword_type == LEXER_KEYW_EVAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { is_eval = true; } if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { opcode = CBC_CALL_PROP; context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode); } #if JERRY_ESNEXT else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_CONSTRUCTOR)) { opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL); } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE); opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE); opcode = CBC_CALL_PROP; } #endif /* JERRY_ESNEXT */ else if (JERRY_UNLIKELY (context_p->status_flags & PARSER_INSIDE_WITH) && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { opcode = CBC_CALL_PROP; parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE); parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_BASE); } } #if JERRY_ESNEXT bool has_spread_element = false; if (context_p->token.type == LEXER_TEMPLATE_LITERAL) { call_arguments = parser_parse_tagged_template_literal (context_p); } else { lexer_next_token (context_p); while (context_p->token.type != LEXER_RIGHT_PAREN) { if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } if (context_p->token.type == LEXER_THREE_DOTS) { has_spread_element = true; call_arguments++; parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); lexer_next_token (context_p); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); continue; } if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } break; } } #else /* !JERRY_ESNEXT */ lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) { while (true) { if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type != LEXER_COMMA) { break; } lexer_next_token (context_p); } if (context_p->token.type != LEXER_RIGHT_PAREN) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } } #endif /* JERRY_ESNEXT */ lexer_next_token (context_p); if (is_eval) { context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; #if JERRY_ESNEXT uint16_t eval_flags = PARSER_SAVE_STATUS_FLAGS (context_p->status_flags); const uint32_t required_flags = PARSER_IS_FUNCTION | PARSER_LEXICAL_BLOCK_NEEDED; if (context_p->status_flags & PARSER_FUNCTION_IS_PARSING_ARGS) { context_p->status_flags |= PARSER_LEXICAL_BLOCK_NEEDED; } else if (((context_p->status_flags & (required_flags | PARSER_IS_STRICT)) == required_flags) || ((context_p->global_status_flags & ECMA_PARSE_FUNCTION_CONTEXT) && !(context_p->status_flags & PARSER_IS_FUNCTION))) { eval_flags |= PARSER_GET_EVAL_FLAG (ECMA_PARSE_FUNCTION_CONTEXT); } if (eval_flags != 0) { parser_emit_cbc_ext_call (context_p, CBC_EXT_LOCAL_EVAL, eval_flags); } else { #endif /* JERRY_ESNEXT */ parser_emit_cbc (context_p, CBC_EVAL); #if JERRY_ESNEXT } #endif /* JERRY_ESNEXT */ } #if JERRY_ESNEXT if (has_spread_element) { uint16_t spread_opcode; if (opcode == CBC_CALL) { spread_opcode = CBC_EXT_SPREAD_CALL; } else if (opcode == CBC_CALL_PROP) { spread_opcode = CBC_EXT_SPREAD_CALL_PROP; } else if (opcode == CBC_NEW) { spread_opcode = CBC_EXT_SPREAD_NEW; } else { /* opcode is unchanged */ JERRY_ASSERT (opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL)); spread_opcode = CBC_EXT_SPREAD_SUPER_CALL; } parser_emit_cbc_ext_call (context_p, spread_opcode, call_arguments); continue; } #endif /* JERRY_ESNEXT */ if (call_arguments <= 1) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0 + (call_arguments * 6))); continue; } if (opcode == CBC_CALL_PROP) { parser_emit_cbc (context_p, (uint16_t) (CBC_CALL0_PROP + (call_arguments * 6))); continue; } if (opcode == CBC_NEW) { parser_emit_cbc (context_p, (uint16_t) (CBC_NEW0 + call_arguments)); continue; } } else if (call_arguments == 2) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, CBC_CALL2); continue; } if (opcode == CBC_CALL_PROP) { parser_flush_cbc (context_p); /* Manually adjusting stack usage. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; parser_emit_cbc (context_p, CBC_CALL2_PROP); continue; } } parser_emit_cbc_call (context_p, opcode, call_arguments); continue; } default: { if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { parser_push_result (context_p); parser_emit_cbc (context_p, CBC_NEW0); parser_stack_pop_uint8 (context_p); continue; } if (!(context_p->token.flags & LEXER_WAS_NEWLINE) && (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE) && grouping_level != PARSE_EXPR_LEFT_HAND_SIDE) { cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR; parser_push_result (context_p); parser_emit_unary_lvalue_opcode (context_p, opcode); lexer_next_token (context_p); } break; } } break; } #if JERRY_ESNEXT uint8_t last_unary_token = LEXER_INCREASE; #endif /* JERRY_ESNEXT */ /* Generate byte code for the unary operators. */ while (true) { uint8_t token = context_p->stack_top_uint8; if (!LEXER_IS_UNARY_OP_TOKEN (token)) { #if JERRY_ESNEXT if (context_p->token.type == LEXER_EXPONENTIATION && last_unary_token != LEXER_INCREASE && last_unary_token != LEXER_DECREASE) { parser_raise_error (context_p, PARSER_ERR_INVALID_EXPONENTIATION); } #endif /* JERRY_ESNEXT */ break; } #if JERRY_ESNEXT last_unary_token = token; #endif /* JERRY_ESNEXT */ parser_push_result (context_p); parser_stack_pop_uint8 (context_p); if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token)) { if (token == LEXER_KEYW_DELETE) { token = CBC_DELETE_PUSH_RESULT; } else { token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token)); } parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token); } #if JERRY_ESNEXT else if (JERRY_UNLIKELY (token == LEXER_KEYW_AWAIT)) { cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) ? CBC_EXT_GENERATOR_AWAIT : CBC_EXT_AWAIT); parser_emit_cbc_ext (context_p, opcode); } #endif /* JERRY_ESNEXT */ else { token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token)); if (token == CBC_TYPEOF) { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_emit_ident_reference (context_p, CBC_TYPEOF_IDENT); } else { parser_emit_cbc (context_p, token); } } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { /* It is not worth to combine with push multiple literals * since the byte code size will not decrease. */ JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1)); context_p->last_cbc_opcode = (uint16_t) (token + 1); } else { parser_emit_cbc (context_p, token); } } } } } /* parser_process_unary_expression */ /** * Append a binary '=' token. * * @return - pushed assignment opcode onto the parser stack */ static void parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< context */ uint32_t pattern_flags) /**< pattern flags */ { JERRY_UNUSED (pattern_flags); /* Unlike other tokens, the whole byte code is saved for binary * assignment, since it has multiple forms depending on the * previous instruction. */ uint8_t assign_opcode = CBC_ASSIGN; if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); uint16_t literal_index; switch (context_p->last_cbc_opcode) { case CBC_PUSH_LITERAL: { literal_index = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; break; } case CBC_PUSH_TWO_LITERALS: { literal_index = context_p->last_cbc.value; context_p->last_cbc_opcode = CBC_PUSH_LITERAL; break; } case CBC_PUSH_THIS_LITERAL: { literal_index = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = CBC_PUSH_THIS; parser_flush_cbc (context_p); break; } default: { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); literal_index = context_p->last_cbc.third_literal_index; context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; break; } } assign_opcode = CBC_ASSIGN_SET_IDENT; #if JERRY_ESNEXT if (!(pattern_flags & (PARSER_PATTERN_LET | PARSER_PATTERN_CONST | PARSER_PATTERN_LOCAL))) { if (scanner_literal_is_const_reg (context_p, literal_index)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); } } else if (literal_index < PARSER_REGISTER_START) { assign_opcode = CBC_INIT_LET; if (scanner_literal_is_created (context_p, literal_index)) { assign_opcode = CBC_ASSIGN_LET_CONST; } else if (pattern_flags & PARSER_PATTERN_CONST) { assign_opcode = CBC_INIT_CONST; } else if (pattern_flags & PARSER_PATTERN_LOCAL) { assign_opcode = CBC_INIT_ARG_OR_CATCH; } } #endif /* JERRY_ESNEXT */ parser_stack_push_uint16 (context_p, literal_index); JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, assign_opcode)); } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, 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) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_ASSIGN_PROP_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); assign_opcode = CBC_ASSIGN_PROP_LITERAL; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; } } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_ASSIGN_PROP_THIS_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); assign_opcode = CBC_ASSIGN_PROP_THIS_LITERAL; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; } } #if JERRY_ESNEXT else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE); parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_SUPER); assign_opcode = CBC_EXT_OPCODE; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE); parser_stack_push_uint8 (context_p, CBC_EXT_ASSIGN_SUPER); assign_opcode = CBC_EXT_OPCODE; } #endif /* JERRY_ESNEXT */ else { /* Invalid LeftHandSide expression. */ //3820, 3815 #if JERRY_ESNEXT parser_check_invalid_new_target (context_p, CBC_ASSIGN); parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); #else /* !JERRY_ESNEXT */ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); #endif /* JERRY_ESNEXT */ } parser_stack_push_uint8 (context_p, assign_opcode); parser_stack_push_uint8 (context_p, LEXER_ASSIGN); } /* parser_append_binary_single_assignment_token */ #if JERRY_ESNEXT /** * Check for invalid chain of logical operators */ static void parser_check_invalid_logical_op (parser_context_t *context_p, /**< context */ uint8_t invalid_token1, /**< token id of first invalid token */ uint8_t invalid_token2) /**< token id of second invalid token */ { parser_stack_iterator_t iterator; parser_stack_iterator_init (context_p, &iterator); while (true) { uint8_t token = parser_stack_iterator_read_uint8 (&iterator); if (!LEXER_IS_BINARY_NON_LVALUE_OP_TOKEN (token)) { return; } if (token == invalid_token1 || token == invalid_token2) { parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING); } /* If a logical operator is found, and there is no SyntaxError, the scan can be terminated * since there was no SyntaxError when the logical operator was pushed onto the stack. */ if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND || token == LEXER_NULLISH_COALESCING) { return; } parser_stack_iterator_skip (&iterator, sizeof (uint8_t)); } } /* parser_check_invalid_logical_op */ #endif /* JERRY_ESNEXT */ /** * Append a binary token. */ static void parser_append_binary_token (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)); parser_push_result (context_p); if (context_p->token.type == LEXER_ASSIGN) { parser_append_binary_single_assignment_token (context_p, 0); return; } if (LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->token.type)) { if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { parser_check_invalid_assign (context_p); parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE); #if JERRY_ESNEXT if (scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST); } #endif /* JERRY_ESNEXT */ } else if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode); } else { /* Invalid LeftHandSide expression. */ #if JERRY_ESNEXT parser_check_invalid_new_target (context_p, CBC_ASSIGN); parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); #else /* !JERRY_ESNEXT */ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); #endif /* JERRY_ESNEXT */ parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE); } } else if (context_p->token.type == LEXER_LOGICAL_OR || context_p->token.type == LEXER_LOGICAL_AND) { parser_branch_t branch; uint16_t opcode = CBC_BRANCH_IF_LOGICAL_TRUE; if (context_p->token.type == LEXER_LOGICAL_AND) { opcode = CBC_BRANCH_IF_LOGICAL_FALSE; } #if JERRY_ESNEXT parser_check_invalid_logical_op (context_p, LEXER_NULLISH_COALESCING, LEXER_NULLISH_COALESCING); #endif /* JERRY_ESNEXT */ parser_emit_cbc_forward_branch (context_p, opcode, &branch); parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); parser_stack_push_uint8 (context_p, context_p->token.type); return; } #if JERRY_ESNEXT else if (context_p->token.type == LEXER_NULLISH_COALESCING) { parser_branch_t branch; uint16_t opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH); parser_emit_cbc_forward_branch (context_p, opcode, &branch); parser_stack_push (context_p, &branch, sizeof (parser_branch_t)); parser_stack_push_uint8 (context_p, context_p->token.type); return; } #endif /* JERRY_ESNEXT */ parser_stack_push_uint8 (context_p, context_p->token.type); } /* parser_append_binary_token */ /** * Emit opcode for binary computations. */ static void parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ uint8_t min_prec_treshold) /**< minimal precedence of tokens */ { while (true) { uint8_t token = context_p->stack_top_uint8; uint16_t opcode; /* For left-to-right operators (all binary operators except assignment * and logical operators), the byte code is flushed if the precedence * of the next operator is less or equal than the current operator. For * assignment and logical operators, we add 1 to the min precendence to * force right-to-left evaluation order. */ if (!LEXER_IS_BINARY_OP_TOKEN (token) || parser_binary_precedence_table[token - LEXER_FIRST_BINARY_OP] < min_prec_treshold) { return; } parser_push_result (context_p); parser_stack_pop_uint8 (context_p); if (token == LEXER_ASSIGN) { uint16_t index = PARSER_INVALID_LITERAL_INDEX; opcode = context_p->stack_top_uint8; #if JERRY_ESNEXT if (JERRY_UNLIKELY (opcode == CBC_EXT_OPCODE)) { parser_stack_pop_uint8 (context_p); JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER); opcode = PARSER_TO_EXT_OPCODE (context_p->stack_top_uint8); parser_stack_pop_uint8 (context_p); } else #endif /* JERRY_ESNEXT */ { parser_stack_pop_uint8 (context_p); if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) { JERRY_ASSERT (opcode == CBC_ASSIGN_SET_IDENT || opcode == CBC_ASSIGN_PROP_LITERAL || opcode == CBC_ASSIGN_PROP_THIS_LITERAL || opcode == CBC_ASSIGN_LET_CONST || opcode == CBC_INIT_ARG_OR_CATCH || opcode == CBC_INIT_LET || opcode == CBC_INIT_CONST); index = parser_stack_pop_uint16 (context_p); } } #if JERRY_ESNEXT bool group_expr_assingment = false; if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_GROUP_EXPR)) { group_expr_assingment = true; parser_stack_pop_uint8 (context_p); } if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_CONST)) { parser_stack_pop_uint8 (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR); } #endif /* JERRY_ESNEXT */ if (index != PARSER_INVALID_LITERAL_INDEX) { #if JERRY_ESNEXT if (!group_expr_assingment) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { uint16_t name_index = scanner_save_literal (context_p, index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { parser_set_function_name (context_p, function_literal_index, (uint16_t) index, 0); } } #endif /* JERRY_ESNEXT */ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && opcode == CBC_ASSIGN_SET_IDENT) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc.value = index; context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; continue; } parser_emit_cbc_literal (context_p, (uint16_t) opcode, index); if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit)) { /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } continue; } } else if (LEXER_IS_BINARY_LVALUE_OP_TOKEN (token)) { parser_stack_push_uint8 (context_p, CBC_ASSIGN); parser_stack_push_uint8 (context_p, LEXER_ASSIGN); parser_stack_push_uint8 (context_p, lexer_convert_binary_lvalue_token_to_binary (token)); continue; } else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND) { parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); continue; } #if JERRY_ESNEXT else if (token == LEXER_NULLISH_COALESCING) { parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); continue; } #endif /* JERRY_ESNEXT */ else { opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token); if (PARSER_IS_PUSH_NUMBER (context_p->last_cbc_opcode)) { lexer_convert_push_number_to_push_literal (context_p); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL); continue; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS); continue; } } parser_emit_cbc (context_p, opcode); } } /* parser_process_binary_opcodes */ #if JERRY_ESNEXT /** * End position marker of a pattern. */ typedef struct { scanner_location_t location; /**< end position of the pattern */ lexer_token_t token; /**< token at the end position */ } parser_pattern_end_marker_t; /** * Literal index should not be emitted while processing rhs target value */ #define PARSER_PATTERN_RHS_NO_LIT PARSER_INVALID_LITERAL_INDEX /** * Process the target of an initializer pattern. */ static parser_pattern_end_marker_t parser_pattern_get_target (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_marker; end_marker.token.type = LEXER_INVALID_PATTERN; parser_branch_t skip_init; if (flags & PARSER_PATTERN_TARGET_DEFAULT) { JERRY_ASSERT (flags & PARSER_PATTERN_TARGET_ON_STACK); parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init); } if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) { scanner_location_t start_location; if (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED || (flags & PARSER_PATTERN_REST_ELEMENT)) { /* Found invalid pattern, push null value to fake the rhs target. */ parser_emit_cbc (context_p, CBC_PUSH_NULL); } else { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER); scanner_get_location (&start_location, context_p); scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location); scanner_release_next (context_p, sizeof (scanner_location_info_t)); scanner_seek (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); scanner_get_location (&(end_marker.location), context_p); end_marker.token = context_p->token; scanner_set_location (context_p, &start_location); scanner_seek (context_p); parser_flush_cbc (context_p); } } if (flags & PARSER_PATTERN_TARGET_DEFAULT) { parser_set_branch_to_current_position (context_p, &skip_init); } return end_marker; } /* parser_pattern_get_target */ /** * Finalize an assignment/binding pattern. */ static void parser_pattern_finalize (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ parser_pattern_end_marker_t *end_marker_p) /**< pattern end position */ { if ((flags & (PARSER_PATTERN_TARGET_ON_STACK | PARSER_PATTERN_TARGET_DEFAULT)) != PARSER_PATTERN_TARGET_ON_STACK) { if (end_marker_p->token.type == LEXER_INVALID_PATTERN) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } scanner_set_location (context_p, &(end_marker_p->location)); context_p->token = end_marker_p->token; } else { JERRY_ASSERT (!(flags & PARSER_PATTERN_TARGET_DEFAULT)); lexer_next_token (context_p); } if ((flags & (PARSER_PATTERN_BINDING | PARSER_PATTERN_NESTED_PATTERN)) == PARSER_PATTERN_BINDING) { /* Pop the result of the expression. */ parser_emit_cbc (context_p, CBC_POP); } parser_flush_cbc (context_p); } /* parser_pattern_finalize */ /** * Emit right-hand-side target value. */ static void parser_pattern_emit_rhs (parser_context_t *context_p, /**< context */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index) /**< literal index for object pattern */ { if (literal_index != PARSER_PATTERN_RHS_NO_LIT) { parser_emit_cbc_ext_literal (context_p, rhs_opcode, literal_index); } else { parser_emit_cbc_ext (context_p, rhs_opcode); } } /* parser_pattern_emit_rhs */ /** * Form an assignment from a pattern. */ static void parser_pattern_form_assignment (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index, /**< literal index for object pattern */ parser_line_counter_t ident_line_counter) /**< identifier line counter */ { JERRY_UNUSED (ident_line_counter); uint16_t name_index = PARSER_INVALID_LITERAL_INDEX; if ((flags & PARSER_PATTERN_BINDING) || (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)) { name_index = context_p->lit_object.index; } parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); parser_append_binary_single_assignment_token (context_p, flags); parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); if (context_p->token.type == LEXER_ASSIGN && !(flags & PARSER_PATTERN_REST_ELEMENT)) { parser_branch_t skip_init; lexer_next_token (context_p); parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (name_index != PARSER_INVALID_LITERAL_INDEX) { uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p); if (function_literal_index == PARSER_ANONYMOUS_CLASS) { name_index = scanner_save_literal (context_p, name_index); parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index); } else if (function_literal_index < PARSER_NAMED_FUNCTION) { parser_set_function_name (context_p, function_literal_index, name_index, 0); } } parser_set_branch_to_current_position (context_p, &skip_init); } parser_process_binary_opcodes (context_p, 0); JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); parser_stack_pop_uint8 (context_p); #if JERRY_DEBUGGER if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) && ident_line_counter != context_p->last_breakpoint_line) { parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED); parser_flush_cbc (context_p); parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, ident_line_counter); context_p->last_breakpoint_line = ident_line_counter; } #endif /* JERRY_DEBUGGER */ } /* parser_pattern_form_assignment */ /** * Parse pattern inside a pattern. */ static void parser_pattern_process_nested_pattern (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index) /**< literal index for object pattern */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE); parser_pattern_flags_t options = (PARSER_PATTERN_NESTED_PATTERN | PARSER_PATTERN_TARGET_ON_STACK | (flags & (PARSER_PATTERN_BINDING | PARSER_PATTERN_LET | PARSER_PATTERN_CONST | PARSER_PATTERN_LOCAL | PARSER_PATTERN_ARGUMENTS))); JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p || context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER || context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->source_p == context_p->source_p) { if (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER) { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { options |= PARSER_PATTERN_HAS_REST_ELEMENT; } if (!(flags & PARSER_PATTERN_REST_ELEMENT)) { options |= PARSER_PATTERN_TARGET_DEFAULT; } else { scanner_release_next (context_p, sizeof (scanner_location_info_t)); } } else { if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { options |= PARSER_PATTERN_HAS_REST_ELEMENT; } scanner_release_next (context_p, sizeof (scanner_info_t)); } } parser_pattern_emit_rhs (context_p, rhs_opcode, literal_index); if (context_p->token.type == LEXER_LEFT_BRACE) { parser_parse_object_initializer (context_p, options); } else { parser_parse_array_initializer (context_p, options); } parser_emit_cbc (context_p, CBC_POP); } /* parser_pattern_process_nested_pattern */ /** * Process the current {Binding, Assignment}Property * * @return true, if a nested pattern is processed, false otherwise */ static bool parser_pattern_process_assignment (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags, /**< flags */ uint16_t rhs_opcode, /**< opcode to process the rhs value */ uint16_t literal_index, /**< literal index for object pattern */ lexer_token_type_t end_type) /**< end type token */ { if ((context_p->token.type == LEXER_LEFT_BRACE || context_p->token.type == LEXER_LEFT_SQUARE) && (context_p->next_scanner_info_p->source_p != context_p->source_p || !(context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_NO_DESTRUCTURING))) { parser_pattern_process_nested_pattern (context_p, flags, rhs_opcode, literal_index); return true; } parser_line_counter_t ident_line_counter = context_p->token.line; #if JERRY_LINE_INFO parser_line_counter_t ident_column_counter = context_p->token.column; #endif /* JERRY_LINE_INFO */ if (flags & PARSER_PATTERN_BINDING) { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); if (flags & (PARSER_PATTERN_LET | PARSER_PATTERN_CONST) && context_p->token.keyword_type == LEXER_KEYW_LET) { parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING); } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } if (flags & PARSER_PATTERN_ARGUMENTS) { if (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) { parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); #endif /* JERRY_MODULE_SYSTEM */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); lexer_next_token (context_p); if (context_p->token.type != end_type && context_p->token.type != LEXER_ASSIGN && context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION); } } else { parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_LEFT_HAND_SIDE); if (!PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && !PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode)) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } } parser_pattern_form_assignment (context_p, flags, rhs_opcode, literal_index, ident_line_counter); #if JERRY_LINE_INFO parser_line_info_append (context_p, ident_line_counter, ident_column_counter); #endif /* JERRY_LINE_INFO */ return false; } /* parser_pattern_process_assignment */ /** * Parse array initializer. */ static void parser_parse_array_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); lexer_next_token (context_p); parser_emit_cbc_ext (context_p, CBC_EXT_ITERATOR_CONTEXT_CREATE); while (context_p->token.type != LEXER_RIGHT_SQUARE) { uint16_t rhs_opcode = CBC_EXT_ITERATOR_STEP; if (context_p->token.type == LEXER_COMMA) { parser_emit_cbc_ext (context_p, rhs_opcode); parser_emit_cbc (context_p, CBC_POP); lexer_next_token (context_p); continue; } parser_pattern_flags_t options = flags; if (context_p->token.type == LEXER_THREE_DOTS) { lexer_next_token (context_p); rhs_opcode = CBC_EXT_REST_INITIALIZER; options |= PARSER_PATTERN_REST_ELEMENT; } parser_pattern_process_assignment (context_p, options, rhs_opcode, PARSER_PATTERN_RHS_NO_LIT, LEXER_RIGHT_SQUARE); if (context_p->token.type == LEXER_COMMA && rhs_opcode != CBC_EXT_REST_INITIALIZER) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN); } } /* close the iterator */ parser_emit_cbc_ext (context_p, CBC_EXT_ITERATOR_CONTEXT_END); parser_pattern_finalize (context_p, flags, &end_pos); } /* parser_parse_array_initializer */ /** * Parse object initializer. */ static void parser_parse_object_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { parser_pattern_end_marker_t end_pos = parser_pattern_get_target (context_p, flags); /* 12.14.5.2: ObjectAssignmentPattern : { } */ if (lexer_check_next_character (context_p, LIT_CHAR_RIGHT_BRACE)) { parser_emit_cbc_ext (context_p, CBC_EXT_REQUIRE_OBJECT_COERCIBLE); lexer_consume_next_character (context_p); parser_pattern_finalize (context_p, flags, &end_pos); return; } cbc_ext_opcode_t context_opcode = CBC_EXT_OBJ_INIT_CONTEXT_CREATE; if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { context_opcode = CBC_EXT_OBJ_INIT_REST_CONTEXT_CREATE; } parser_emit_cbc_ext (context_p, context_opcode); while (true) { lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_OBJECT_PATTERN); uint16_t prop_index = context_p->lit_object.index; parser_line_counter_t start_line = context_p->token.line; parser_line_counter_t start_column = context_p->token.column; uint16_t push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_PROP_LITERAL; if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } if (context_p->token.type == LEXER_THREE_DOTS) { lexer_next_token (context_p); flags |= PARSER_PATTERN_REST_ELEMENT; if (parser_pattern_process_assignment (context_p, flags, CBC_EXT_OBJ_INIT_PUSH_REST, PARSER_PATTERN_RHS_NO_LIT, LEXER_RIGHT_BRACE)) { parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT); } if (context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); } /* Checked at the end because there might be syntax errors before. */ JERRY_ASSERT (flags & PARSER_PATTERN_HAS_REST_ELEMENT); break; } if (context_p->token.type == LEXER_RIGHT_SQUARE) { prop_index = PARSER_PATTERN_RHS_NO_LIT; push_prop_opcode = ((flags & PARSER_PATTERN_HAS_REST_ELEMENT) ? CBC_EXT_INITIALIZER_PUSH_NAME : CBC_EXT_INITIALIZER_PUSH_PROP); } else if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { push_prop_opcode = CBC_EXT_INITIALIZER_PUSH_NAME_LITERAL; } if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED); parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } lexer_next_token (context_p); if (context_p->token.type == LEXER_COLON) { lexer_next_token (context_p); parser_pattern_process_assignment (context_p, flags, push_prop_opcode, prop_index, LEXER_RIGHT_BRACE); } else { if (push_prop_opcode == CBC_EXT_INITIALIZER_PUSH_NAME || push_prop_opcode == CBC_EXT_INITIALIZER_PUSH_PROP) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } if (context_p->token.type != LEXER_RIGHT_BRACE && context_p->token.type != LEXER_ASSIGN && context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } parser_reparse_as_common_identifier (context_p, start_line, start_column); if (flags & PARSER_PATTERN_ARGUMENTS) { if (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT) { parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED); } context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT; } #if JERRY_MODULE_SYSTEM parser_module_append_export_name (context_p); #endif /* JERRY_MODULE_SYSTEM */ parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE || context_p->token.type == LEXER_ASSIGN || context_p->token.type == LEXER_COMMA); parser_pattern_form_assignment (context_p, flags, push_prop_opcode, prop_index, start_line); #if JERRY_LINE_INFO parser_line_info_append (context_p, start_line, start_column); #endif /* JERRY_LINE_INFO */ } if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } else if (context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } } if (flags & PARSER_PATTERN_HAS_REST_ELEMENT) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, (PARSER_OBJ_INIT_REST_CONTEXT_STACK_ALLOCATION - PARSER_OBJ_INIT_CONTEXT_STACK_ALLOCATION)); } parser_emit_cbc_ext (context_p, CBC_EXT_OBJ_INIT_CONTEXT_END); parser_pattern_finalize (context_p, flags, &end_pos); } /* parser_parse_object_initializer */ /** * Parse an initializer. */ void parser_parse_initializer (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { if (context_p->token.type == LEXER_LEFT_BRACE) { parser_parse_object_initializer (context_p, flags); } else { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); parser_parse_array_initializer (context_p, flags); } } /* parser_parse_initializer */ /** * Parse an initializer using the next character. */ void parser_parse_initializer_by_next_char (parser_context_t *context_p, /**< context */ parser_pattern_flags_t flags) /**< flags */ { JERRY_ASSERT (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE)); if (lexer_consume_next_character (context_p) == LIT_CHAR_LEFT_BRACE) { if (context_p->next_scanner_info_p->source_p == context_p->source_p) { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER || context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS); if (context_p->next_scanner_info_p->u8_arg & SCANNER_LITERAL_OBJECT_HAS_REST) { flags |= PARSER_PATTERN_HAS_REST_ELEMENT; } if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LITERAL_FLAGS) { scanner_release_next (context_p, sizeof (scanner_info_t)); } } parser_parse_object_initializer (context_p, flags); } else { parser_parse_array_initializer (context_p, flags); } } /* parser_parse_initializer_by_next_char */ #endif /* JERRY_ESNEXT */ /** * Process ternary expression. */ static void parser_process_ternary_expression (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_QUESTION_MARK); cbc_opcode_t opcode = CBC_BRANCH_IF_FALSE_FORWARD; parser_branch_t cond_branch; parser_branch_t uncond_branch; parser_push_result (context_p); if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_TRUE_FORWARD; } parser_emit_cbc_forward_branch (context_p, (uint16_t) opcode, &cond_branch); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &uncond_branch); parser_set_branch_to_current_position (context_p, &cond_branch); /* Although byte code is constructed for two branches, * only one of them will be executed. To reflect this * the stack is manually adjusted. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_set_branch_to_current_position (context_p, &uncond_branch); /* Last opcode rewrite is not allowed because * the result may come from the first branch. */ parser_flush_cbc (context_p); parser_process_binary_opcodes (context_p, 0); } /* parser_process_ternary_expression */ /** * Process expression sequence. */ static void parser_process_expression_sequence (parser_context_t *context_p) /**< context */ { if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN) { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL); page_p->bytes[context_p->stack.last_position - 1] = LEXER_COMMA_SEP_LIST; context_p->stack_top_uint8 = LEXER_COMMA_SEP_LIST; } lexer_next_token (context_p); } /* parser_process_expression_sequence */ /** * Process group expression. */ static void parser_process_group_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { JERRY_ASSERT (*grouping_level_p >= PARSER_GROUPING_LEVEL_INCREASE); (*grouping_level_p) -= PARSER_GROUPING_LEVEL_INCREASE; uint8_t token = context_p->stack_top_uint8; if (token == LEXER_COMMA_SEP_LIST) { parser_push_result (context_p); parser_flush_cbc (context_p); } parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); #if JERRY_ESNEXT /* Lookahead for anonymous function declaration after '=' token when the assignment base is LHS expression with a single indentifier in it. e.g.: (a) = function () {} */ if (JERRY_UNLIKELY (context_p->token.type == LEXER_ASSIGN && PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL && parser_is_assignment_expr (context_p) && *grouping_level_p != PARSE_EXPR_LEFT_HAND_SIDE)) { parser_stack_push_uint8 (context_p, LEXER_ASSIGN_GROUP_EXPR); } #endif /* JERRY_ESNEXT */ } /* parser_process_group_expression */ /** * Parse block expression. */ void parser_parse_block_expression (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { parser_parse_expression (context_p, options | PARSE_EXPR_NO_PUSH_RESULT); if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 2)); PARSER_PLUS_EQUAL_U16 (context_p->last_cbc_opcode, 2); parser_flush_cbc (context_p); } else { parser_emit_cbc (context_p, CBC_POP_BLOCK); } } /* parser_parse_block_expression */ /** * Parse expression statement. */ void parser_parse_expression_statement (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { parser_parse_expression (context_p, options | PARSE_EXPR_NO_PUSH_RESULT); if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } } /* parser_parse_expression_statement */ JERRY_STATIC_ASSERT (PARSE_EXPR_LEFT_HAND_SIDE == 0x1, value_of_parse_expr_left_hand_side_must_be_1); /** * Parse expression. */ void parser_parse_expression (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { size_t grouping_level = (options & PARSE_EXPR_LEFT_HAND_SIDE); parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); if (options & PARSE_EXPR_HAS_LITERAL) { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); goto process_unary_expression; } while (true) { if (parser_parse_unary_expression (context_p, &grouping_level)) { parser_process_binary_opcodes (context_p, 0); break; } while (true) { process_unary_expression: parser_process_unary_expression (context_p, grouping_level); if (JERRY_LIKELY (grouping_level != PARSE_EXPR_LEFT_HAND_SIDE)) { uint8_t min_prec_treshold = 0; if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { #if JERRY_ESNEXT if (JERRY_UNLIKELY (context_p->token.type == LEXER_NULLISH_COALESCING)) { parser_check_invalid_logical_op (context_p, LEXER_LOGICAL_OR, LEXER_LOGICAL_AND); } #endif /* JERRY_ESNEXT */ min_prec_treshold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP]; #if JERRY_ESNEXT /* Check for BINARY_LVALUE tokens + LEXER_LOGICAL_OR + LEXER_LOGICAL_AND + LEXER_EXPONENTIATION */ if ((min_prec_treshold == PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION) || (min_prec_treshold <= PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE && min_prec_treshold != PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE)) { /* Right-to-left evaluation order. */ min_prec_treshold++; } #else /* !JERRY_ESNEXT */ /* Check for BINARY_LVALUE tokens + LEXER_LOGICAL_OR + LEXER_LOGICAL_AND */ if (min_prec_treshold <= PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE && min_prec_treshold != PARSER_RIGHT_TO_LEFT_ORDER_TERNARY_PRECEDENCE) { /* Right-to-left evaluation order. */ min_prec_treshold++; } #endif /* JERRY_ESNEXT */ } parser_process_binary_opcodes (context_p, min_prec_treshold); } if (context_p->token.type == LEXER_RIGHT_PAREN && (context_p->stack_top_uint8 == LEXER_LEFT_PAREN || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST)) { parser_process_group_expression (context_p, &grouping_level); continue; } break; } if (grouping_level == PARSE_EXPR_LEFT_HAND_SIDE) { break; } if (JERRY_UNLIKELY (context_p->token.type == LEXER_QUESTION_MARK)) { parser_process_ternary_expression (context_p); if (context_p->token.type == LEXER_RIGHT_PAREN) { goto process_unary_expression; } } else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { parser_append_binary_token (context_p); lexer_next_token (context_p); continue; } if (JERRY_UNLIKELY (context_p->token.type == LEXER_COMMA) && (!(options & PARSE_EXPR_NO_COMMA) || grouping_level >= PARSER_GROUPING_LEVEL_INCREASE)) { parser_process_expression_sequence (context_p); continue; } break; } if (grouping_level >= PARSER_GROUPING_LEVEL_INCREASE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); parser_stack_pop_uint8 (context_p); if (!(options & PARSE_EXPR_NO_PUSH_RESULT)) { parser_push_result (context_p); } } /* parser_parse_expression */ /** * @} * @} * @} */ #endif /* JERRY_PARSER */