Соавтор: Роман Фомичёв.

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

Введение


OpenJDK (Open Java Development Kit) — проект по созданию реализации платформы Java (Java SE), состоящий исключительно из свободного и открытого исходного кода. Проект стартовал в 2006 году усилиями компании Sun. В проекте используются несколько языков — C, C++ и Java. Нас интересуют исходные коды написанные на С и С++. Для проверки возьмем 9-ю версию OpenJDK. Код этой реализации Java платформы доступен в репозитории Mercurial.

Проект был проверен с помощью статического анализатора кода PVS-Studio. В нем реализовано множество диагностических правил, позволяющих найти большое количество ошибок, допущенных при программировании, в том числе и таких, которые трудно обнаружимы при простом просмотре кода. Некоторые из этих ошибок не влияют на логику работы программы, а некоторые могут привести к печальным последствиям при исполнении программы. На сайте анализатора есть примеры ошибок, найденных в других проектах. Для анализа доступны проекты, использующие языки C, C++ и С#. Загрузить пробную версию анализатора можно по ссылке.

Ошибки в логических выражениях




Сначала рассмотрим ошибки в логических выражениях:
int StubAssembler::call_RT(....) {
#ifdef _LP64
  // if there is any conflict use the stack
  if (arg1 == c_rarg2 || arg1 == c_rarg3 ||
      arg2 == c_rarg1 || arg1 == c_rarg3 ||
      arg3 == c_rarg1 || arg1 == c_rarg2) {
  ....
}

Предупреждение PVS-Studio: V501 There are identical sub-expressions 'arg1 == c_rarg3' to the left and to the right of the '||' operator. c1_Runtime1_x86.cpp 174

Анализатор сообщает о дублировании проверки arg1 == c_rarg3. В этом фрагменте или избыточная проверка или, что гораздо хуже, присутствует логическая ошибка. Возможно, вместо продублированного условия должно было проверяться что-то другое. Разработчикам есть смысл взглянуть повнимательнее на этот код.

В этом же условии есть еще одно повторяющееся выражение arg1 == c_rarg2:

Предупреждение PVS-Studio: V501 There are identical sub-expressions 'arg1 == c_rarg2' to the left and to the right of the '||' operator. c1_Runtime1_x86.cpp 174

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

В следующем фрагменте встретилась «неидеальная» проверка в условии метода Ideal:
Node *AddLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
  ....
  if( op2 == Op_AddL &&
      in2->in(1) == in1 &&
      op1 != Op_ConL &&
      0 ) {
  ....
}

Предупреждение PVS-Studio: V560 A part of conditional expression is always false: 0. addnode.cpp 435

Довольно странно в логическом выражении использовать 0. Скорее всего, этот фрагмент кода ещё в разработке и с целью отладки условие сделали невыполнимым. Соответствующие комментарии в коде отсутствуют, и в этом случае высока вероятность забыть исправить код в дальнейшем. Результатом этой ошибки будет то, что все, что находится внутри этого условия, никогда не будет исполняться, так как результат вычисления логического выражения будет всегда false.

Приоритеты операций


Довольно часто программисты надеются на свое знание приоритетов и не выделяют составные части сложного выражения скобками:
int method_size() const
  { return sizeof(Method)/wordSize + is_native() ? 2 : 0; }

Предупреждение PVS-Studio: V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '+' operator. method.hpp 249

В данном случае я не знаю специфики кода, но есть подозрение, что в функции планировали выбирать значение смещения '2' или '0' в зависимости от результата вызова функции is_native(), но выражение имеет иной порядок вычисления. Сначала выполнится сложение sizeof(Method)/wordSize + is_native(), а потом уже будет возвращен результат 2 или 0. Скорее всего, код должен выглядеть так:
{ return sizeof(Method)/wordSize + (is_native() ? 2 : 0); }

Это очень распространённая ошибка с приоритетом операций. В базе найденных анализатором ошибок были выявлены самые популярные из них и приведены в статье: Логические выражения в C/C++. Как ошибаются профессионалы.

Copy-Paste




Следующая характерная группа ошибок связана с копированием кода. От этого излюбленного приема программистов никуда не денешься, поэтому исследуем места, где был применен copy paste:
static int
setImageHints(....)
{
  ....
  if (dstCMP->isDefaultCompatCM) {
      hintP->allocDefaultDst = FALSE;
      hintP->cvtToDst = FALSE;
  }
  else if (dstCMP->isDefaultCompatCM) {
      hintP->allocDefaultDst = FALSE;
      hintP->cvtToDst = FALSE;
  }
  ....
}

Предупреждение PVS-Studio: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 1873, 1877. awt_ImagingLib.c 1873

В этом примере полностью совпадают как условия в if и else if, так и тот код, который должен исполняться. Второе условие полностью бессмысленно, оно никогда не будет исполняться.

Еще один похожий случай:
static int expandPackedBCR(JNIEnv *env, RasterS_t *rasterP, 
                           int component,
                           unsigned char *outDataP)
{
  ....
  /* Convert the all bands */
  if (rasterP->numBands < 4) {
      /* Need to put in alpha */
      for (y=0; y < rasterP->height; y++) {
          inP = lineInP;
          for (x=0; x < rasterP->width; x++) {
              for (c=0; c < rasterP->numBands; c++) {
                  *outP++ = (unsigned char)
                      (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                       <<loff[c]);
              }
              inP++;
          }
          lineInP += rasterP->scanlineStride;
      }
  }
  else {
      for (y=0; y < rasterP->height; y++) {
          inP = lineInP;
          for (x=0; x < rasterP->width; x++) {
              for (c=0; c < rasterP->numBands; c++) {
                  *outP++ = (unsigned char)
                      (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                       <<loff[c]);
              }
              inP++;
          }
          lineInP += rasterP->scanlineStride;
      }
  }
  ....
}

Предупреждение PVS-Studio: V523 The 'then' statement is equivalent to the 'else' statement. awt_ImagingLib.c 2927

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

Есть еще два места с идентичным дублированием. Просто укажу их, без приведения кода:
  • V523 The 'then' statement is equivalent to the 'else' statement. awt_ImagingLib.c 3111
  • V523 The 'then' statement is equivalent to the 'else' statement. awt_ImagingLib.c 3307


Ну и последний интересный пример, связанный с возможной ошибкой copy paste:

Node* GraphKit::record_profiled_receiver_for_speculation(Node* n)
{
  ....
  ciKlass* exact_kls = profile_has_unique_klass();
  bool maybe_null = true;
  if (java_bc() == Bytecodes::_checkcast ||
      java_bc() == Bytecodes::_instanceof ||
      java_bc() == Bytecodes::_aastore) {
    ciProfileData* data = 
      method()->method_data()->bci_to_data(bci());
    bool maybe_null = data == NULL ? true :    <==
                      data->as_BitData()->null_seen();
  }
  return record_profile_for_speculation(n, 
    exact_kls, maybe_null);
  return n;
}

Предупреждение PVS-Studio: V561 It's probably better to assign value to 'maybe_null' variable than to declare it anew. Previous declaration: graphKit.cpp, line 2170. graphKit.cpp 2175

Что же происходит в этом коде? Перед блоком if объявлена переменная bool maybe_null = true;. Затем, когда исполняется код в блоке if, объявляется переменная с аналогичным именем. После выхода из блока значение этой переменной будет утеряно, и вызов функции, использующей эту переменную, произойдет в любом случае со значением true. Хорошо, если эта переменная была продублирована с целью отладки. В противном случае данный код исполняется некорректно и требует модификации:
maybe_null = data == NULL ? true :    
             data->as_BitData()->null_seen();

Работа с указателями




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

Первым делом рассмотрим случай явного использования нулевого указателя:
static jint JNICALL
cbObjectTagInstance(....)
{
    ClassInstancesData  *data;

    /* Check data structure */
    data = (ClassInstancesData*)user_data;
    if (data == NULL) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }
  ....
}

