parser_parse_function_arguments (parser_context_t *context_p, /**< context */
                                 lexer_token_type_t end_type) /**< expected end type */
{
  JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);

#if JERRY_ESNEXT
  JERRY_ASSERT (context_p->status_flags & PARSER_IS_FUNCTION);
  JERRY_ASSERT (!(context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED));

  bool has_duplicated_arg_names = false;

  if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags))
  {
    parser_branch_t branch;
    parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &branch);
    parser_stack_push (context_p, &branch, sizeof (parser_branch_t));

#ifndef JERRY_NDEBUG
    context_p->context_stack_depth = PARSER_TRY_CONTEXT_STACK_ALLOCATION;
#endif /* !JERRY_NDEBUG */
  }
#endif /* JERRY_ESNEXT */

  if (context_p->token.type == end_type)
  {
#if JERRY_ESNEXT
    context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_AWAIT_YIELD;

    if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
    {
      scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_ARGS);
      parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR);
      parser_emit_cbc (context_p, CBC_POP);
      scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY);
      return;
    }
#endif /* JERRY_ESNEXT */
    scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
    return;
  }

#if JERRY_ESNEXT
  bool has_complex_argument = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_HAS_COMPLEX_ARGUMENT) != 0;
#endif /* JERRY_ESNEXT */
  bool is_strict = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_IS_STRICT) != 0;

  scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_ARGS);
  scanner_set_active (context_p);

#if JERRY_ESNEXT
  context_p->status_flags |= PARSER_FUNCTION_IS_PARSING_ARGS;
#endif /* JERRY_ESNEXT */

  while (true)
  {
#if JERRY_ESNEXT
    if (context_p->token.type == LEXER_THREE_DOTS)
    {
      if (context_p->status_flags & PARSER_IS_PROPERTY_SETTER)
      {
        parser_raise_error (context_p, PARSER_ERR_SETTER_REST_PARAMETER);
      }
      lexer_next_token (context_p);

      if (has_duplicated_arg_names)
      {
        parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
      }

      context_p->status_flags |= PARSER_FUNCTION_HAS_REST_PARAM | PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT;
    }

    if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE)
    {
      if (has_duplicated_arg_names)
      {
        parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
      }

      context_p->status_flags |= PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT;

      if (!(context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM))
      {
        parser_emit_cbc_literal (context_p,
                                 CBC_PUSH_LITERAL,
                                 (uint16_t) (PARSER_REGISTER_START + context_p->argument_count));
      }
      else
      {
        parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_REST_OBJECT);
      }

      uint32_t flags = (PARSER_PATTERN_BINDING
                        | PARSER_PATTERN_TARGET_ON_STACK
                        | PARSER_PATTERN_LOCAL
                        | PARSER_PATTERN_ARGUMENTS);

      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)
          {
            flags |= PARSER_PATTERN_HAS_REST_ELEMENT;
          }

          if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
          {
            parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER);
          }

          if (context_p->argument_length == UINT16_MAX)
          {
            context_p->argument_length = context_p->argument_count;
          }

          flags |= PARSER_PATTERN_TARGET_DEFAULT;
        }
        else if (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;
          }
          scanner_release_next (context_p, sizeof (scanner_info_t));
        }
        else
        {
          parser_raise_error (context_p, PARSER_ERR_INVALID_DESTRUCTURING_PATTERN);
        }
      }

      parser_parse_initializer (context_p, flags);

      context_p->argument_count++;
      if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
      {
        parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
      }

      if (context_p->token.type != LEXER_COMMA)
      {
        if (context_p->token.type != end_type)
        {
          parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED
                                                                  : PARSER_ERR_IDENTIFIER_EXPECTED);

          parser_raise_error (context_p, error);
        }
        break;
      }

      lexer_next_token (context_p);

      if (context_p->token.type == end_type)
      {
        break;
      }
      continue;
    }
#endif /* JERRY_ESNEXT */

    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 (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS)
    {
      context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG;
    }

    if (JERRY_UNLIKELY (context_p->lit_object.literal_p->status_flags & LEXER_FLAG_FUNCTION_ARGUMENT))
    {
#if JERRY_ESNEXT
      if ((context_p->status_flags & PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT)
          || (context_p->status_flags & PARSER_IS_ARROW_FUNCTION))
      {
        parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
      }
      has_duplicated_arg_names = true;
#endif /* JERRY_ESNEXT */

      context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG;
    }
    else
    {
      context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_FUNCTION_ARGUMENT;
    }

    lexer_next_token (context_p);

