Написать код Java для определения версии JVM

17

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

Для обеспечения этой цели, точные формальные правила:

  1. Код должен быть написан на Java и должен выводить версию JRE, в которой он работает.

  2. Код не должен использовать какой-либо JDK или JRE API, предоставленный специально для определения версии Java или который предоставляет версию JDK или JRE бесплатно.

  3. Код не должен использовать отражение.

  4. Код требуется только для работы в Hotspot Java SE 5, 6 и 7, но может работать и в других JVM.

  5. Код не должен использовать сторонние библиотеки в пути к классам.

  6. Код не должен запускать любой другой процесс, Java или нет.

  7. Код не должен использовать переменные среды.

  8. Код не должен искать файловую систему в поисках уже существующих файлов или папок.

  9. Код должен содержаться в одном файле и вызываться через public static void main(String[] args)или public static void main(String... args).

  10. Код не должен использовать какой-либо непубличный API, присутствующий в JRE.

  11. Код не должен генерировать никаких NoClassDefFoundError, NoSuchMethodError, ClassNotFoundException или NoSuchMethodException во время его выполнения.

  12. Код должен работать в системе, отключенной от Интернета или от любой локальной сети.

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

счет

Для измерения наилучшего решения используется метод max (n / s), где n - это количество различных версий Java, обнаруженных без нарушения любого из этих правил (по крайней мере, версии 5, 6 и 7), а s - количество лексических токенов. в решении.

Виктор Стафуса
источник
Не удалось найти лучший тег, и мне пришлось предоставить, наконец, два. Кроме того, мне не хватает представителя для создания новых тегов. Причина для java в том, что это, предположительно, очень переносимый язык, поэтому написание будет очень интересным. Кроме того, java-версии определены таким образом, что мы можем сравнивать записи, обнаруживающие окружающую среду, с однородностью, без необходимости сравнивать апельсины с яблоками.
Виктор Стафуса
Вы могли бы рассмотреть [закулисный] аргумент, что обнаружение версии виртуальной машины является шагом в атаке на систему. Я не могу сказать, что у меня есть другое предложение.
dmckee
@dmckee Удалил тег [code-golf]. Добавьте тег [underhanded]. Не могли бы вы создать тег [java]?
Виктор Стафуса
4
Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что скрытые проблемы больше не обсуждаются на этом сайте. meta.codegolf.stackexchange.com/a/8326/20469
кошка
@cat, вместо этого вы должны удалить тег, потому что он не подходит под вопрос.
Питер Тейлор

Ответы:

9

6/102 = 0,0588

Обнаруживает 6 версий. Имеет 102 лексических маркеров (вниз от 103, после того, как я удалил publicв public class).

import java.security.Signature;

class GuessVersion {
        public static void main(String[] args) {
                String version = "Java 1.1";
                try {
                        "".getBytes("ISO8859_13");
                        version = "Java 1.3";

                        "".getBytes("ISO8859_15");
                        version = "Java 1.4";

                        Signature.getInstance("SHA256withRSA");
                        version = "Java 5";

                        "".getBytes("KOI8_U");
                        version = "Java 6";

                        Signature.getInstance("SHA256withECDSA");
                        version = "Java 7";
                } catch(Exception e) {}
                System.out.println(version);
        }
}

Java 1.1 представила кодировки символов и криптографические алгоритмы для Java. Более поздние версии добавили больше кодировок и алгоритмов. Эта программа пытается использовать кодировки и алгоритмы, пока не поймает исключение. Я ожидаю, что бросит отсутствующую кодировку и бросит java.io.UnsupportedEncodingExceptionотсутствующий алгоритм java.security.NoSuchAlgorithmException.

У меня был старый PowerPC Macintosh с четырьмя старыми версиями Java. У моего компьютера с OpenBSD есть еще две версии, поэтому я протестировал эти шесть версий:

  • Java 1.1.8 в MRJ 2.2.6 для Mac OS 9.2.2
  • Java 1.3.1_16 для Mac OS X Panther
  • Java 1.4.2_21 для Mac OS X Tiger
  • Java 1.5.0_19 для Mac OS X Tiger
  • OpenJDK 1.6.0_32 для OpenBSD 5.5
  • OpenJDK 1.7.0_21 для OpenBSD 5.5