Предупреждение PVS-Studio: V522 Dereferencing of the null pointer 'data' might take place. util.c 2424

Совершенно непонятный код с использованием нулевого указателя, может привести к аварийному завершению программы. Возможно, эта ветка никогда не срабатывала, благодаря чему удалось избежать проблем при работе программы. В этом же файле нашлось еще три подобных места:
  • V522 Dereferencing of the null pointer 'data' might take place. util.c 2543
  • V522 Dereferencing of the null pointer 'data' might take place. util.c 2601
  • V522 Dereferencing of the null pointer 'data' might take place. util.c 2760


А вот в следующих случаях возможность использования нулевого указателя скрыта глубже. Это очень распространенная ситуация, подобные предупреждения встречаются практически во всех проверяемых проектах:
static jboolean
visibleClasses(PacketInputStream *in, PacketOutputStream *out)
{
  ....
  else {
    (void)outStream_writeInt(out, count);
    for (i = 0; i < count; i++) {
      jbyte tag;
      jclass clazz;

      clazz = classes[i];                     <==
      tag = referenceTypeTag(clazz);

      (void)outStream_writeByte(out, tag);
      (void)outStream_writeObjectRef(env, out, clazz);
    }
  }

  if ( classes != NULL )                      <==
    jvmtiDeallocate(classes);
  ....
    return JNI_TRUE;
}