#if JERRY_ESNEXT
    uint16_t literal_index = context_p->lit_object.index;

    if (context_p->token.type == LEXER_ASSIGN)
    {
      JERRY_ASSERT (has_complex_argument);

      if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
      {
        parser_raise_error (context_p, PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER);
      }

      if (context_p->argument_length == UINT16_MAX)
      {
        context_p->argument_length = context_p->argument_count;
      }

      parser_branch_t skip_init;

      if (has_duplicated_arg_names)
      {
        parser_raise_error (context_p, PARSER_ERR_DUPLICATED_ARGUMENT_NAMES);
      }

      context_p->status_flags |= PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT;

      /* LEXER_ASSIGN does not overwrite lit_object. */
      parser_emit_cbc_literal (context_p,
                               CBC_PUSH_LITERAL,
                               (uint16_t) (PARSER_REGISTER_START + context_p->argument_count));
      parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_DEFAULT_INITIALIZER, &skip_init);

      lexer_next_token (context_p);
      parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);

      parser_set_branch_to_current_position (context_p, &skip_init);

      uint16_t opcode = CBC_ASSIGN_LET_CONST;

      if (literal_index >= PARSER_REGISTER_START)
      {
        opcode = CBC_MOV_IDENT;
      }
      else if (!scanner_literal_is_created (context_p, literal_index))
      {
        opcode = CBC_INIT_ARG_OR_CATCH;
      }

      parser_emit_cbc_literal (context_p, opcode, literal_index);
    }
    else if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
    {
      parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_REST_OBJECT);

      uint16_t opcode = CBC_MOV_IDENT;

      if (literal_index < PARSER_REGISTER_START)
      {
        opcode = CBC_INIT_ARG_OR_CATCH;

        if (scanner_literal_is_created (context_p, literal_index))
        {
          opcode = CBC_ASSIGN_LET_CONST;
        }
      }

      parser_emit_cbc_literal (context_p, opcode, literal_index);
    }
    else if (has_complex_argument && literal_index < PARSER_REGISTER_START)
    {
      uint16_t opcode = CBC_INIT_ARG_OR_FUNC;

      if (scanner_literal_is_created (context_p, literal_index))
      {
        opcode = CBC_ASSIGN_LET_CONST_LITERAL;
      }

      parser_emit_cbc_literal_value (context_p,
                                     opcode,
                                     (uint16_t) (PARSER_REGISTER_START + context_p->argument_count),
                                     literal_index);
    }
#endif /* JERRY_ESNEXT */

    context_p->argument_count++;
    if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS)
    {
      parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
    }

    if (context_p->token.type != LEXER_COMMA)
    {
      if (context_p->token.type != end_type)
      {
        parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED
                                                                : PARSER_ERR_IDENTIFIER_EXPECTED);

        parser_raise_error (context_p, error);
      }
      break;
    }

#if JERRY_ESNEXT
    if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM)
    {
      parser_raise_error (context_p, PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER);
    }
#endif /* JERRY_ESNEXT */

    lexer_next_token (context_p);

#if JERRY_ESNEXT
    if (context_p->token.type == end_type)
    {
      break;
    }
#endif /* JERRY_ESNEXT */
  }

  scanner_revert_active (context_p);

#if JERRY_ESNEXT
  JERRY_ASSERT (has_complex_argument || !(context_p->status_flags & PARSER_FUNCTION_HAS_COMPLEX_ARGUMENT));

  if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
  {
    parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR);
    parser_emit_cbc (context_p, CBC_POP);
  }

  if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED)
  {
    if ((context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_LEXICAL_ENV_NEEDED)
        || scanner_is_context_needed (context_p, PARSER_CHECK_FUNCTION_CONTEXT))
    {
      context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;

      parser_branch_t branch;
      parser_emit_cbc_forward_branch (context_p, CBC_BLOCK_CREATE_CONTEXT, &branch);
      parser_stack_push (context_p, &branch, sizeof (parser_branch_t));

#ifndef JERRY_NDEBUG
      PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
#endif /* !JERRY_NDEBUG */
    }
    else
    {
      context_p->status_flags &= (uint32_t) ~PARSER_LEXICAL_BLOCK_NEEDED;
    }
  }

  context_p->status_flags &= (uint32_t) ~(PARSER_DISALLOW_AWAIT_YIELD | PARSER_FUNCTION_IS_PARSING_ARGS);
#endif /* JERRY_ESNEXT */

  scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY);

  if (is_strict)
  {
    context_p->status_flags |= PARSER_IS_STRICT;
  }
} /* parser_parse_function_arguments */