Écrivez un code java pour détecter la version JVM

17

L'objectif est d'écrire du code java qui détecte la version JVM en s'appuyant sur les changements de compatibilité, les effets secondaires, les bugs et / ou les comportements indéfinis qui fonctionnent d'une manière dans une version et d'une autre manière dans une autre version. En outre, le code doit être au moins un peu lisible, sans sacrifier les espaces blancs et les noms de variables lisibles.

Pour garantir cet objectif, les règles formelles exactes sont les suivantes:

  1. Le code doit être écrit en java et doit afficher la version JRE dans laquelle il s'exécute.

  2. Le code ne doit utiliser aucune API JDK ou JRE fournie spécifiquement pour détecter la version java ou qui donne la version JDK ou JRE gratuitement.

  3. Le code ne doit pas utiliser de réflexion.

  4. Le code est uniquement requis pour fonctionner dans Hotspot Java SE 5, 6 et 7, mais peut fonctionner dans d'autres JVM.

  5. Le code ne doit utiliser aucune bibliothèque tierce dans le chemin de classe.

  6. Le code ne doit démarrer aucun autre processus, Java ou non.

  7. Le code ne doit pas utiliser de variables d'environnement.

  8. Le code ne doit pas rechercher dans le système de fichiers à la recherche de fichiers ou de dossiers préexistants.

  9. Le code doit être contenu dans un seul fichier et être appelé via public static void main(String[] args)ou public static void main(String... args).

  10. Le code ne doit utiliser aucune API non publique présente dans le JRE.

  11. Le code ne doit générer aucune NoClassDefFoundError, NoSuchMethodError, ClassNotFoundException ou NoSuchMethodException pendant son exécution.

  12. Le code doit s'exécuter dans un système déconnecté d'Internet ou de tout réseau local.

  13. Vous devez expliquer pourquoi il se comporte d'une manière dans une version et d'une autre manière dans une autre version.

Notation

La méthode utilisée pour mesurer la meilleure solution est max (n / s), où n est le nombre de versions java différentes détectées sans violer aucune de ces règles (au moins les versions 5, 6 et 7) et s est le nombre de jetons lexicaux. dans la solution.

Victor Stafusa
la source
Impossible de trouver une meilleure balise, et j'ai dû en fournir au moins deux. De plus, je n'ai pas assez de représentants pour créer de nouveaux tags. La raison de Java est parce que c'est supposément un langage très portable, donc l'écriture serait très intéressante. De plus, les versions java sont définies de manière à ce que nous puissions comparer les entrées détectant l'environnement avec uniformité, sans pour autant devoir comparer les oranges aux pommes.
Victor Stafusa
Vous pourriez considérer [sournois] en faisant valoir que la détection de version de VM est une étape dans l'attaque du système. Je ne peux pas dire que j'ai une autre suggestion.
dmckee
@dmckee Suppression de la balise [code-golf]. Ajoutez la balise [underhanded]. Pourriez-vous s'il vous plaît créer la balise [java]?
Victor Stafusa
4
Je vote pour fermer cette question comme hors sujet car les défis sournois ne sont plus sur le sujet sur ce site. meta.codegolf.stackexchange.com/a/8326/20469
cat
@cat, vous auriez dû supprimer la balise, car elle ne correspondait pas à la question.
Peter Taylor

Réponses:

9

6/102 = 0,0588