Предупреждение PVS-Studio: V595 The 'classes' pointer was utilized before it was verified against nullptr. Check lines: 58, 66. ClassLoaderReferenceImpl.c 58

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

Приведу еще один аналогичный пример:
int InstructForm::needs_base_oop_edge(FormDict &globals) const {
  if( is_simple_chain_rule(globals) ) {
    const char *src = _matrule->_rChild->_opType;
    OperandForm *src_op = globals[src]->is_operand();
    assert( src_op, "Not operand class of chain rule" );
    return src_op->_matrule ? 
           src_op->_matrule->needs_base_oop_edge() : 0;
  }                             // Else check instruction

  return _matrule ? _matrule->needs_base_oop_edge() : 0;
}

Предупреждение PVS-Studio: V595 The '_matrule' pointer was utilized before it was verified against nullptr. Check lines: 3534, 3540. formssel.cpp 3534

Здесь проверка указателя осуществляется снизу в тернарном операторе — _matrule? _matrule->needs_base_oop_edge(): 0;. А выше происходит простое обращение к этому указателю — const char *src = _matrule->_rChild->_opType;. Рецепт исправления аналогичен: нужно проверить указатель до его использования. Таких мест нашлось довольно много, я приведу их списком:
  • V595 The '_pipeline' pointer was utilized before it was verified against nullptr. Check lines: 3265, 3274. output_c.cpp 3265
  • V595 The 'index_bound' pointer was utilized before it was verified against nullptr. Check lines: 790, 806. c1_RangeCheckElimination.cpp 790
  • V595 The 'g_type_init' pointer was utilized before it was verified against nullptr. Check lines: 94, 108. GioFileTypeDetector.c 94
  • V595 The 'classArray' pointer was utilized before it was verified against nullptr. Check lines: 1169, 1185. JPLISAgent.c 1169
  • V595 The 'q' pointer was utilized before it was verified against nullptr. Check lines: 594, 599. mpi.c 594
  • V595 The 'info.waiters' pointer was utilized before it was verified against nullptr. Check lines: 224, 228. ObjectReferenceImpl.c 224
  • V595 The 'methods' pointer was utilized before it was verified against nullptr. Check lines: 225, 229. ReferenceTypeImpl.c 225
  • V595 The 'fields' pointer was utilized before it was verified against nullptr. Check lines: 433, 437. ReferenceTypeImpl.c 433
  • V595 The 'nested' pointer was utilized before it was verified against nullptr. Check lines: 538, 540. ReferenceTypeImpl.c 538
  • V595 The 'interfaces' pointer was utilized before it was verified against nullptr. Check lines: 593, 595. ReferenceTypeImpl.c 593
  • V595 The 'buf' pointer was utilized before it was verified against nullptr. Check lines: 265, 266. ps_proc.c 265
  • V595 The 'monitors' pointer was utilized before it was verified against nullptr. Check lines: 382, 387. ThreadReferenceImpl.c 382
  • V595 The 'monitors' pointer was utilized before it was verified against nullptr. Check lines: 557, 560. ThreadReferenceImpl.c 557
  • V595 The 'signature' pointer was utilized before it was verified against nullptr. Check lines: 520, 526. debugInit.c 520
  • V595 The 'BlackPoint' pointer was utilized before it was verified against nullptr. Check lines: 192, 208. cmssamp.c 192
  • V595 The 'nativename' pointer was utilized before it was verified against nullptr. Check lines: 506, 511. awt_Font.c 506
  • V595 The 'pseq->seq' pointer was utilized before it was verified against nullptr. Check lines: 788, 791. cmsnamed.c 788
  • V595 The 'GammaTables' pointer was utilized before it was verified against nullptr. Check lines: 1430, 1434. cmsopt.c 1430


