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 */