Модификаторы классов Java Illegal Код исключения 0x209

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

java.lang.ClassFormatError: Недействительные модификаторы классов в классе FooBar 0x209

Googling это не раскрывает много деталей. Согласно this, проблема может быть связана с несоответствием между модификаторами интерфейса и реализации. И, конечно же, это должно быть какое-то новое ограничение, которое не было в 1.5.

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

Итак, есть ли у кого-нибудь точная информация о 0x209 - что конкретно означает код?

EDIT:

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

Мы используем Doug Lea древний util.concurrent package в определенных областях, потому что некоторые компоненты работают в средах, которые предоставляют Java 1.1 (да, Это  довольно хорошо смеяться, я не против).

Этот же код (с использованием параллельных utils) также используется в качестве небольшого компонента другого сопутствующего программного обеспечения. Поскольку код Doug использовал некоторые функции 1.2, нам также пришлось модифицировать некоторые части util.concurrent, чтобы сделать его совместимым с 1.1 пакетом коллекций Sun 1.1 (больше не найти ссылку на них). Как-то это вызвало это своеобразное поведение компиляции Eclipse, которое возникает, когда мы меняем формат класса на Java 1.6. Это минимальный код, который вызывает проблему:

EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import com.sun.java.util.collections.Map;

public class FooBar
{
    public static void main(String[] args) {
        Map.Entry e = (Map.Entry)(new ConcurrentHashMap().entrySet().iterator().next());
    }
}

После компиляции с Eclipse (с компиляцией, установленной в 1.6, 1.5 отлично работает) и попытайтесь загрузить класс из Sun 1.6 JRE, проблема возникает. Обходной путь: вместо того, чтобы перебирать записи, мы перебираем ключи и получаем значения внутри циклов с помощью клавиш.

Наша установка здесь настолько экзотична, что неудивительно, что никто другой не столкнулся с этим. Я, наконец, проверил наши скрипты сборки, и вот, ant - script имеет 1.6 исходных и целевых настроек. По-видимому, это специфичный для Eclipse.

EDIT2:

Я подошел ближе к отчету об ошибке Sun, который я здесь связал. Проблема там также связана с com.sun.java.util.collections.Map.Entry. И это произошло с Sun Javac. Интересно.

Ответ 1

Код не является кодом, а внутренним представлением модификаторов класса в байтовом коде вашего файла класса.

Существует восемь разных флагов.

ACC_PUBLIC (0x0001)
ACC_FINAL (0x0010)
ACC_SUPER (0x0020)
ACC_INTERFACE (0x0200)
ACC_ABSTRACT (0x0400)
ACC_SYNTHETIC (0x1000)
ACC_ANNOTATION (0x2000)
ACC_ENUM (0x4000)

Например, публичный конечный класс будет иметь байты модификатора 0x0011, открытый абстрактный класс 0x0401.

Ваш случай 0x209 (или лучше: 0x0209) является незаконным modfier. Он выглядит как интерфейс (0x0200), но часть 0x0009 не входит в спецификацию. Я бы предположил, что это ошибка компилятора.

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

import java.io.*;

public class Main {

 public static void main(String[] args) throws Exception {
  String path = "D:/Arne/workspaces/IDEDeluxe/TestBytecode/bin/";
  String[] fileNames = { "Main.class" };
  for(String fileName : fileNames)
   traceFile(path, fileName);
 }

 private static void traceFile(String path, String fileName) throws FileNotFoundException, IOException {
  DataInputStream stream = new DataInputStream(new BufferedInputStream(new FileInputStream(path + fileName)));
  trace(fileName, readClassAccessFlags(stream));
  stream.close();
 }

 private static int readClassAccessFlags(DataInputStream stream) throws IOException {
  skipHeader(stream);
  skipConstantPool(stream);
  return stream.readUnsignedShort();
 }

 private static void skipHeader(DataInputStream stream) throws IOException {
  stream.readInt();
  stream.readUnsignedShort();
  stream.readUnsignedShort();
 }

 private static void skipConstantPool(DataInputStream stream) throws IOException {
  int constantPoolCount = stream.readUnsignedShort();
  for(int n = 1; n < constantPoolCount; n++) {
   int tag = stream.readUnsignedByte();
   switch(tag) {
   case 7:
    stream.readUnsignedShort();
    break;
   case 9:
   case 10:
   case 11:
    stream.readUnsignedShort();
    stream.readUnsignedShort();
    break;
   case 8:
    stream.readUnsignedShort();
    break;
   case 3:
   case 4:
    stream.readInt();
    break;
   case 5:
   case 6:
    stream.readInt();
    stream.readInt();
    break;
   case 12:
    stream.readUnsignedShort();
    stream.readUnsignedShort();
    break;
   case 1:
    stream.readUTF();
    break;
   }
  }
 }

 private static void trace(String fileName, int flags) {
  System.out.print(fileName + ": " + Integer.toHexString(flags) + " - ");
  if((flags & 0x0001) != 0)
   flags -= 0x0001;
  if((flags & 0x0010) != 0)
   flags -= 0x0010;
  if((flags & 0x0020) != 0)
   flags -= 0x0020;
  if((flags & 0x0200) != 0)
   flags -= 0x0200;
  if((flags & 0x0400) != 0)
   flags -= 0x0400;
  if((flags & 0x1000) != 0)
   flags -= 0x1000;
  if((flags & 0x2000) != 0)
   flags -= 0x2000;
  if((flags & 0x4000) != 0)
   flags -= 0x4000;
  if(flags == 0)
   System.out.println("OK!");
  else
   System.out.println("INVALID!!!");
 }

}