Иногда программисты наоборот проверяют указатели, но делают это неправильно:
FileBuff::FileBuff( BufferedFile *fptr, ArchDesc& archDesc) : 
                   _fp(fptr), _AD(archDesc) {
  ....
  _bigbuf = new char[_bufferSize];
  if( !_bigbuf ) {
    file_error(SEMERR, 0, "Buffer allocation failed\n");
    exit(1);
  ....
}

Предупреждение PVS-Studio: V668 There is no sense in testing the '_bigbuf' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. filebuff.cpp 47

В данном случае проверка указателя _bigbuf на нулевое значение после использования new бессмысленна. В случае если системе не удастся выделить память, то будет сгенерировано исключение и выполнение функции прекратится. Для исправления ошибки можно использовать несколько подходов. Cделать выделение памяти в блоке try catch, или использовать для выделения памяти конструкцию new(std::nothrow), которая не будет генерировать исключение в случае неудачи. Есть еще несколько таких неправильных проверок:
  • V668 There is no sense in testing the 'vspace' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. psParallelCompact.cpp 455
  • V668 There is no sense in testing the 'uPtr' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. jni.cpp 113


Последняя ошибка в работе с указателями допущена при явном приведении указателя одного типа к указателю другого типа:
mlib_status mlib_convMxNext_f32(...)
{
  mlib_d64 dspace[1024], *dsa = dspace;
  ....
  mlib_f32 *fsa;
  ....

  if (3 * wid_e + m > 1024) {
    dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64));

    if (dsa == NULL)
      return MLIB_FAILURE;
  }

  fsa = (mlib_f32 *) dsa; <==
  ....
}

Предупреждение PVS-Studio: V615 An odd explicit conversion from 'double *' type to 'float *' type. mlib_ImageConvMxN_Fp.c 294

Указателю на float mlib_f32 *fsa пытаются присвоить указатель на double mlib_d64 dspace[1024], *dsa = dspace. Типы float и double имеют различный размер и подобное приведение типов, скорее всего, ошибочно. Несоответствие размеров приводимых типов приведет к тому, что указатель fsa будет указывать на некорректный для типа float формат числа.

В другом файле есть еще два таких аналогичных преобразования, надо хорошенько проверить этот код и использовать правильные преобразования типов.
  • V615 An odd explicit conversion from 'double *' type to 'float *' type. mlib_ImageLookUp_Bit.c 525
  • V615 An odd explicit conversion from 'double *' type to 'float *' type. mlib_ImageLookUp_Bit.c 526

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

Разные ошибки




Следующая ошибка, наверное, тоже результат неудачного копирования кода:
static bool
parse_bool (const char **pp, const char *end, unsigned int *pv)
{
  ....

  /* CSS allows on/off as aliases 1/0. */
  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
    *pv = 1;
  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
    *pv = 0;
  else
    return false;

  return true;
}

