Escriba un código Java para detectar la versión JVM

17

El objetivo es escribir código java que detecte la versión JVM basándose en cambios de compatibilidad, efectos secundarios, errores y / o comportamiento indefinido que funcione de una manera en una versión y otra en otra. Además, el código debe ser al menos un poco legible, sin sacrificar espacios en blanco y nombres de variables legibles.

Para garantizar ese objetivo, las reglas formales exactas son:

  1. El código debe estar escrito en Java y debe mostrar la versión JRE en la que se está ejecutando.

  2. El código no debe usar ninguna API JDK o JRE proporcionada específicamente para detectar la versión de Java o que proporciona la versión JDK o JRE de forma gratuita.

  3. El código no debe usar la reflexión.

  4. El código solo es necesario para trabajar en Hotspot Java SE 5, 6 y 7, pero puede funcionar en otras JVM.

  5. El código no debe usar ninguna biblioteca de terceros en el classpath.

  6. El código no debe iniciar ningún otro proceso, java o no.

  7. El código no debe usar variables de entorno.

  8. El código no debe buscar en el sistema de archivos en busca de archivos o carpetas preexistentes.

  9. El código debe estar contenido en un solo archivo y debe invocarse mediante public static void main(String[] args)o public static void main(String... args).

  10. El código no debe usar ninguna API no pública presente en el JRE.

  11. El código no debe generar ningún NoClassDefFoundError, NoSuchMethodError, ClassNotFoundException o NoSuchMethodException durante su ejecución.

  12. El código debe ejecutarse en un sistema desconectado de Internet o de cualquier red local.

  13. Debe proporcionar una explicación de por qué se comporta de una manera en una versión y de otra en otra.

Puntuación

El método utilizado para medir la mejor solución es max (n / s), donde n es el número de diferentes versiones de Java detectadas sin violar ninguna de estas reglas (al menos las versiones 5, 6 y 7) y s es el número de tokens léxicos En la solución.

Victor Stafusa
fuente
No pude encontrar una etiqueta mejor, y tuve que proporcionar al menos dos. Además, no tengo suficiente representante para crear nuevas etiquetas. La razón de java es porque supuestamente es un lenguaje muy portátil, por lo que escribir sería muy interesante. Además, las versiones de Java se definen de manera que podemos comparar entradas que detectan el entorno con uniformidad, sin tener que comparar naranjas con manzanas.
Victor Stafusa
Podría considerar [poco claro] argumentando que la detección de la versión de VM es un paso para atacar el sistema. No puedo decir que tengo otra sugerencia.
dmckee
@dmckee dejó caer la etiqueta [code-golf]. Agregue la etiqueta [underhanded]. ¿Podría por favor crear la etiqueta [java]?
Victor Stafusa
44
Estoy votando para cerrar esta pregunta como fuera de tema porque los desafíos poco claros ya no están en el tema en este sitio. meta.codegolf.stackexchange.com/a/8326/20469
gato
@cat, deberías haber eliminado la etiqueta, porque no se ajustaba a la pregunta.
Peter Taylor

Respuestas:

9

6/102 = 0.0588

Detecta 6 versiones. Cuenta con 102 fichas léxicas (frente a 103, después de que he eliminado publicen 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 introdujo codificaciones de caracteres y algoritmos criptográficos en Java. Las versiones posteriores agregaron más codificaciones y algoritmos. Este programa intenta usar codificaciones y algoritmos hasta que detecta una excepción. Espero una codificación faltante para lanzar java.io.UnsupportedEncodingExceptiony un algoritmo faltante para lanzar java.security.NoSuchAlgorithmException.

Tenía un viejo PowerPC Macintosh con cuatro versiones antiguas de Java. Mi máquina OpenBSD tiene dos versiones más, así que probé estas seis versiones:

  • Java 1.1.8 en MRJ 2.2.6 para Mac OS 9.2.2
  • Java 1.3.1_16 para Mac OS X Panther
  • Java 1.4.2_21 para Mac OS X Tiger
  • Java 1.5.0_19 para Mac OS X Tiger
  • OpenJDK 1.6.0_32 para OpenBSD 5.5
  • OpenJDK 1.7.0_21 para OpenBSD 5.5

Este programa también puede ejecutarse en JamVM 1.5.4 y gcj 4.8.2 para OpenBSD, pero no los identifica como implementaciones diferentes. Solo imprime "Java 5".

Mac OS Runtime para Java

Gracias a "¡Escribe una vez, ejecuta en todas partes!", Puedo escribir este programa una vez, compilarlo una vez y ejecutar una clase GuessVersion.class en las ocho máquinas virtuales. Necesito un compilador para Java 1.1, la versión más antigua de mi colección.

Mi compilador es la javacherramienta de MRJ SDK 2.2. Debido a que el Mac OS clásico no tenía línea de comando, javaces una herramienta bastante gráfica donde selecciono archivos y opciones y hago clic en "Do Javac". Después de editar mi código, solo hago clic en "Do Javac" nuevamente.

javac de MRJ SDK 2.2 para Mac OS clásico

La forma más fácil de ejecutar GuessVersion.class es abrirlo en JBindery, otra herramienta de MRJ SDK 2.2. El tiempo de ejecución es MRJ 2.2.6, una implementación de Java 1.1.8.

kernigh
fuente
22

No estoy seguro de cuál es mi puntaje, porque depende de lo que consideres exactamente un token léxico, pero estoy tratando de abusar de ese sistema de conteo tanto como sea posible con una cadena larga ...

También depende de si cuenta esto como la identificación de 7 versiones diferentes o 16 ... (podría trivialmente extenderse hasta 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){}
    }
}

Funciona al intentar definir una interfaz en un cargador de clases personalizado con números de versión principales descendentes del formato de clase. El primero que no arroja un java.lang.UnsupportedClassVersionErrorcorresponde a la versión de la VM.

Peter Taylor
fuente
Se contaron 84 fichas. Sin embargo, todavía no lo probé.
Victor Stafusa
Tu respuesta es genial. Podría trivialmente reducir a 83 tokens usando String... args.
Victor Stafusa
@Victor, eso complicaría la cuestión de si es compatible con 7 versiones diferentes aún más. No conozco ningún compilador que admita la sintaxis de Java 5 y las compila en archivos de clase compatibles con Java 1.
Peter Taylor
Buen punto. Me olvide de eso.
Victor Stafusa
1
Java 1.1.8 (en MRJ 2.2.6) no pudo compilar este, hasta que he añadido 17 fichas más: protected Class loadClass(String name, boolean resolve) { return Object.class; }. Los documentos API actuales no mencionan cómo se trataba de un método abstracto antes de Java 1.2. Devuelvo Object.class porque el método recibe una llamada para "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);
    }
}

El algoritmo interno cambió entre Java 6 y 7. Consulte /programming//a/7224864/540552

XMLGregorianCalendar.equals (null) solía lanzar NullPointerException en java 5, pero esto se solucionó en java 6. Consulte http://bugs.sun.com/view_bug.do?bug_id=6285370

100 96 92 87 85 fichas aquí. Gracias a Peter Taylor por reducir 7 tokens.

Victor Stafusa
fuente
1
Puede guardar 3 tokens almacenando el número de versión en s1. Probablemente pueda guardar otros 2 al atrapar Throwable directamente, suponiendo que DatatypeConfigurationExceptionno se lanzará.
Peter Taylor
1
O mejor, manténgalo int apero inicialícelo de inmediato, para que el ifbloque se vacíe. Niegue la condición, elimine el else y use en --lugar de la asignación directa a a.
Peter Taylor