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