Предупреждение PVS-Studio: V666 Consider inspecting third argument of the function 'strncmp'. It is possible that the value does not correspond with the length of a string which was passed with the second argument. hb-shape.cc 104

Здесь как раз тот случай, когда ошибка не влияет на работоспособность программы. Вместо сравнения трех символов, сравниваются только первые два, я не исключаю, что автор кода может и специально сделал такую проверку. Так как значение в буфере p может быть on или off, то достаточно сравнивать и первые два символа. Но для порядка, все-таки можно скорректировать код:
else if (*pp - p == 3 || 0 == strncmp (p, "off", 3))

Нашлось несколько мест, где возможны ошибки при реализации классов:
class ProductionState {
  ....
private:
    // Disable public use of constructor, copy-ctor,  ...
  ProductionState( )                         :
  _production(cmpstr, hashstr, Form::arena) 
  {  assert( false, "NotImplemented");  };
  ProductionState( const ProductionState & ) :
  _production(cmpstr, hashstr, Form::arena) 
  {  assert( false, "NotImplemented");  }; // Deep-copy
};

Предупреждение PVS-Studio: V690 Copy constructor is declared as private in the 'ProductionState' class, but the default '=' operator will still be generated by compiler. It is dangerous to use such a class. dfa.cpp 76

В этом классе попытались запретить копирование, но забыли добавить в приватную область оператор копирования. Он будет сгенерирован по умолчанию и будет доступен для использования. Даже если сейчас в коде нигде не используется этот оператор, то нет гарантии, что в дальнейшем кто-то случайно его не вызовет. При вызове такого оператора произойдет почленное копирование для класса, который не должен копироваться. Это может привести к различным эффектам вплоть до аварийного завершения программы. В данном случае следует добавить в приватную область объявление оператора "=".

Есть еще два класса, где есть подобные проблемы, желательно скорректировать их таким образом, чтобы не нарушать "Закон Большой Двойки".
  • V690 The 'MemRegion' class implements a copy constructor, but lacks the '=' operator. It is dangerous to use such a class. memRegion.hpp 43
  • V690 Copy constructor is declared as private in the 'Label' class, but the default '=' operator will still be generated by compiler. It is dangerous to use such a class. assembler.hpp 73


Последняя ошибка похожа на простую опечатку:
bool os::start_debugging(char *buf, int buflen) {
  int len = (int)strlen(buf);
  char *p = &buf[len];
  ....
  if (yes) {
    // yes, user asked VM to launch debugger
    jio_snprintf(buf, sizeof(buf), "gdb /proc/%d/exe %d",
      os::current_process_id(), os::current_process_id());

    os::fork_and_exec(buf);
    yes = false;
  }
  return yes;
}

Предупреждение PVS-Studio: V579 The jio_snprintf function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the second argument. os_linux.cpp 6094