Эта программа также может работать в JamVM 1.5.4 и gcj 4.8.2 для OpenBSD, но не идентифицирует их как различные реализации. Он печатает только "Java 5".

Mac OS Runtime для Java

Благодаря «Пишите один раз, запускайте везде!», Я могу написать эту программу один раз, скомпилировать ее один раз и запустить один GuessVersion.class на всех восьми виртуальных машинах. Мне нужен компилятор для Java 1.1, самой старой версии в моей коллекции.

Мой компилятор - javacинструмент из MRJ SDK 2.2. Поскольку у Classic Mac OS не было командной строки, javacэто довольно графический инструмент, в котором я выбираю файлы и параметры и нажимаю «Сделать Javac». После того, как я отредактировал свой код, я просто снова нажимаю «Сделать Javac».

javac из MRJ SDK 2.2 для классической Mac OS

Самый простой способ запустить GuessVersion.class - открыть его в JBindery, другом инструменте из MRJ SDK 2.2. Среда выполнения - MRJ 2.2.6, реализация Java 1.1.8.

kernigh
источник
22

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

Это также зависит от того, считаете ли вы это идентифицирующим 7 различными версиями или 16 ... (оно может быть тривиально расширено до 190).

class V extends ClassLoader
{
    public static void main(String[]args)
    {
        for(byte b=60;;)
            try {
                byte[]buf="\u00ca\u00fe\u00ba\u00be\u0000\u0000\u00002\u0000\u0005\u0007\u0000\u0003\u0007\u0000\u0004\u0001\u0000\u0001A\u0001\u0000\u0010java/lang/Object\u0006\u0000\u0000\u0001\u0000\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000".getBytes("ISO-8859-1");
                buf[7]=b--;
                new V().defineClass(buf,0,53);
                System.out.println(b-43);
                break;
            }
            catch(Throwable t){}
    }
}

Он работает, пытаясь определить интерфейс в пользовательском загрузчике классов с нисходящими номерами основных версий формата класса. Первый, который не выбрасывает, java.lang.UnsupportedClassVersionErrorсоответствует версии виртуальной машины.

Питер Тейлор
источник
Подсчитано 84 токена. Все еще не проверял это все же.
Виктор Стафуса
Ваш ответ гениален. Можно легко сократить до 83 токенов с помощью String... args.
Виктор Стафуса
@Victor, это усложнит вопрос о том, поддерживает ли оно еще 7 разных версий. Я не знаю ни одного компилятора, который поддерживает синтаксис Java 5 и компилирует в файлы классов, совместимые с Java 1.
Питер Тейлор
Хорошая точка зрения. Я забыл об этом.
Виктор Стафуса
1
Java 1.1.8 (в MRJ 2.2.6) не удалось собрать это, пока я добавил еще 17 маркеров: protected Class loadClass(String name, boolean resolve) { return Object.class; }. В текущих документах API не упоминается, как это был абстрактный метод до Java 1.2. Я возвращаю Object.class, потому что метод получает один вызов для «java.lang.Object».
Kernigh
8
class Evil {
    public static void main(String... args) {
        String s1 = "Good";
        s1 += "morning";
        int a = 7;
        if (s1 != s1.intern())
            try {
                a--;
                javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar().equals(null);
            } catch (Throwable e) {
                a--;
            }
        System.out.println(a);
    }
}

Алгоритм интернирования изменился между Java 6 и 7. Https://stackoverflow.com/a/7224864/540552

XMLGregorianCalendar.equals (null) использовался для создания исключения NullPointerException в java 5, но это было исправлено в java 6. См. Http://bugs.sun.com/view_bug.do?bug_id=6285370

100 96 92 87 85 жетонов здесь. Спасибо Питеру Тейлору за сокращение 7 жетонов.

Виктор Стафуса
источник
1
Вы можете сохранить 3 токена, сохранив номер версии в s1. Вероятно, вы можете сохранить еще 2, поймав Throwable напрямую, при условии, что DatatypeConfigurationExceptionон не будет брошен.
Питер Тейлор
1
Или лучше сохранить, int aно сразу инициализировать, чтобы ifблок стал пустым. Отмените условие, удалите else и используйте --вместо прямого присваивания a.
Питер Тейлор