Détecte 6 versions. A 102 jetons lexicaux (au lieu de 103, après avoir supprimé publicdans 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 a introduit des encodages de caractères et des algorithmes cryptographiques à Java. Les versions ultérieures ont ajouté plus d'encodages et d'algorithmes. Ce programme essaie d'utiliser des encodages et des algorithmes jusqu'à ce qu'il intercepte une exception. J'attends un encodage manquant à lancer java.io.UnsupportedEncodingExceptionet un algorithme manquant à lancer java.security.NoSuchAlgorithmException.

J'avais un vieux PowerPC Macintosh avec quatre anciennes versions de Java. Ma machine OpenBSD a deux autres versions, j'ai donc testé ces six versions:

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

Ce programme peut également s'exécuter dans JamVM 1.5.4 et gcj 4.8.2 pour OpenBSD, mais ne les identifie pas comme des implémentations différentes. Il imprime uniquement "Java 5".

Mac OS Runtime pour Java

Grâce à "Écrire une fois, exécuter partout!", Je peux écrire ce programme une fois, le compiler une fois et exécuter une GuessVersion.class sur les huit machines virtuelles. J'ai besoin d'un compilateur pour Java 1.1, la plus ancienne version de ma collection.

Mon compilateur est l' javacoutil de MRJ SDK 2.2. Parce que le Mac OS classique n'avait pas de ligne de commande, javacest un joli outil graphique où je sélectionne les fichiers et les options et cliquez sur "Do Javac". Après avoir modifié mon code, je clique à nouveau sur "Do Javac".

javac de MRJ SDK 2.2 pour Mac OS classique

La façon la plus simple d'exécuter GuessVersion.class est de l'ouvrir dans JBindery, un autre outil du MRJ SDK 2.2. Le runtime est MRJ 2.2.6, une implémentation de Java 1.1.8.

kernigh
la source
22

Je ne sais pas quel est mon score, car cela dépend de ce que vous considérez précisément comme un jeton lexical, mais j'essaie d'abuser autant que possible de ce système de comptage avec une longue chaîne ...

Cela dépend aussi si vous comptez cela comme identifiant 7 versions différentes ou 16 ... (Il pourrait être étendu jusqu'à 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){}
    }
}

Il fonctionne en tentant de définir une interface dans un chargeur de classe personnalisé avec des numéros de version principaux décroissants du format de classe. Le premier qui ne jette pasjava.lang.UnsupportedClassVersionError correspond à la version de la VM.

Peter Taylor
la source
Compté 84 jetons. Mais je ne l'ai toujours pas testé.
Victor Stafusa
Votre réponse est géniale. Pourrait réduire trivialement à 83 jetons en utilisant String... args.
Victor Stafusa
@Victor, cela compliquerait la question de savoir s'il prend en charge 7 versions différentes encore plus. Je ne connais aucun compilateur qui prend en charge la syntaxe Java 5 et se compile en fichiers de classe compatibles Java 1.
Peter Taylor
Bon point. J'ai oublié cela.
Victor Stafusa
1
Java 1.1.8 (en MRJ 2.2.6) n'a pas réussi à compiler ce, jusqu'à ce que j'ajouté 17 autres jetons: protected Class loadClass(String name, boolean resolve) { return Object.class; }. Les documents API actuels négligent de mentionner en quoi il s'agissait d'une méthode abstraite avant Java 1.2. Je renvoie Object.class parce que la méthode reçoit un appel pour "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);
    }
}

L'algorithme d'internement a changé entre Java 6 et 7. Voir /programming//a/7224864/540552

XMLGregorianCalendar.equals (null) utilisé pour lancer NullPointerException dans java 5, mais cela a été corrigé dans java 6. Voir http://bugs.sun.com/view_bug.do?bug_id=6285370

100 96 92 87 85 jetons ici. Merci à Peter Taylor d'avoir réduit 7 jetons.

Victor Stafusa
la source
1
Vous pouvez enregistrer 3 jetons en stockant le numéro de version dans s1. Vous pouvez probablement enregistrer 2 autres en attrapant directement Throwable, en supposant que DatatypeConfigurationExceptioncela ne sera pas lancé.
Peter Taylor
1
Ou mieux, conservez-le int amais initialisez-le immédiatement, de sorte que le ifbloc devienne vide. Annulez la condition, supprimez le reste et utilisez --au lieu de l'affectation directe à a.
Peter Taylor