Программист хотел передать длину буфера, но не учел, что он не локально объявленный массив, а указатель приходящий в аргументе функции. В результате вычисления выражения sizeof(buf) мы получим не длину буфера, а размер указателя, который будет равен 4 или 8 байтам. Исправить ошибку легко, так выше длина буфера уже была получена: int len = (int)strlen(buf);. Правильный вариант будет выглядеть следующим образом:
jio_snprintf(buf, len ....

Заключение




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


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. OpenJDK check by PVS-Studio.

Прочитали статью и есть вопрос?
Часто к нашим статьям задают одни и те же вопросы. Ответы на них мы собрали здесь: Ответы на вопросы читателей статей про PVS-Studio, версия 2015. Пожалуйста, ознакомьтесь со списком.
Поделиться с друзьями
-->

Комментарии (17)


  1. istui
    17.06.2016 13:24
    +1

    Вопрос не совсем по теме: умеет ли PVS-studio находить ошибки вида

    auto average = total / x.size();
    
    ?

    Имеется в виду: (1) возможное деление на ноль и (2) отсутствие проверок в коде на (1)?
    Пробная версия, к сожалению, эти места не обнаружила. Если такого функционала нет, я думаю, есть смысл его добавить — подобный код встречается не так уж и редко…


    1. DieselMachine
      17.06.2016 15:29
      +2

      Если выдавать предупреждение на каждое деление, то будет много ложных срабатываний.
      Мы сделали так: если выражение в знаменателе было как-то ограничено, например в условии блока if, или это счётчик цикла, который принимает ограниченное количество значений и ноль в него тоже входит, тогда выдаём предупреждение.


      1. istui
        18.06.2016 09:46

        В итоге получается, что случай, приведенный мной выше, анализатор пропускает, а при выполнении программы в редких кейсах размер вектора становится равным нулю, и все падает…

        Было бы неплохо все же иметь хоть какую-то опцию, вроде «расширенная проверка деления на ноль», по умолчанию отключенную для удобства.


        1. Andrey2008
          18.06.2016 10:48

          Нет, это плохая идея. Это тоже самое, что просто показать в программе все деления (ну кроме деления на константу). При реализации хотя-бы пяти таких диагностик анализатор превращается в тыкву.


  1. lany
    17.06.2016 13:30
    +3

    { return sizeof(Method)/wordSize + (is_native()? 2: 0); }


    Вообще разработчики OpenJDK тоже пользуются статическим анализом. Это довольно свежий код и эту багу уже исправили 7 июня (видимо, после того как вы начали писать статью).


  1. antreides
    17.06.2016 13:52
    +4

    Спасибо.
    Немного непонятно: почему для проверки была взята не стабильная версия OpenJDK 8, а девятая, которая еще в активной разработке, не оттестирована и у которой еще почти год до General Availability? Это даже не бета-версия.


    1. lany
      17.06.2016 14:11
      +3

      Так же интереснее!


      1. SvyatoslavMC
        17.06.2016 15:39
        +4

        Ваш проект всё ещё в

        в активной разработке

        не оттестирован

        даже не бета-версия

        ?

        А дальше как в рекламе :D


    1. Am0ralist
      17.06.2016 14:31
      +5

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


    1. gmvbif
      17.06.2016 14:44
      +11

      Когда ребята из PVS-Studio берут не свежую версию — им кричат, что надо брать trunk (master) и там все эти баги уже пофикшены.


    1. EvgeniyRyzhkov
      17.06.2016 14:45
      +4

      Тестировать стабильную версию — самый худший способ применения анализа кода. Лучше чем ничего, но сильно хуже, чем должно быть. На тестирование и отладку стабильной версии УЖЕ было потрачено куча сил. Которые можно было бы сэкономить, если регулярно использовать статический анализ.


    1. vanyamasnuha
      17.06.2016 16:55

      Там баги уже выловили вручную. А могли б анализатором.


  1. AndreyRubankov
    17.06.2016 15:49

    В прошлых статьях (проверка C#, кажется) писали, что Java проверять не интересно, т.к. это не проприетарная платформа.
    И вот статья о проверке Java, интересно что повлияло на изменение курса?


    1. SvyatoslavMC
      17.06.2016 15:51
      +1

      Статья о проверке C/C++ проекта. Не могу сказать, что Java совсем не причём, но в прошлых статьях скорее всего писали о проверке проектов именно на языке Java.


    1. lany
      17.06.2016 15:51
      +1

      Я думаю, речь шла о проверке Java-кода. А здесь выполняется проверка той части OpenJDK, что написана на C++.


  1. apangin
    18.06.2016 16:08
    +3

    Спасибо! Давно хотелось увидеть анализ OpenJDK.
    Признаться, ожидал, что всё будет хуже.

    Две, действительно, серьёзные баги (про приоритет "? :" и про sizeof(buf) в printf) к моменту публикации уже были пофикшены, а остальное — просто хорошие замечания, хотя и не влияющие на работоспособность. Каст double* к float* и вовсе не бага, а типичный сценарий переиспользования буфера.

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


  1. denixx
    21.06.2016 10:06
    +3

    Плюс в карму за терморектальный криптоанализатор на второй картинке статьи. Бедная Java…