Третья проверка кода проекта Chromium с помощью анализатора PVS-Studio

    Браузер Chromium очень быстро развивается. Например, когда в 2011 году мы впервые проверили этот проект (solution), он состоял из 473 проектов. Сейчас, он состоит уже из 1169 проектов. Нам было интересно, смогли ли разработчики Google сохранить высочайшее качество кода, при такой скорости развития Chromium. Да, смогли.

    Chromium


    Chromium — веб-браузер с открытым исходным кодом, разработанный компанией Google. На основе Chromium создаётся браузер Google Chrome. На странице "Get the Code" можно узнать, как скачать исходный код этого проекта.

    Немного общей информации


    Раньше мы уже проверяли проект Chromium, о чём имеется две статьи: первая проверка (23.05.2011), вторая проверка (13.10.2011). И всё время находили ошибки. Это тонкий намёк о пользе анализаторов кода.

    Сейчас (исходный код проекта скачан в июле 2013) Chromium состоит из 1169 проектов. Общий объем исходного кода на языке Си/Си++ составляет 260 мегабайт. Дополнительно к этому можно прибавить ещё 450 мегабайт используемых внешних библиотек.

    Если взять нашу первую проверку проекта Chromium в 2011 году, то можно заметить — объем внешних библиотек в целом не изменился. Зато код, самого проекта существенно вырос c 155 мегабайт до 260 мегабайт.

    Ради интереса посчитаем цикломатическую сложность


    В анализаторе PVS-Studio есть возможность поиска функций с большой цикломатической сложностью. Как правило, такие функции являются кандидатами для рефакторинга. Проверив 1160 проектов, мне естественно стало интересно, какой из них можно назвать рекордсменом в номинации «самая сложная функция».

    Самая максимальная цикломатическая сложность, равная 2782, принадлежит функции ValidateChunkAMD64() в проекте Chromium. Но её пришлось дисквалифицировать из состязания. Функция находится в файле validator_x86_64.c, который является автогенерируемым. Жаль. А то был бы эпичный рекордсмен. Я и близко с такой цикломатической сложностью не сталкивался.

    Таким образом, первые три места получают следующие функции:
    1. Библиотека WebKit. Функция HTMLTokenizer::nextToken() в файле htmltokenizer.cpp. Цикломатическая сложность 1106.
    2. Библиотека Mesa. Функция _mesa_glsl_lex() в файле glsl_lexer.cc. Цикломатическая сложность 1088.
    3. Библиотека usrsctplib (какой-то безызвестный спортсмен). Функция sctp_setopt() в файле htmltokenizer.cpp. Цикломатическая сложность 1026.

    Если кто-то не знает, что такое цикломатическая сложность 1000, то пусть и не знает. Психическое здоровье будет лучше :). В общем, много это.

    Качество кода


    Что я могу сказать о качестве кода проекта Chromium? Качество по-прежнему великолепное. Да, как и в любом большом проекте, всегда можно найти ошибки. Но если поделить их количество на объем кода, их плотность будет ничтожна. Это очень хороший код с очень малым количеством ошибок. Вручаю медальку за чистый код. Предыдущая медалька досталась проекту Casablanca (C++ REST SDK) от компании Microsoft.
    Рисунок 1. Медалька создателям Chromium.
    Рисунок 1. Медалька создателям Chromium.

    За компанию вместе с Chromium были проверены входящие в него сторонние библиотеки. Но описывать найденные в них ошибки не интересно. Тем более я просматривал отчёт очень поверхностно. Нет, я вовсе не плохой человек. Я бы посмотрел на вас, если бы вы попробовали полноценно изучить отчёт о проверке 1169 проектов. То, что я заметил при беглом просмотре, я поместил в базу примеров ошибок. В этой статье я хочу коснуться только тех ошибок, которые успел заметить в коде самого Chromium (его плагинов и тому подобного).

    Раз проект Chromium, такой хороший, так зачем я буду приводить примеры найденных ошибок? Всё очень просто. Я хочу продемонстрировать мощь анализатора PVS-Studio. Если он сумел найти ошибки в Chromium, то инструмент заслуживает вашего внимания.

    Анализатор сумел прожевать десятки тысяч файлов, общим объемом 710 мегабайт, и не загнулся от этого. Не смотря на то, что проект разрабатывается высококвалифицированными разработчиками и проверяется различными инструментами, PVS-Studio всё равно умудрился выявить дефекты. Это замечательное достижение! И последнее — он сделал это за разумное время (около 5 часов) за счёт параллельной проверки (AMD FX-8320/3.50 GHz/eight-core processor, 16.0 GB RAM).

    Некоторые из найденных ошибок


    Предлагаю рассмотреть некоторые примеры кода, на которых остановился мой взгляд при просмотре отчета. Уверен, что при детальном изучении, можно будет найти намного больше интересного.

    Замеченное N1 — опечатки


    Vector3dF
    Matrix3F::SolveEigenproblem(Matrix3F* eigenvectors) const
    {
      // The matrix must be symmetric.
      const float epsilon = std::numeric_limits<float>::epsilon();
      if (std::abs(data_[M01] - data_[M10]) > epsilon ||
          std::abs(data_[M02] - data_[M02]) > epsilon ||
          std::abs(data_[M12] - data_[M21]) > epsilon) {
        NOTREACHED();
        return Vector3dF();
      }
      ....
    }

    V501 There are identical sub-expressions to the left and to the right of the '-' operator: data_[M02] — data_[M02] matrix3_f.cc 128

    Надо проверить, что матрица размером 3x3 симметрична.
    Рисунок 2. Матрица 3x3.
    Рисунок 2. Матрица 3x3.

    Для этого надо сравнить следующие элементы:
    • M01 и M10
    • M02 и M20
    • M12 и M21

    Скорее всего, код писался с помощью технологии Copy-Paste. В результате ячейка M02 сравнивается сама с собой. Вот такой вот забавный класс матрицы.

    Другая простая опечатка:
    bool IsTextField(const FormFieldData& field) {
      return
        field.form_control_type == "text" ||
        field.form_control_type == "search" ||
        field.form_control_type == "tel" ||
        field.form_control_type == "url" ||
        field.form_control_type == "email" ||
        field.form_control_type == "text";
    }

    V501 There are identical sub-expressions 'field.form_control_type == «text»' to the left and to the right of the '||' operator. autocomplete_history_manager.cc 35

    Два раза происходит сравнение со строкой «text». Это подозрительно. Возможно, одна строка просто лишняя. А может быть, отсутствует другое нужное здесь сравнение.

    Замеченное N2 — противоположные условия


    static void ParseRequestCookieLine(
        const std::string& header_value,
        ParsedRequestCookies* parsed_cookies)
    {
      std::string::const_iterator i = header_value.begin();
      ....
      if (*i == '"') {
        while (i != header_value.end() && *i != '"') ++i;
      ....
    }

    V637 Two opposite conditions were encountered. The second condition is always false. Check lines: 500, 501. web_request_api_helpers.cc 500

    Мне кажется, этот код должен был пропускать текст, обрамленный двойными кавычками. Но на самом деле, этот код ничего не делает. Условие сразу ложно. Для наглядности, напишу псевдокод, чтобы подчеркнуть суть ошибки:
    if ( A == 'X' ) {
      while ( .... && A != 'X' ) ....;

    Скорее всего, здесь забыли сдвинуть указатель на один символ и код должен выглядеть так:
    if (*i == '"') {
      ++i;
      while (i != header_value.end() && *i != '"') ++i;


    Замеченное N3 — неудачное удаление элементов


    void ShortcutsProvider::DeleteMatchesWithURLs(
      const std::set<GURL>& urls)
    {
      std::remove_if(matches_.begin(),
                     matches_.end(),
                     RemoveMatchPredicate(urls));
      listener_->OnProviderUpdate(true);
    }

    V530 The return value of function 'remove_if' is required to be utilized. shortcuts_provider.cc 136

    Для удаления элементов из контейнера используется функция std::remove_if(). Но используется неправильно. На самом деле, remove_if() ничего не удаляет. Она сдвигает элементы в начало и возвращает итератор на мусор. Удалять мусор нужно самостоятельно, вызывая у контейнеров функцию erase(). См. также статью в Wikipedia "Erase-remove idiom".

    Правильный код:
    matches_.erase(std::remove_if(.....), matches_.end());


    Замеченное N4 — вечная путаница с SOCKET


    SOCKET в мире Linux, это целочисленный ЗНАКОВЫЙ тип данных.

    SOCKET в мире Windows, это целочисленный БЕЗЗНАКОВЫЙ тип данных.

    В заголовочных файлах Visual C++ тип SOCKET объявлен так:
    typedef UINT_PTR SOCKET;

    Однако про это постоянно забывают и пишут код следующего вида:
    class NET_EXPORT_PRIVATE TCPServerSocketWin {
       ....
       SOCKET socket_;
       ....
    };
    
    int TCPServerSocketWin::Listen(....) {
      ....
      socket_ = socket(address.GetSockAddrFamily(),
                       SOCK_STREAM, IPPROTO_TCP);
      if (socket_ < 0) {
        PLOG(ERROR) << "socket() returned an error";
        return MapSystemError(WSAGetLastError());
      }
      ....
    }

    V547 Expression 'socket_ < 0' is always false. Unsigned type value is never < 0. tcp_server_socket_win.cc 48

    Переменная беззнакового типа всегда больше или равна нулю. Это значит, что проверка 'socket_ < 0' не имеет смысла. Если при работе программы сокет не удастся открыть, эта ситуация не будет корректно обработана.

    Замеченное N5 — путаница с операциями ~ и !


    enum FontStyle {
      NORMAL = 0,
      BOLD = 1,
      ITALIC = 2,
      UNDERLINE = 4,
    };
    
    void LabelButton::SetIsDefault(bool is_default) {
      ....
      style = is_default ? style | gfx::Font::BOLD :
                           style & !gfx::Font::BOLD;
      ....
    }

    V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. label_button.cc 131

    Как мне кажется, код должен был работать так:
    • Если переменная 'is_default' равна true, то всегда нужно выставить бит, отвечающий за стиль BOLD.
    • Если переменная 'is_default' равна false, то всегда нужно сбросить бит, отвечающий за стиль BOLD.

    Однако, выражение «style & !gfx::Font::BOLD» работает не так, как ожидает программист. Результат операции "!gfx::Font::BOLD" будет равен 'false'. Или другими словами, 0. Код написанный выше эквивалентен этому:
    style = is_default ? style | gfx::Font::BOLD : 0;

    Чтобы код работал правильно, следовало использовать операцию '~':
    style = is_default ? style | gfx::Font::BOLD :
                         style & ~gfx::Font::BOLD;


    Замеченное N6 — подозрительное создание временных объектов


    base::win::ScopedComPtr<IDirect3DSurface9> scaler_scratch_surfaces_[2];
    
    bool AcceleratedSurfaceTransformer::ResizeBilinear(
      IDirect3DSurface9* src_surface, ....)
    {
      ....
      IDirect3DSurface9* read_buffer = (i == 0) ?
        src_surface : scaler_scratch_surfaces_[read_buffer_index];
      ....
    }

    V623 Consider inspecting the '?:' operator. A temporary object of the 'ScopedComPtr' type is being created and subsequently destroyed. Check second operand. accelerated_surface_transformer_win.cc 391

    Вряд ли этот код привести к ошибке, но он заслуживает, чтобы рассказать о нём. Мне кажется, некоторые программисты узнают о новой интересной ловушке в языке Си++.

    На первый взгляд здесь всё просто. В зависимости от условия, мы выбираем указатель 'src_surface' или один из элементов массива 'scaler_scratch_surfaces_'. Массив состоит из объектов типа base::win::ScopedComPtr<IDirect3DSurface9>, которые могут автоматически приводиться к указателю на IDirect3DSurface9.

    Дьявол кроется в деталях.

    Тернарный оператор '?:' не может возвращать разный тип в зависимости от условия. Поясню на простом примере.
    int A = 1;
    auto X = v ? A : 2.0;

    Оператор ?: возвращает тип 'double'. В результате переменная 'X' будет тоже иметь тип double. Но это не важно. Важно, что переменная 'A' будет неявно расширена до типа 'double'!

    Беда возникает, если написать что-то подобное:
    CString s1(L"1");
    wchar_t s2[] = L"2";
    bool a = false;
    const wchar_t *s = a ? s1 : s2;

    В результате выполнения этого кода переменная 's' будет указывать на данные, находящиеся внутри временного объекта типа CString. Проблема в том, что этот объект будет сразу уничтожен.

    Вернемся теперь к исходному коду Chromium.
    IDirect3DSurface9* read_buffer = (i == 0) ?
        src_surface : scaler_scratch_surfaces_[read_buffer_index];

    Здесь произойдет следующее, если выполнится условие 'i == 0':
    • из указателя 'src_surface' будет сконструирован временный объект типа base::win::ScopedComPtr<IDirect3DSurface9>;
    • временный объект будет неявно приведён к указателю типа IDirect3DSurface9 и помещён в переменную read_buffer;
    • временный объект будет разрушен.

    Я не знаю логику работы программы и класса ScopedComPtr и затрудняюсь сказать, могут ли возникнуть негативные последствия или нет. Скорее всего, в конструкторе счётчик количества ссылок будет увеличен, а в деструкторе уменьшен. И всё будет хорошо.

    Если это не так, то ненароком можно получить не валидный указатель или сломать подсчёт ссылок.

    Одним словом, даже если ошибки нет, буду рад, если читатели узнали что-то новое. Тернарный оператор куда опаснее, чем кажется.

    Вот ещё одно такое подозрительное место:
    typedef
      GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle;
    
    DWORD HandlePolicy::DuplicateHandleProxyAction(....)
    {
      ....
      base::win::ScopedHandle remote_target_process;
      ....
      HANDLE target_process =
        remote_target_process.IsValid() ?
          remote_target_process : ::GetCurrentProcess();
      ....
    }

    V623 Consider inspecting the '?:' operator. A temporary object of the 'GenericScopedHandle' type is being created and subsequently destroyed. Check third operand. handle_policy.cc 81

    Замеченное N7 — повторяющиеся проверки


    string16 GetAccessString(HandleType handle_type,
                             ACCESS_MASK access) {
      ....
      if (access & FILE_WRITE_ATTRIBUTES)
        output.append(ASCIIToUTF16("\tFILE_WRITE_ATTRIBUTES\n"));
      if (access & FILE_WRITE_DATA)
        output.append(ASCIIToUTF16("\tFILE_WRITE_DATA\n"));
      if (access & FILE_WRITE_EA)
        output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
      if (access & FILE_WRITE_EA)
        output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
      ....
    }

    V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 176, 178. handle_enumerator_win.cc 178

    Если установлен флаг FILE_WRITE_EA, то стока "\tFILE_WRITE_EA\n" будет добавлена дважды. Очень подозрительный код.

    Аналогичную странную картину можно наблюдать здесь:
    static bool PasswordFormComparator(const PasswordForm& pf1,
                                       const PasswordForm& pf2) {
      if (pf1.submit_element < pf2.submit_element)
        return true;
      if (pf1.username_element < pf2.username_element)
        return true;
      if (pf1.username_value < pf2.username_value)
        return true;
      if (pf1.username_value < pf2.username_value)
        return true;
      if (pf1.password_element < pf2.password_element)
        return true;
      if (pf1.password_value < pf2.password_value)
        return true;
    
      return false;
    }

    V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 259, 261. profile_sync_service_password_unittest.cc 261

    Два раза повторяется проверка «pf1.username_value < pf2.username_value». Возможна, одна строка просто лишняя. А возможно, забыли проверить что-то другое, и должно быть написано совсем другое условие.

    Замеченное N8 — «одноразовые» циклы


    ResourceProvider::ResourceId
    PictureLayerImpl::ContentsResourceId() const
    {
      ....
      for (PictureLayerTilingSet::CoverageIterator iter(....);
           iter;
           ++iter)
      {
        if (!*iter)
          return 0;
    
        const ManagedTileState::TileVersion& tile_version = ....;
    
        if (....)
          return 0;
    
        if (iter.geometry_rect() != content_rect)
          return 0;
    
        return tile_version.get_resource_id();
      }
      return 0;
    }

    V612 An unconditional 'return' within a loop. picture_layer_impl.cc 638

    С этим циклом что-то не так. Цикл выполняет только одну итерацию. В конце цикла находится безусловный оператор return. Возможные причины:
    • Так и задумано. Но это очень сомнительно. Зачем тогда создавать цикл, создавать итератор и т.д.
    • Вместо одного из 'return', должно быть написано 'continue'. Но тоже сомнительно.
    • Скорее всего, перед последним 'return' пропущено какое-то условие.

    Попались и другие странные циклы, которые выполняются только раз:
    scoped_ptr<ActionInfo> ActionInfo::Load(....)
    {
      ....
      for (base::ListValue::const_iterator iter = icons->begin();
            iter != icons->end(); ++iter)
      {
        std::string path;
        if (....);
          return scoped_ptr<ActionInfo>();
        }
    
        result->default_icon.Add(....);
        break;
      }
      ....
    }

    V612 An unconditional 'break' within a loop. action_info.cc 76
    const BluetoothServiceRecord* BluetoothDeviceWin::GetServiceRecord(
        const std::string& uuid) const
    {
      for (ServiceRecordList::const_iterator iter =
             service_record_list_.begin();
           iter != service_record_list_.end();
           ++iter)
      {
        return *iter;
      }
      return NULL;
    }

    V612 An unconditional 'return' within a loop. bluetooth_device_win.cc 224

    Замеченное N9 — неинициализированные переменные


    HRESULT IEEventSink::Attach(IWebBrowser2* browser) {
      DCHECK(browser);
      HRESULT result;
      if (browser) {
        web_browser2_ = browser;
        FindIEProcessId();
        result = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
      }
      return result;
    }

    V614 Potentially uninitialized variable 'result' used. ie_event_sink.cc 240

    Если указатель 'browser' равен нулю, то функция вернет неинициализированную переменную.

    Другой фрагмент кода:
    void SavePackage::GetSaveInfo() {
      ....
      bool skip_dir_check;
      ....
      if (....) {
        ....->GetSaveDir(...., &skip_dir_check);
      }
      ....
      BrowserThread::PostTask(BrowserThread::FILE,
                              FROM_HERE,
                              base::Bind(..., skip_dir_check, ...));
    }

    V614 Potentially uninitialized variable 'skip_dir_check' used. Consider checking the fifth actual argument of the 'Bind' function. save_package.cc 1326

    Переменная 'skip_dir_check' может остаться неинициализированной.

    Замеченное N10 — выравнивание кода не соответствует логике его работы


    void OnTraceNotification(int notification) {
      if (notification & TraceLog::EVENT_WATCH_NOTIFICATION)
        ++event_watch_notification_;
        notifications_received_ |= notification;
    }

    V640 The code's operational logic does not correspond with its formatting. The statement is indented to the right, but it is always executed. It is possible that curly brackets are missing. trace_event_unittest.cc 57

    Рассматривая такой код, непонятно, забыты здесь фигурные скобки или нет. Даже если код корректен, его следует поправить, чтобы он не вводил других программистов в задумчивое состояние.

    Вот ещё пара мест с ОЧЕНЬ подозрительным выравниванием кода:
    • nss_memio.c 152
    • nss_memio.c 184


    Замеченное N11 — проверка указателя после new


    Во многих программах живет старый унаследованный код, написанный ещё в те времена, когда оператор 'new' не кидал исключение. Раньше в случае нехватки памяти он возвращал нулевой указатель.

    Chromium в этом плане не исключение, и в нем встречаются такие проверки. Беда не в том, что выполняется бессмысленная проверка. Опасно, что при нулевом указателе раньше должны были выполняться какие-то действия или функции должны возвращать определенные значения. Теперь из-за генерации исключения логика работы изменилась. Код, который должен был получить управление при ошибке выделения памяти, теперь бездействует.

    Рассмотрим пример:
    static base::DictionaryValue* GetDictValueStats(
        const webrtc::StatsReport& report)
    {
      ....
      DictionaryValue* dict = new base::DictionaryValue();
      if (!dict)
        return NULL;
    
      dict->SetDouble("timestamp", report.timestamp);
    
      base::ListValue* values = new base::ListValue();
      if (!values) {
        delete dict;
        return NULL;
      }
      ....
    }

    V668 There is no sense in testing the 'dict' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. peer_connection_tracker.cc 164

    V668 There is no sense in testing the 'values' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. peer_connection_tracker.cc 169

    Первая проверка «if (!dict) return NULL;» скорее всего вреда не принесёт. А вот со второй проверкой дело обстоит хуже. Если при создании объекта с помощью «new base::ListValue()» не удастся выделить память, то будет сгенерировано исключение 'std::bad_alloc'. На этом работа функции GetDictValueStats() завершится.

    В результате, вот этот код:
    if (!values) {
      delete dict;
      return NULL;
    }

    никогда не уничтожит объект, адрес которого хранится в переменной 'dict'.

    Правильным решением здесь будет проведение рефакторинга кода и использование умных указателей (smart pointers).

    Рассмотрим другой фрагмент кода:
    bool Target::Init() {
    {
      ....
      ctx_ = new uint8_t[abi_->GetContextSize()];
    
      if (NULL == ctx_) {
        Destroy();
        return false;
      }
      ....
    }

    V668 There is no sense in testing the 'ctx_' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. target.cc 73

    В случае ошибки выделения памяти не будет вызвана функция Destroy().

    Дальше писать про это не интересно. Я просто приведу список других замеченных мной потенциально опасных мест в коде:
    • 'data' pointer. target.cc 109
    • 'page_data' pointer. mock_printer.cc 229
    • 'module' pointer. pepper_entrypoints.cc 39
    • 'c_protocols' pointer. websocket.cc 44
    • 'type_enum' pointer. pin_base_win.cc 96
    • 'pin_enum' pointer. filter_base_win.cc 75
    • 'port_data'. port_monitor.cc 388
    • 'xcv_data' pointer. port_monitor.cc 552
    • 'monitor_data'. port_monitor.cc 625
    • 'sender_' pointer. crash_service.cc 221
    • 'cache' pointer. crash_cache.cc 269
    • 'current_browser' pointer. print_preview_dialog_controller.cc 403
    • 'udp_socket' pointer. network_stats.cc 212
    • 'popup_' pointer. try_chrome_dialog_view.cc 90


    Замеченное N12 — тесты, которые плохо тестируют


    Юнит тесты замечательная технология повышения качества программ. Однако сами тесты нередко содержат ошибки, в результате чего не выполняют свои функции. Писать тесты для тестов это конечно перебор. Помочь может статический анализ кода. Подробнее эту мысль я рассматривал в статье "Как статический анализ дополняет TDD".

    Приведу некоторые примеры ошибок, встретившиеся мне в тестах для Chromium:
    std::string TestAudioConfig::TestValidConfigs() {
      ....
      static const uint32_t kRequestFrameCounts[] = {
        PP_AUDIOMINSAMPLEFRAMECOUNT,
        PP_AUDIOMAXSAMPLEFRAMECOUNT,
        1024,
        2048,
        4096
      };
      ....
      for (size_t j = 0;
        j < sizeof(kRequestFrameCounts)/sizeof(kRequestFrameCounts);
        j++) {
      ....
    }

    V501 There are identical sub-expressions 'sizeof (kRequestFrameCounts)' to the left and to the right of the '/' operator. test_audio_config.cc 56

    В цикле выполнятся только один тест. Ошибка в том, что «sizeof(kRequestFrameCounts)/sizeof(kRequestFrameCounts)» равняется единице. Правильное выражение: «sizeof(kRequestFrameCounts)/sizeof(kRequestFrameCounts[0])».

    Другой ошибочный тест:
    void DiskCacheEntryTest::ExternalSyncIOBackground(....) {
      ....
      scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1));
      scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize2));
      ....
      EXPECT_EQ(0, memcmp(buffer2->data(), buffer2->data(), 10000));
      ....
    }

    V549 The first argument of 'memcmp' function is equal to the second argument. entry_unittest.cc 393

    Функция «memcmp()» сравнивает буфер сам с собой. В результате тест не выполняет требуемую проверку. Видимо, здесь должно быть:
    EXPECT_EQ(0, memcmp(buffer1->data(), buffer2->data(), 10000));

    А вот тест, который может неожиданно сломать другие тесты:
    static const int kNumPainters = 3;
    
    static const struct {
      const char* name;
      GPUPainter* painter;
    } painters[] = {
      { "CPU CSC + GPU Render", new CPUColorPainter() },
      { "GPU CSC/Render", new GPUColorWithLuminancePainter() },
    };
    
    int main(int argc, char** argv) {
      ....
      // Run GPU painter tests.
      for (int i = 0; i < kNumPainters; i++) {
        scoped_ptr<GPUPainter> painter(painters[i].painter);
      ....  
    }

    V557 Array overrun is possible. The value of 'i' index could reach 2. shader_bench.cc 152

    Возможно, раньше массив 'painters' состоял из трех элементов. Теперь их только два. А значение константы 'kNumPainters' осталось равно 3.

    Некоторые другие места в тестах, которые, как мне кажется, заслуживают внимания:

    V579 The string function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. syncable_unittest.cc 1790

    V579 The string function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. syncable_unittest.cc 1800

    V579 The string function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. syncable_unittest.cc 1810

    V595 The 'browser' pointer was utilized before it was verified against nullptr. Check lines: 5489, 5493. testing_automation_provider.cc 5489

    V595 The 'waiting_for_.get()' pointer was utilized before it was verified against nullptr. Check lines: 205, 222. downloads_api_unittest.cc 205

    V595 The 'pNPWindow' pointer was utilized before it was verified against nullptr. Check lines: 34, 35. plugin_windowed_test.cc 34

    V595 The 'pNPWindow' pointer was utilized before it was verified against nullptr. Check lines: 16, 20. plugin_window_size_test.cc 16

    V595 The 'textfield_view_' pointer was utilized before it was verified against nullptr. Check lines: 182, 191. native_textfield_views_unittest.cc 182

    V595 The 'message_loop_' pointer was utilized before it was verified against nullptr. Check lines: 53, 55. test_flash_message_loop.cc 53

    Замеченное N13 — Функция с переменным количеством аргументов


    Во всех программах, много дефектов встречается в коде, предназначенного для обработки ошибок и реакции на некорректные входные данные. Это обусловлено тем, что такие места сложно тестировать. И, как правило, они и не тестируются. В результате, при возникновении ошибки, программы начинают вести себя намного более причудливо, чем планировалось.

    Пример:
    DWORD GetLastError(VOID);
    
    void TryOpenFile(wchar_t *path, FILE *output) {
      wchar_t path_expanded[MAX_PATH] = {0};
      DWORD size = ::ExpandEnvironmentStrings(
        path, path_expanded, MAX_PATH - 1);
      if (!size) {
        fprintf(output,
                "[ERROR] Cannot expand \"%S\". Error %S.\r\n",
                path, ::GetLastError());
      }
      ....
    }

    V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The pointer to string of wchar_t type symbols is expected. fs.cc 17

    Если переменная 'size' равна нулю, программа попытается записать в файл текстовое сообщение. Но это сообщение, скорее всего, будет содержать в конце биллиберду. Более того, этот код может привести к access violation.

    Для записи использует функция fprintf(). Это функция не контролирует типы своих аргументов. Она ожидает, что последним аргументом должен быть указатель на строку. Но на самом деле, фактическим аргументом является число (код ошибки). Это число будет преобразован в адрес и как дальше поведёт себя программа, неизвестно.

    Незамеченное


    Ещё раз повторю, что я просматривал список сообщений поверхностно. Я привел в этой статье только то, что привлекло моё внимание. Более того, заметил я больше, чем написал в статье. Описывая всё, я получу слишком длинную статью. А она и так уже великовата.

    Я отбросил очень многие фрагменты кода, которые как мне кажется, будут не очень интересны читателю. Приведу пару примеров для ясности.
    bool ManagedUserService::UserMayLoad(
      const extensions::Extension* extension,
      string16* error) const
    {
      if (extension_service &&
          extension_service->GetInstalledExtension(extension->id()))
        return true;
    
      if (extension) {
        bool was_installed_by_default =
          extension->was_installed_by_default();
        .....
      }
    }

    V595 The 'extension' pointer was utilized before it was verified against nullptr. Check lines: 277, 280. managed_user_service.cc 277

    В начале указатель 'extension' разыменовывается в выражении «extension->id()». Затем этот указатель проверяют на равенство нулю.

    Часто, подобный код не содержит ошибок. Бывает, что указатель просто не может быть равен нулю и проверка избыточна. Поэтому перечислять такие места нет смысла, так как я могу ошибиться и выдать вполне рабочий код за ошибочный.

    Вот ещё пример диагностики, которую я предпочёл не заметить:
    bool WebMClusterParser::ParseBlock(....)
    {
      int timecode = buf[1] << 8 | buf[2];
      ....
      if (timecode & 0x8000)
        timecode |= (-1 << 16);
      ....
    }

    V610 Undefined behavior. Check the shift operator '<<. The left operand '-1' is negative. webm_cluster_parser.cc 217

    Формально сдвиг отрицательного значения приводит к неопределённому поведению. Однако, многие компиляторы ведут стабильно и именно так, как ожидает программист. В результате этот код долго и успешно работает, хотя не обязан. Мне не хочется сейчас сражаться с этим, и я пропущу подобные сообщения. Для тех, кто хочет подробнее разобраться в вопросе, рекомендую статью "Не зная брода, не лезь в воду — часть третья".

    О ложных срабатываниях


    Мне часто задают вопрос:

    В статьях вы очень ловко приводите примеры найденных ошибок. Но вы не говорите, каково общее количество выдаваемых сообщений. Часто статические анализаторы выдают очень много ложных срабатываний и среди них практически невозможно отыскать настоящие ошибки. Как дело обстоит с анализатором PVS-Studio?

    И я всегда не знаю, что сходу ответить на этот вопрос. У меня есть два противоположных ответа: первый — много, второй — мало. Все зависит, как подойти к рассмотрению выданного списка сообщения. Сейчас на примере Chromium я попробую объяснить суть такой двойственности.

    Анализатор PVS-Studio выдал 3582 предупреждений первого уровня (набор правил общего назначения GA). Это очень много. И в большинстве этих сообщений являются ложными. Если подойти «в лоб» и начать сразу просматривать весь список, то это очень быстро надоест. И впечатление будет ужасное. Одни сплошные однотипные ложные срабатывания. Ничего интересного не попадается. Плохой инструмент.

    Ошибка такого пользователя в том, что не выполнена даже минимальная настройка инструмента. Да, мы стараемся сделать инструмент PVS-Studio таким, чтобы он работал сразу после установки. Мы стараемся, чтобы ничего не надо было настраивать. Человек должен просто проверить свой проект и изучить список выданных предупреждений.

    Однако так не всегда получается. Так не получилось и с Chromium. Например, огромное количество ложных сообщений возникло из-за макроса 'DVLOG'. Этот макрос что-то логирует. Он написан хитро и PVS-Studio ошибочно посчитал, что в нем может быть ошибка. Поскольку, макрос очень активно используется, ложных сообщений получилось очень много. Можно сказать, что сколько раз используется макрос DVLOG, столько ложных предупреждений попадет в отчет. А именно, о макросе было выдано около 2300 ложных сообщений «V501 There are identical sub-expressions.....».

    Можно подавить эти предупреждения, вписав в заголовочный файл, рядом с объявлением макроса, вот такой вот комментарий:

    //-V:DVLOG:501

    Смотрите, таким простым действием мы вычтем из 3582 сообщений, 2300 ложных. Мы сразу отсеяли 65% сообщений. И теперь нам не надо их зря просматривать.

    Без особых усилий можно сделать ещё несколько подобных уточнений и настроек. В результате, большинство ложных срабатываний исчезнут. В некоторых случаях после настройки следует перезапускать анализ, в некоторых нет. Подробнее всё это описывает в документации раздел "Подавление ложных предупреждений". Конкретно, в случае с ошибками в макросах, придется перезапускать анализ.

    Надеюсь теперь понятно. Почему первый ответ — ложных срабатываний много. И почему второй ответ — ложных срабатываний мало. Всё зависит, готов ли человек потратить хоть немного времени на изучение продукта и способов избавить себя от лишних сообщений.

    Напутствие читателям


    Пользуясь случаем, хочу передать привет родителям. Ой нет, это что-то не то. Пользуясь случаем, хочу передать привет программистам и напомнить, что:
    • Ответ на вопрос «Вы сообщили о найденных в проекте ошибках разработчикам?», находится в заметке "Ответы на вопросы, которые часто задают после прочтения наших статей".
    • С нами лучше всего связаться и задать вопросы, используя форму обратной связи на нашем сайте. Прошу не использовать для этого twitter, комментарии к статьям где-то на сторонних сайтах и так далее.
    • Приглашаю присоединиться к нашему твиттеру: @Code_Analysis. Я постоянно собираю и публикую интересные ссылки по тематике программирования и языку Си++.
    PVS-Studio
    Static Code Analysis for C, C++, C# and Java

    Комментарии 58

      +12
      Если кто-то не знает, что такое цикломатическая сложность 1000, то пусть и не знает. Психическое здоровье будет лучше :). В общем, много это.

      Воодушевленно прочел первую часть предложения, но… пошел в Википедию.
        +2
        Блин, из-за напутствия не удалось стать первым, кто задал вопрос «Сообщили ли о найденных ошибках?» :D

        Поэтому задам другой вопрос — сколько для хромиума вам пришлось раз менять исходный код, чтобы задавить «левые» ошибки?
        И почему нельзя постфактум задавить ошибку от макроса?
          0
          Вы имеете ввиду код хромиума или код анализатора?
            0
            «Можно подавить эти предупреждения, вписав в заголовочный файл, рядом с объявлением макроса, вот такой вот комментарий:
            //-V:DVLOG:501»
            то есть, таки в код хромиума.

            хотя после вопроса мне стало интересно — а в коде анализатора что-либо правилось, по результату прогона про хромиуму?
              0
              Да, правилось. Что к чему именно относится, долго смотреть. Но суммарно по результатам проверок проектов Chromium и MTASA были внесены улучшения в следующие диагностики: V502, V512, V517, V519, V521, V523, V531, V536, V542, V550, V584, V595, V620, V634, V656, V620, V653, V654, V659, V661, V669, V670.
                0
                Ого. А теперь представим, что я купил анализатор, прогнал проект, получил тонну мусора.
                Мне надо писать вам, дабы вы улучшили анализатор, или всё давить со своей стороны и жить с тем, что есть?
                  +7
                  То есть если бы ответ выше был «ничего не улучшали в анализаторе, даже и не думали», то это был бы «правильный» ответ? :-). Всегда работая с проектами мы стараемся улучшать анализатор. «Стыдиться» нам здесь совершенно нечего.

                  По сути вопроса 1. Да, пользователь может много подавить самостоятельно и в этом и заключается в том числе работа с инструментом. 2. Да, многие пользователи нам пишут предложения по сокращению ложных срабатываний и мы их реализуем.
                    0
                    То есть таки по сути вопроса: «нет, не надо, но да, можно». Спасибо :)
                    0
                    На каждый конкретный вид ложного срабатывания надо смотреть отдельно. Что-то из очевидного, Вы скорее всего подавите сами. Про остальное можно написать. Мы отберем явные ляпы анализа или то, что может быть полезно другим. Это мы реализуем в виде правок в коде. По остальным моментам дадим рекомендации. Иногда, мы делаем для пользователей специальные настройки, которые другим не нужны. Например, пользователь вписывает себе волшебный комментарий //Сделать_мне_хорошо_с_диагностикой_Vxx и что-то работает немного по другому для его проектов.
              0
              > И почему нельзя постфактум задавить ошибку от макроса?

              Мы иногда вносим правки/исключения в правила, после проверки очередного проекта. Но не всегда конечно. Это помощь одному конкретному проекту. Не универсально слишком. Только код анализатора пухнет. Мы стараемся делать правки, если хотя бы теоретически, это может помочь и другим проектам.

              Конкретно с макросом DVLOG ничего делать не стал. Имя короткое и вполне возможно не уникальное (или в другом месте, это будет часть какого-то слова, например, FindVLogin). В другом проекте это может значить совсем другое и мы можем пропустить полезную диагностику.
                +3
                Под «постфактум» я имел ввиду как раз интерактивный обзор лога.
                Открылось 3к ошибок.
                Смотрю на 1ю, 2ю, 3ю, понимаю, что все они — от одного макроса; правой кнопкой => Ignore all macros DVLOG errors => профит, остались только остальные ошибки.

                Не вписывая "//-V:DVLOG:501" а просто постфактум, уже собрав всю информацию, выключить его — можно?
                  +1
                  Примерно так можно, да.
                    0
                    А в предыдущем треде утверждали, что надо менять исходники и перекомпиливать.
                    Так надо или нет?
                      0
                      Если говорить о механизме с использованием комментариев, то нужен перезапуск. Не путать с подавлением одиночных сообщений //-Vxxx — здесь не нужен :-).

                      А есть другой грубый механизм. Он убирает сообщения, где встретилась определенная фраза. Он доступен через настройки и перезапуск не нужен. Евгений видимо его имел в виду. Но для случая с DVLOG он бы не подошел.

                      А вообще, как то урывками объяснения получаются. :) Лучше сразу целиком прочитать "Подавление ложных предупреждений".
                        +1
                        Да я уже понял, что с макросами у вас объективно не очень, так как вы обрабатываете код после макросов, где информации о макросах просто не осталось.
                        Причем и посоветовать-то нечего. Правильный выход — разбирать и строить дерево самостоятельно — слишком накладно по расходам на разработку и поддержку в случае обновлений.

                        Ну и основная претензия на «Перезапустим анализ; мы получим на одно сообщение меньше» — если даёте интерактивный инструмент, в идеале, все сделанные вот такие (инструментом) правки должны вступать в силу немедленно. И удалять все false alarm'ы тут же и сразу. А этого явно нет.
              +30
              Медалька с перечеркиванием смотрится неоднозначно. Лучше сделать довольного единорога без радуги.
                +1
                У нас есть идея профессионально перерисовать такую медальку. Но как-то руки всё не доходят. :)
                  +9
                  Так был же вроде вот этот персонаж:

                  image

                  Почему бы и нет? Довольный и окрыленный от радости :)
                    0
                    Какое-то у него неправильное количество рук-ног
                      +1
                      Какой вы требовательный =)
                  +11
                  Да что там — однозначно плохо смотрится.
                  Перечеркивание — негатив, блюющий носорог — еще один.
                  И они не перемножаются, они складываются.
                  0
                  Замечательный инструмент! У меня только один вопрос: а когда и кто это исправит в chromium?
                    0
                    Хотите помочь сообществу и заработать денег? Вчера стартовал очередной этап проекта "Платим за ошибки в Chromium" от Google. Сообщите им об этих ошибках и кто знает, может повезет…
                    +4
                    Не так уж страшна эта ваша цикломатическая сложность 1106
                    bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token)
                    {
                        // If we have a token in progress, then we're supposed to be called back
                        // with the same token so we can finish it.
                        ASSERT(!m_token || m_token == &token || token.type() == HTMLToken::Uninitialized);
                        m_token = &token;
                    
                        if (!m_bufferedEndTagName.isEmpty() && !isEndTagBufferingState(m_state)) {
                            // FIXME: This should call flushBufferedEndTag().
                            // We started an end tag during our last iteration.
                            m_token->beginEndTag(m_bufferedEndTagName);
                            m_bufferedEndTagName.clear();
                            m_appropriateEndTagName.clear();
                            m_temporaryBuffer.clear();
                            if (m_state == HTMLTokenizer::DataState) {
                                // We're back in the data state, so we must be done with the tag.
                                return true;
                            }
                        }
                    
                        if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source))
                            return haveBufferedCharacterToken();
                        UChar cc = m_inputStreamPreprocessor.nextInputCharacter();
                    
                        // Source: http://www.whatwg.org/specs/web-apps/current-work/#tokenisation0
                        switch (m_state) {
                        HTML_BEGIN_STATE(DataState) {
                            if (cc == '&')
                                HTML_ADVANCE_TO(CharacterReferenceInDataState);
                            else if (cc == '<') {
                                if (m_token->type() == HTMLToken::Character) {
                                    // We have a bunch of character tokens queued up that we
                                    // are emitting lazily here.
                                    return true;
                                }
                                HTML_ADVANCE_TO(TagOpenState);
                            } else if (cc == kEndOfFileMarker)
                                return emitEndOfFile(source);
                            else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(DataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CharacterReferenceInDataState) {
                            if (!processEntity(source))
                                return haveBufferedCharacterToken();
                            HTML_SWITCH_TO(DataState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RCDATAState) {
                            if (cc == '&')
                                HTML_ADVANCE_TO(CharacterReferenceInRCDATAState);
                            else if (cc == '<')
                                HTML_ADVANCE_TO(RCDATALessThanSignState);
                            else if (cc == kEndOfFileMarker)
                                return emitEndOfFile(source);
                            else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(RCDATAState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CharacterReferenceInRCDATAState) {
                            if (!processEntity(source))
                                return haveBufferedCharacterToken();
                            HTML_SWITCH_TO(RCDATAState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RAWTEXTState) {
                            if (cc == '<')
                                HTML_ADVANCE_TO(RAWTEXTLessThanSignState);
                            else if (cc == kEndOfFileMarker)
                                return emitEndOfFile(source);
                            else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(RAWTEXTState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataState) {
                            if (cc == '<')
                                HTML_ADVANCE_TO(ScriptDataLessThanSignState);
                            else if (cc == kEndOfFileMarker)
                                return emitEndOfFile(source);
                            else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(PLAINTEXTState) {
                            if (cc == kEndOfFileMarker)
                                return emitEndOfFile(source);
                            bufferCharacter(cc);
                            HTML_ADVANCE_TO(PLAINTEXTState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(TagOpenState) {
                            if (cc == '!')
                                HTML_ADVANCE_TO(MarkupDeclarationOpenState);
                            else if (cc == '/')
                                HTML_ADVANCE_TO(EndTagOpenState);
                            else if (isASCIIUpper(cc)) {
                                m_token->beginStartTag(toLowerCase(cc));
                                HTML_ADVANCE_TO(TagNameState);
                            } else if (isASCIILower(cc)) {
                                m_token->beginStartTag(cc);
                                HTML_ADVANCE_TO(TagNameState);
                            } else if (cc == '?') {
                                parseError();
                                // The spec consumes the current character before switching
                                // to the bogus comment state, but it's easier to implement
                                // if we reconsume the current character.
                                HTML_RECONSUME_IN(BogusCommentState);
                            } else {
                                parseError();
                                bufferCharacter('<');
                                HTML_RECONSUME_IN(DataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(EndTagOpenState) {
                            if (isASCIIUpper(cc)) {
                                m_token->beginEndTag(static_cast<LChar>(toLowerCase(cc)));
                                m_appropriateEndTagName.clear();
                                HTML_ADVANCE_TO(TagNameState);
                            } else if (isASCIILower(cc)) {
                                m_token->beginEndTag(static_cast<LChar>(cc));
                                m_appropriateEndTagName.clear();
                                HTML_ADVANCE_TO(TagNameState);
                            } else if (cc == '>') {
                                parseError();
                                HTML_ADVANCE_TO(DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                bufferCharacter('<');
                                bufferCharacter('/');
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                parseError();
                                HTML_RECONSUME_IN(BogusCommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(TagNameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeAttributeNameState);
                            else if (cc == '/')
                                HTML_ADVANCE_TO(SelfClosingStartTagState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (isASCIIUpper(cc)) {
                                m_token->appendToName(toLowerCase(cc));
                                HTML_ADVANCE_TO(TagNameState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                m_token->appendToName(cc);
                                HTML_ADVANCE_TO(TagNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RCDATALessThanSignState) {
                            if (cc == '/') {
                                m_temporaryBuffer.clear();
                                ASSERT(m_bufferedEndTagName.isEmpty());
                                HTML_ADVANCE_TO(RCDATAEndTagOpenState);
                            } else {
                                bufferCharacter('<');
                                HTML_RECONSUME_IN(RCDATAState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RCDATAEndTagOpenState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(RCDATAEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(RCDATAEndTagNameState);
                            } else {
                                bufferCharacter('<');
                                bufferCharacter('/');
                                HTML_RECONSUME_IN(RCDATAState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RCDATAEndTagNameState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(RCDATAEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(RCDATAEndTagNameState);
                            } else {
                                if (isTokenizerWhitespace(cc)) {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState);
                                    }
                                } else if (cc == '/') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState);
                                    }
                                } else if (cc == '>') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        return flushEmitAndResumeIn(source, HTMLTokenizer::DataState);
                                    }
                                }
                                bufferCharacter('<');
                                bufferCharacter('/');
                                m_token->appendToCharacter(m_temporaryBuffer);
                                m_bufferedEndTagName.clear();
                                m_temporaryBuffer.clear();
                                HTML_RECONSUME_IN(RCDATAState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RAWTEXTLessThanSignState) {
                            if (cc == '/') {
                                m_temporaryBuffer.clear();
                                ASSERT(m_bufferedEndTagName.isEmpty());
                                HTML_ADVANCE_TO(RAWTEXTEndTagOpenState);
                            } else {
                                bufferCharacter('<');
                                HTML_RECONSUME_IN(RAWTEXTState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RAWTEXTEndTagOpenState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(RAWTEXTEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(RAWTEXTEndTagNameState);
                            } else {
                                bufferCharacter('<');
                                bufferCharacter('/');
                                HTML_RECONSUME_IN(RAWTEXTState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(RAWTEXTEndTagNameState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(RAWTEXTEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(RAWTEXTEndTagNameState);
                            } else {
                                if (isTokenizerWhitespace(cc)) {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState);
                                    }
                                } else if (cc == '/') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState);
                                    }
                                } else if (cc == '>') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        return flushEmitAndResumeIn(source, HTMLTokenizer::DataState);
                                    }
                                }
                                bufferCharacter('<');
                                bufferCharacter('/');
                                m_token->appendToCharacter(m_temporaryBuffer);
                                m_bufferedEndTagName.clear();
                                m_temporaryBuffer.clear();
                                HTML_RECONSUME_IN(RAWTEXTState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataLessThanSignState) {
                            if (cc == '/') {
                                m_temporaryBuffer.clear();
                                ASSERT(m_bufferedEndTagName.isEmpty());
                                HTML_ADVANCE_TO(ScriptDataEndTagOpenState);
                            } else if (cc == '!') {
                                bufferCharacter('<');
                                bufferCharacter('!');
                                HTML_ADVANCE_TO(ScriptDataEscapeStartState);
                            } else {
                                bufferCharacter('<');
                                HTML_RECONSUME_IN(ScriptDataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEndTagOpenState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(ScriptDataEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataEndTagNameState);
                            } else {
                                bufferCharacter('<');
                                bufferCharacter('/');
                                HTML_RECONSUME_IN(ScriptDataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEndTagNameState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(ScriptDataEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataEndTagNameState);
                            } else {
                                if (isTokenizerWhitespace(cc)) {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState);
                                    }
                                } else if (cc == '/') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState);
                                    }
                                } else if (cc == '>') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        return flushEmitAndResumeIn(source, HTMLTokenizer::DataState);
                                    }
                                }
                                bufferCharacter('<');
                                bufferCharacter('/');
                                m_token->appendToCharacter(m_temporaryBuffer);
                                m_bufferedEndTagName.clear();
                                m_temporaryBuffer.clear();
                                HTML_RECONSUME_IN(ScriptDataState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapeStartState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapeStartDashState);
                            } else
                                HTML_RECONSUME_IN(ScriptDataState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapeStartDashState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedDashDashState);
                            } else
                                HTML_RECONSUME_IN(ScriptDataState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedDashState);
                            } else if (cc == '<')
                                HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedDashState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedDashDashState);
                            } else if (cc == '<')
                                HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedDashDashState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedDashDashState);
                            } else if (cc == '<')
                                HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState);
                            else if (cc == '>') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedLessThanSignState) {
                            if (cc == '/') {
                                m_temporaryBuffer.clear();
                                ASSERT(m_bufferedEndTagName.isEmpty());
                                HTML_ADVANCE_TO(ScriptDataEscapedEndTagOpenState);
                            } else if (isASCIIUpper(cc)) {
                                bufferCharacter('<');
                                bufferCharacter(cc);
                                m_temporaryBuffer.clear();
                                m_temporaryBuffer.append(toLowerCase(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState);
                            } else if (isASCIILower(cc)) {
                                bufferCharacter('<');
                                bufferCharacter(cc);
                                m_temporaryBuffer.clear();
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState);
                            } else {
                                bufferCharacter('<');
                                HTML_RECONSUME_IN(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedEndTagOpenState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState);
                            } else {
                                bufferCharacter('<');
                                bufferCharacter('/');
                                HTML_RECONSUME_IN(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataEscapedEndTagNameState) {
                            if (isASCIIUpper(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(toLowerCase(cc)));
                                HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState);
                            } else if (isASCIILower(cc)) {
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                addToPossibleEndTag(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState);
                            } else {
                                if (isTokenizerWhitespace(cc)) {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState);
                                    }
                                } else if (cc == '/') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState);
                                    }
                                } else if (cc == '>') {
                                    if (isAppropriateEndTag()) {
                                        m_temporaryBuffer.append(static_cast<LChar>(cc));
                                        return flushEmitAndResumeIn(source, HTMLTokenizer::DataState);
                                    }
                                }
                                bufferCharacter('<');
                                bufferCharacter('/');
                                m_token->appendToCharacter(m_temporaryBuffer);
                                m_bufferedEndTagName.clear();
                                m_temporaryBuffer.clear();
                                HTML_RECONSUME_IN(ScriptDataEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapeStartState) {
                            if (isTokenizerWhitespace(cc) || cc == '/' || cc == '>') {
                                bufferCharacter(cc);
                                if (temporaryBufferIs(scriptTag.localName()))
                                    HTML_ADVANCE_TO(ScriptDataDoubleEscapedState);
                                else
                                    HTML_ADVANCE_TO(ScriptDataEscapedState);
                            } else if (isASCIIUpper(cc)) {
                                bufferCharacter(cc);
                                m_temporaryBuffer.append(toLowerCase(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState);
                            } else if (isASCIILower(cc)) {
                                bufferCharacter(cc);
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState);
                            } else
                                HTML_RECONSUME_IN(ScriptDataEscapedState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapedState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashState);
                            } else if (cc == '<') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapedDashState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashDashState);
                            } else if (cc == '<') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapedDashDashState) {
                            if (cc == '-') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashDashState);
                            } else if (cc == '<') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState);
                            } else if (cc == '>') {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapedLessThanSignState) {
                            if (cc == '/') {
                                bufferCharacter(cc);
                                m_temporaryBuffer.clear();
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState);
                            } else
                                HTML_RECONSUME_IN(ScriptDataDoubleEscapedState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ScriptDataDoubleEscapeEndState) {
                            if (isTokenizerWhitespace(cc) || cc == '/' || cc == '>') {
                                bufferCharacter(cc);
                                if (temporaryBufferIs(scriptTag.localName()))
                                    HTML_ADVANCE_TO(ScriptDataEscapedState);
                                else
                                    HTML_ADVANCE_TO(ScriptDataDoubleEscapedState);
                            } else if (isASCIIUpper(cc)) {
                                bufferCharacter(cc);
                                m_temporaryBuffer.append(toLowerCase(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState);
                            } else if (isASCIILower(cc)) {
                                bufferCharacter(cc);
                                m_temporaryBuffer.append(static_cast<LChar>(cc));
                                HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState);
                            } else
                                HTML_RECONSUME_IN(ScriptDataDoubleEscapedState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BeforeAttributeNameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeAttributeNameState);
                            else if (cc == '/')
                                HTML_ADVANCE_TO(SelfClosingStartTagState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (isASCIIUpper(cc)) {
                                m_token->addNewAttribute();
                                m_token->beginAttributeName(source.numberOfCharactersConsumed());
                                m_token->appendToAttributeName(toLowerCase(cc));
                                HTML_ADVANCE_TO(AttributeNameState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                if (cc == '"' || cc == '\'' || cc == '<' || cc == '=')
                                    parseError();
                                m_token->addNewAttribute();
                                m_token->beginAttributeName(source.numberOfCharactersConsumed());
                                m_token->appendToAttributeName(cc);
                                HTML_ADVANCE_TO(AttributeNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AttributeNameState) {
                            if (isTokenizerWhitespace(cc)) {
                                m_token->endAttributeName(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(AfterAttributeNameState);
                            } else if (cc == '/') {
                                m_token->endAttributeName(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(SelfClosingStartTagState);
                            } else if (cc == '=') {
                                m_token->endAttributeName(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(BeforeAttributeValueState);
                            } else if (cc == '>') {
                                m_token->endAttributeName(source.numberOfCharactersConsumed());
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (isASCIIUpper(cc)) {
                                m_token->appendToAttributeName(toLowerCase(cc));
                                HTML_ADVANCE_TO(AttributeNameState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->endAttributeName(source.numberOfCharactersConsumed());
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                if (cc == '"' || cc == '\'' || cc == '<' || cc == '=')
                                    parseError();
                                m_token->appendToAttributeName(cc);
                                HTML_ADVANCE_TO(AttributeNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterAttributeNameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(AfterAttributeNameState);
                            else if (cc == '/')
                                HTML_ADVANCE_TO(SelfClosingStartTagState);
                            else if (cc == '=')
                                HTML_ADVANCE_TO(BeforeAttributeValueState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (isASCIIUpper(cc)) {
                                m_token->addNewAttribute();
                                m_token->beginAttributeName(source.numberOfCharactersConsumed());
                                m_token->appendToAttributeName(toLowerCase(cc));
                                HTML_ADVANCE_TO(AttributeNameState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                if (cc == '"' || cc == '\'' || cc == '<')
                                    parseError();
                                m_token->addNewAttribute();
                                m_token->beginAttributeName(source.numberOfCharactersConsumed());
                                m_token->appendToAttributeName(cc);
                                HTML_ADVANCE_TO(AttributeNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BeforeAttributeValueState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeAttributeValueState);
                            else if (cc == '"') {
                                m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1);
                                HTML_ADVANCE_TO(AttributeValueDoubleQuotedState);
                            } else if (cc == '&') {
                                m_token->beginAttributeValue(source.numberOfCharactersConsumed());
                                HTML_RECONSUME_IN(AttributeValueUnquotedState);
                            } else if (cc == '\'') {
                                m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1);
                                HTML_ADVANCE_TO(AttributeValueSingleQuotedState);
                            } else if (cc == '>') {
                                parseError();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                if (cc == '<' || cc == '=' || cc == '`')
                                    parseError();
                                m_token->beginAttributeValue(source.numberOfCharactersConsumed());
                                m_token->appendToAttributeValue(cc);
                                HTML_ADVANCE_TO(AttributeValueUnquotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AttributeValueDoubleQuotedState) {
                            if (cc == '"') {
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(AfterAttributeValueQuotedState);
                            } else if (cc == '&') {
                                m_additionalAllowedCharacter = '"';
                                HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                m_token->appendToAttributeValue(cc);
                                HTML_ADVANCE_TO(AttributeValueDoubleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AttributeValueSingleQuotedState) {
                            if (cc == '\'') {
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(AfterAttributeValueQuotedState);
                            } else if (cc == '&') {
                                m_additionalAllowedCharacter = '\'';
                                HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                m_token->appendToAttributeValue(cc);
                                HTML_ADVANCE_TO(AttributeValueSingleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AttributeValueUnquotedState) {
                            if (isTokenizerWhitespace(cc)) {
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_ADVANCE_TO(BeforeAttributeNameState);
                            } else if (cc == '&') {
                                m_additionalAllowedCharacter = '>';
                                HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState);
                            } else if (cc == '>') {
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->endAttributeValue(source.numberOfCharactersConsumed());
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                if (cc == '"' || cc == '\'' || cc == '<' || cc == '=' || cc == '`')
                                    parseError();
                                m_token->appendToAttributeValue(cc);
                                HTML_ADVANCE_TO(AttributeValueUnquotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CharacterReferenceInAttributeValueState) {
                            bool notEnoughCharacters = false;
                            DecodedHTMLEntity decodedEntity;
                            bool success = consumeHTMLEntity(source, decodedEntity, notEnoughCharacters, m_additionalAllowedCharacter);
                            if (notEnoughCharacters)
                                return haveBufferedCharacterToken();
                            if (!success) {
                                ASSERT(decodedEntity.isEmpty());
                                m_token->appendToAttributeValue('&');
                            } else {
                                for (unsigned i = 0; i < decodedEntity.length; ++i)
                                    m_token->appendToAttributeValue(decodedEntity.data[i]);
                            }
                            // We're supposed to switch back to the attribute value state that
                            // we were in when we were switched into this state. Rather than
                            // keeping track of this explictly, we observe that the previous
                            // state can be determined by m_additionalAllowedCharacter.
                            if (m_additionalAllowedCharacter == '"')
                                HTML_SWITCH_TO(AttributeValueDoubleQuotedState);
                            else if (m_additionalAllowedCharacter == '\'')
                                HTML_SWITCH_TO(AttributeValueSingleQuotedState);
                            else if (m_additionalAllowedCharacter == '>')
                                HTML_SWITCH_TO(AttributeValueUnquotedState);
                            else
                                ASSERT_NOT_REACHED();
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterAttributeValueQuotedState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeAttributeNameState);
                            else if (cc == '/')
                                HTML_ADVANCE_TO(SelfClosingStartTagState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                parseError();
                                HTML_RECONSUME_IN(BeforeAttributeNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(SelfClosingStartTagState) {
                            if (cc == '>') {
                                m_token->setSelfClosing();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                HTML_RECONSUME_IN(DataState);
                            } else {
                                parseError();
                                HTML_RECONSUME_IN(BeforeAttributeNameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BogusCommentState) {
                            m_token->beginComment();
                            HTML_RECONSUME_IN(ContinueBogusCommentState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(ContinueBogusCommentState) {
                            if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker)
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            else {
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(ContinueBogusCommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(MarkupDeclarationOpenState) {
                            DEFINE_STATIC_LOCAL(String, dashDashString, ("--"));
                            DEFINE_STATIC_LOCAL(String, doctypeString, ("doctype"));
                            DEFINE_STATIC_LOCAL(String, cdataString, ("[CDATA["));
                            if (cc == '-') {
                                SegmentedString::LookAheadResult result = source.lookAhead(dashDashString);
                                if (result == SegmentedString::DidMatch) {
                                    source.advanceAndASSERT('-');
                                    source.advanceAndASSERT('-');
                                    m_token->beginComment();
                                    HTML_SWITCH_TO(CommentStartState);
                                } else if (result == SegmentedString::NotEnoughCharacters)
                                    return haveBufferedCharacterToken();
                            } else if (cc == 'D' || cc == 'd') {
                                SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(doctypeString);
                                if (result == SegmentedString::DidMatch) {
                                    advanceStringAndASSERTIgnoringCase(source, "doctype");
                                    HTML_SWITCH_TO(DOCTYPEState);
                                } else if (result == SegmentedString::NotEnoughCharacters)
                                    return haveBufferedCharacterToken();
                            } else if (cc == '[' && shouldAllowCDATA()) {
                                SegmentedString::LookAheadResult result = source.lookAhead(cdataString);
                                if (result == SegmentedString::DidMatch) {
                                    advanceStringAndASSERT(source, "[CDATA[");
                                    HTML_SWITCH_TO(CDATASectionState);
                                } else if (result == SegmentedString::NotEnoughCharacters)
                                    return haveBufferedCharacterToken();
                            }
                            parseError();
                            HTML_RECONSUME_IN(BogusCommentState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentStartState) {
                            if (cc == '-')
                                HTML_ADVANCE_TO(CommentStartDashState);
                            else if (cc == '>') {
                                parseError();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentStartDashState) {
                            if (cc == '-')
                                HTML_ADVANCE_TO(CommentEndState);
                            else if (cc == '>') {
                                parseError();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToComment('-');
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentState) {
                            if (cc == '-')
                                HTML_ADVANCE_TO(CommentEndDashState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentEndDashState) {
                            if (cc == '-')
                                HTML_ADVANCE_TO(CommentEndState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToComment('-');
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentEndState) {
                            if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == '!') {
                                parseError();
                                HTML_ADVANCE_TO(CommentEndBangState);
                            } else if (cc == '-') {
                                parseError();
                                m_token->appendToComment('-');
                                HTML_ADVANCE_TO(CommentEndState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->appendToComment('-');
                                m_token->appendToComment('-');
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CommentEndBangState) {
                            if (cc == '-') {
                                m_token->appendToComment('-');
                                m_token->appendToComment('-');
                                m_token->appendToComment('!');
                                HTML_ADVANCE_TO(CommentEndDashState);
                            } else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToComment('-');
                                m_token->appendToComment('-');
                                m_token->appendToComment('!');
                                m_token->appendToComment(cc);
                                HTML_ADVANCE_TO(CommentState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPEState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPENameState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->beginDOCTYPE();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                HTML_RECONSUME_IN(BeforeDOCTYPENameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BeforeDOCTYPENameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPENameState);
                            else if (isASCIIUpper(cc)) {
                                m_token->beginDOCTYPE(toLowerCase(cc));
                                HTML_ADVANCE_TO(DOCTYPENameState);
                            } else if (cc == '>') {
                                parseError();
                                m_token->beginDOCTYPE();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->beginDOCTYPE();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->beginDOCTYPE(cc);
                                HTML_ADVANCE_TO(DOCTYPENameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPENameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(AfterDOCTYPENameState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (isASCIIUpper(cc)) {
                                m_token->appendToName(toLowerCase(cc));
                                HTML_ADVANCE_TO(DOCTYPENameState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToName(cc);
                                HTML_ADVANCE_TO(DOCTYPENameState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterDOCTYPENameState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(AfterDOCTYPENameState);
                            if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                DEFINE_STATIC_LOCAL(String, publicString, ("public"));
                                DEFINE_STATIC_LOCAL(String, systemString, ("system"));
                                if (cc == 'P' || cc == 'p') {
                                    SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(publicString);
                                    if (result == SegmentedString::DidMatch) {
                                        advanceStringAndASSERTIgnoringCase(source, "public");
                                        HTML_SWITCH_TO(AfterDOCTYPEPublicKeywordState);
                                    } else if (result == SegmentedString::NotEnoughCharacters)
                                        return haveBufferedCharacterToken();
                                } else if (cc == 'S' || cc == 's') {
                                    SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(systemString);
                                    if (result == SegmentedString::DidMatch) {
                                        advanceStringAndASSERTIgnoringCase(source, "system");
                                        HTML_SWITCH_TO(AfterDOCTYPESystemKeywordState);
                                    } else if (result == SegmentedString::NotEnoughCharacters)
                                        return haveBufferedCharacterToken();
                                }
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterDOCTYPEPublicKeywordState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState);
                            else if (cc == '"') {
                                parseError();
                                m_token->setPublicIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                parseError();
                                m_token->setPublicIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
                            } else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BeforeDOCTYPEPublicIdentifierState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState);
                            else if (cc == '"') {
                                m_token->setPublicIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                m_token->setPublicIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
                            } else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuotedState) {
                            if (cc == '"')
                                HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState);
                            else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToPublicIdentifier(cc);
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuotedState) {
                            if (cc == '\'')
                                HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState);
                            else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToPublicIdentifier(cc);
                                HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterDOCTYPEPublicIdentifierState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == '"') {
                                parseError();
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                parseError();
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiersState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == '"') {
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterDOCTYPESystemKeywordState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState);
                            else if (cc == '"') {
                                parseError();
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                parseError();
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
                            } else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BeforeDOCTYPESystemIdentifierState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState);
                            if (cc == '"') {
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
                            } else if (cc == '\'') {
                                m_token->setSystemIdentifierToEmptyString();
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
                            } else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                m_token->setForceQuirks();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuotedState) {
                            if (cc == '"')
                                HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
                            else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToSystemIdentifier(cc);
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(DOCTYPESystemIdentifierSingleQuotedState) {
                            if (cc == '\'')
                                HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
                            else if (cc == '>') {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            } else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                m_token->appendToSystemIdentifier(cc);
                                HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(AfterDOCTYPESystemIdentifierState) {
                            if (isTokenizerWhitespace(cc))
                                HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState);
                            else if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker) {
                                parseError();
                                m_token->setForceQuirks();
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            } else {
                                parseError();
                                HTML_ADVANCE_TO(BogusDOCTYPEState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(BogusDOCTYPEState) {
                            if (cc == '>')
                                return emitAndResumeIn(source, HTMLTokenizer::DataState);
                            else if (cc == kEndOfFileMarker)
                                return emitAndReconsumeIn(source, HTMLTokenizer::DataState);
                            HTML_ADVANCE_TO(BogusDOCTYPEState);
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CDATASectionState) {
                            if (cc == ']')
                                HTML_ADVANCE_TO(CDATASectionRightSquareBracketState);
                            else if (cc == kEndOfFileMarker)
                                HTML_RECONSUME_IN(DataState);
                            else {
                                bufferCharacter(cc);
                                HTML_ADVANCE_TO(CDATASectionState);
                            }
                        }
                        END_STATE()
                    
                        HTML_BEGIN_STATE(CDATASectionRightSquareBracketState) {
                            if (cc == ']')
                                HTML_ADVANCE_TO(CDATASectionDoubleRightSquareBracketState);
                            else {
                                bufferCharacter(']');
                                HTML_RECONSUME_IN(CDATASectionState);
                            }
                        }
                    
                        HTML_BEGIN_STATE(CDATASectionDoubleRightSquareBracketState) {
                            if (cc == '>')
                                HTML_ADVANCE_TO(DataState);
                            else {
                                bufferCharacter(']');
                                bufferCharacter(']');
                                HTML_RECONSUME_IN(CDATASectionState);
                            }
                        }
                        END_STATE()
                    
                        }
                    
                        ASSERT_NOT_REACHED();
                        return false;
                    }
                    

                      0
                      Согласен. В общем, просто много if. С циклами и goto можно понаписать намного запутанней. Однако, чем было, тем и мерил.
                        +2
                        А сложность мерялась до или после разворачивания макросов?
                          0
                          После.
                            +2
                            Налицо ошибка расчета. Так как погромист работает ДО макросов. И это выглядит как простые вызовы функторов.
                              0
                              В описании цикломатической сложности, кажется ничего не говорится про макросы. Потом, не считать тоже плохо. А то попрячут всё в макросы. :)
                                0
                                А чем макрос отличается от функции? Только тем, что он функтор, фактически же.
                                  0
                                  Это смотря какой макрос. :)
                                    +2
                                    То, что система функций на сях настолько гиблая, что функторы получаемы только макросами — это уже второй вопрос :)
                                    С точки зрения программиста — они являются одной единицей.
                                0
                                Совершенно очевидно, что «не налицо». Считать сложный макрос за «единичку» при расчете цикломатической сложности несправедливо.
                                  0
                                  1 макрос = 1 функция. их цели эквивалентны — упрятать повторяющуюся часть кода в маленький отдельный кусочек
                                    +3
                                    // ADPCM codecs
                                    
                                    #define DK3_GET_NEXT_NIBBLE() \
                                        if (decode_top_nibble_next) \
                                        { \
                                            nibble = (last_byte >> 4) & 0x0F; \
                                            decode_top_nibble_next = 0; \
                                        } \
                                        else \
                                        { \
                                            last_byte = *src++; \
                                            if (src >= buf + buf_size) break; \
                                            nibble = last_byte & 0x0F; \
                                            decode_top_nibble_next = 1; \
                                        }
                                    


                                    Да, да… Особенно, мило break; смотрится. :)
                                      0
                                      Пожалуйста, эквивалент на функциях:
                                      def proceed():
                                        ....
                                        def DK3_GET_NEXT_NIBBLE():
                                          if decode_top_nibble_next:
                                            nibble = (last_byte>>4) & 0x0F
                                          else:
                                            last_byte = buf[srci]
                                            srci += 1
                                            if srci >= buf_size:
                                              raise BufferOverrun()
                                            nibble = last_byte & 0x0F
                                          decode_top_nibble_next ^= True
                                      
                                        try:
                                          while True:
                                            ...
                                            DK3_GET_NEXT_NIBBLE()
                                        except BufferOverrun():
                                           pass # its ok, just end of loop
                                      
                        +3
                        >Замеченное N8 — «одноразовые» циклы

                        Одноразовые циклы могут быть удобным инструментом, когда в конце функции нужно выполнить какую-то очистку, но по ходу самой функции есть много мест, когда нужно прервать работу и перейти к этой очистке. В итоге имеем выбор:

                        -Заводить кучу флагов
                        -Бросать\ловить исключения
                        -Использовать goto
                        -Написать код вида
                        do { ... } while(false); 
                        //тут очистка
                        

                        и переходить к очистке простым break.

                        Последний способ выглядит элегантнее предыдущих. RAII, конечно, ещё элегантнее, но иногда ради него нужно наворотить лишних 20-30 строк и это портит ощущение красоты кода.
                          +1
                          Согласен. Но такие места я и не выписываю. То что я выписал, очень не похоже на предложенные варианты. :)
                            +4
                            Я бы всё же предпочел исключения, а если по каким-то причинам это неприемлемо, то банальный goto — это как раз тот случай когда его применение оправдано, особенно в случае повышенных требований к быстродействию.

                            Еще популярный и элегантный вариант — оформление в виде отдельной функции тела «цикла» с несколькими return. Собственно его и использую чаще всего. А при оптимизации компилятором он вполне может заинлайниться и не отличаться от кода с goto в итоге ничем.
                              +1
                              Использовать исключения для штатной передачи управления — последняя глупость, какую только можно представить. Очень большие накладные расходы.
                              А вот блок finally как раз для этого придуман.
                                0
                                Смотря что считать штатной, а что нет. В простом CRUD приложении выходит до десятка нештатных, когда до штатного выполнения запроса дело не доходит, это ошибки конфигурирования и формирования среды. А штатная — получение пустого результата на запрос.

                                А разве блок finally в C++ работает не совместно исключительно с исключениями? А в используемой мною версии основного языка блоков finally пока нет.
                            • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              >>V547 Expression 'socket_ < 0' is always false. Unsigned type value is never < 0. tcp_server_socket_win.cc 48

                              А компилятор разве не должен в данном случае матернуться?
                                0
                                Некоторые могут. Если специально попросить. Просто по умолчанию такое обычно выключено. В мире столько кода с подобными проверками… Анализатору PVS-Studio приходится прикладывать много усилий, чтобы попытаться отличить ошибку от нормального кода. И всё равно много ложных срабатываний. Понапишут, понатопчат… :)
                                  0
                                  Ну, строго говоря, код у самого Микрософта не блещет правильностью (да и остальные тоже не святые) — я после догого времени написания кода на С под *никсы пересел на шарп — так меня бесят _знаковые_ параметры для смещения в тех же функциях работы с отображаемой памятью. Но раз исторически так сложилось…
                                    0
                                    Исторически сложилось, что беззнаковые типы не были включены в Common Language Specification, поэтому не могут использоваться в CLS-совместимых сборках.
                                +1
                                Два раза происходит сравнение со строкой «text». Это подозрительно. Возможно, одна строка просто лишняя. А может быть, отсутствует другое нужное здесь сравнение.

                                Похоже, там должен быть color.
                                0
                                Получается, что не только неудавшийся ТОП1, но три следующих функции являются реализациями КА парсеров, и наврняка целиком или частично сформированы автоматом. Сомневаюсь, что та же HTMLTokenizer::nextToken написана вручную.

                                К чему я. Лучше бы примеры функций из другой области взять.
                                И да, мерить стоит до разворачивания макросов. Т.е. тот код, с которым работает разработчик, а не компилятор.
                                  +2
                                  Самая максимальная цикломатическая сложность, равная 2782, принадлежит функции ValidateChunkAMD64() в проекте Chromium. Но её пришлось дисквалифицировать из состязания. Функция находится в файле validator_x86_64.c, который является автогенерируемым. Жаль. А то был бы эпичный рекордсмен. Я и близко с такой цикломатической сложностью не сталкивался.

                                  Это ещё не самая сложная её версия. Промежуточные версии, насколько я знаю, в trunk так и не попали, так как требовали 10+ минут на компиляцию… или просто сносили крышу компилятору и он крэшился :)
                                    0
                                    Я видел ф-цию с цикломатической сложностью в 315. Я узнал, что у МСВЦ есть ограничение на вложенность блоков. Я думал, что я видел ВСЕ. Оказалось, что бывает и похуже…
                                      0
                                      > Замеченное N2 — противоположные условия

                                      if (*i == '"') {
                                          while (i != header_value.end() && *i != '"') ++i;
                                        ....
                                      


                                      а разве условия здесь противоположные?
                                        0
                                        Попробуйте подобрать значение символа, на который указывает 'i', чтобы выполнилось ++i.
                                          –2
                                          а разве это не тоже самое?

                                          if ($header_value[0] != '"') {
                                               while (($i != strlen($header_value)) && ($header_value[$i] != '"')) $i++;
                                               ...
                                          
                                            0
                                            Предлагаю внимательно перечитать пример. :)
                                        0
                                        > [...] Но её пришлось дисквалифицировать из состязания. Функция находится в файле [...], который является автогенерируемым.
                                        > [...]
                                        > 2. Библиотека Mesa. Функция _mesa_glsl_lex() в файле glsl_lexer.cc. Цикломатическая сложность 1088.


                                        Это тоже сгенерировано (lex).
                                          0
                                          Эх. Жаль не посмотрел.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое