Schreiben Sie einen Java-Code, um die JVM-Version zu erkennen

17

Ziel ist es, Java-Code zu schreiben, der die JVM-Version anhand von Kompatibilitätsänderungen, Nebenwirkungen, Fehlern und / oder undefiniertem Verhalten erkennt, die in einer Version auf eine andere Art und Weise funktionieren. Außerdem sollte der Code mindestens ein bisschen lesbar sein, ohne auf Leerzeichen und lesbare Variablennamen zu verzichten.

Um dieses Ziel zu erreichen, lauten die genauen formalen Regeln:

  1. Der Code muss in Java geschrieben sein und die JRE-Version ausgeben, in der er ausgeführt wird.

  2. Der Code darf keine JDK- oder JRE-API verwenden, die speziell für die Erkennung der Java-Version bereitgestellt wurde oder die JDK- oder JRE-Version kostenlos zur Verfügung stellt.

  3. Der Code darf keine Reflektion verwenden.

  4. Der Code ist nur für die Verwendung in Hotspot Java SE 5, 6 und 7 erforderlich, kann jedoch auch in anderen JVMs verwendet werden.

  5. Der Code darf keine Bibliotheken von Drittanbietern im Klassenpfad verwenden.

  6. Der Code darf keinen anderen Prozess starten, weder Java noch.

  7. Der Code darf keine Umgebungsvariablen verwenden.

  8. Der Code darf das Dateisystem nicht nach bereits vorhandenen Dateien oder Ordnern durchsuchen.

  9. Der Code muss in einer einzigen Datei enthalten sein und über public static void main(String[] args)oder aufgerufen werden public static void main(String... args).

  10. Der Code darf keine nicht öffentliche API verwenden, die in der JRE vorhanden ist.

  11. Der Code darf während seiner Ausführung keine NoClassDefFoundError, NoSuchMethodError, ClassNotFoundException oder NoSuchMethodException generieren.

  12. Der Code sollte auf einem System ausgeführt werden, das nicht mit dem Internet oder einem lokalen Netzwerk verbunden ist.

  13. Sie sollten erklären, warum es sich in einer Version auf die eine und in einer anderen Version auf die andere Weise verhält.

Wertung

Die Methode zur Messung der besten Lösung ist max (n / s), wobei n die Anzahl der verschiedenen Java-Versionen ist, die erkannt wurden, ohne dass eine dieser Regeln verletzt wurde (mindestens Version 5, 6 und 7), und s die Anzahl der lexikalischen Token in der Lösung.

Victor Stafusa
quelle
Konnte kein besseres Tag finden, und ich musste mindestens zwei bereitstellen. Außerdem habe ich nicht genügend Mitarbeiter, um neue Tags zu erstellen. Der Grund für Java ist, dass es sich vermutlich um eine sehr portable Sprache handelt, so dass das Schreiben sehr interessant wäre. Darüber hinaus sind die Java-Versionen so definiert, dass wir Einträge, die die Umgebung erfassen, einheitlich vergleichen können, ohne dass Orangen mit Äpfeln verglichen werden müssen.
Victor Stafusa
Sie könnten in Betracht ziehen, zu argumentieren, dass die Erkennung der VM-Version ein Schritt ist, um das System anzugreifen. Ich kann nicht sagen, dass ich einen anderen Vorschlag habe.
dmckee
@dmckee Das Tag [code-golf] wurde entfernt. Fügen Sie das Tag [underhanded] hinzu. Könnten Sie bitte das [Java] -Tag erstellen?
Victor Stafusa
4
Ich stimme dafür, diese Frage als "Off-Topic" zu schließen, da hinterhältige Herausforderungen auf dieser Site nicht mehr zum Thema gehören. meta.codegolf.stackexchange.com/a/8326/20469
cat
@cat, du hättest stattdessen das Tag entfernen sollen, weil es nicht zu der Frage passte.
Peter Taylor

Antworten:

9

6/102 = 0,0588

Erkennt 6 Versionen. Hat 102 lexikalische Token (von 103 nach dem Löschen publicin 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 führte Zeichenkodierungen und kryptografische Algorithmen in Java ein. In späteren Versionen wurden weitere Codierungen und Algorithmen hinzugefügt. Dieses Programm versucht, Kodierungen und Algorithmen zu verwenden, bis eine Ausnahme auftritt. Ich erwarte, dass eine fehlende Kodierung java.io.UnsupportedEncodingExceptionund ein fehlender Algorithmus ausgelöst werden java.security.NoSuchAlgorithmException.

Ich hatte einen alten PowerPC Macintosh mit vier alten Java-Versionen. Meine OpenBSD-Maschine hat zwei weitere Versionen, deshalb habe ich diese sechs Versionen getestet:

  • Java 1.1.8 in MRJ 2.2.6 für Mac OS 9.2.2
  • Java 1.3.1_16 für Mac OS X Panther
  • Java 1.4.2_21 für Mac OS X Tiger
  • Java 1.5.0_19 für Mac OS X Tiger
  • OpenJDK 1.6.0_32 für OpenBSD 5.5
  • OpenJDK 1.7.0_21 für OpenBSD 5.5

Dieses Programm kann auch in JamVM 1.5.4 und gcj 4.8.2 für OpenBSD ausgeführt werden, identifiziert sie jedoch nicht als unterschiedliche Implementierungen. Es wird nur "Java 5" gedruckt.

Mac OS Runtime für Java

Dank "Einmal schreiben, überall ausführen!" Kann ich dieses Programm einmal schreiben, einmal kompilieren und in allen acht virtuellen Maschinen eine GuessVersion.class ausführen. Ich benötige einen Compiler für Java 1.1, die älteste Version meiner Sammlung.

Mein Compiler ist das javacTool aus MRJ SDK 2.2. Da das klassische Mac OS keine Befehlszeile hatte, javacist es ein hübsches grafisches Tool, in dem ich Dateien und Optionen auswähle und auf "Do Javac" klicke. Nachdem ich meinen Code bearbeitet habe, klicke ich erneut auf "Do Javac".

javac von MRJ SDK 2.2 für Classic Mac OS

Der einfachste Weg, GuessVersion.class auszuführen, besteht darin, es in JBindery, einem anderen Tool aus MRJ SDK 2.2, zu öffnen. Die Laufzeit ist MRJ 2.2.6, eine Implementierung von Java 1.1.8.

Kernigh
quelle
22

Ich bin mir nicht sicher, wie hoch meine Punktzahl ist, da dies davon abhängt, was genau Sie als lexikalisches Token betrachten, aber ich versuche, dieses Zählsystem mit einer langen Zeichenfolge so weit wie möglich zu missbrauchen ...

Es hängt auch davon ab, ob Sie dies als Identifizierung von 7 verschiedenen Versionen oder 16 ... (Es könnte trivial auf 190 erweitert werden).

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){}
    }
}

Es funktioniert, indem versucht wird, eine Schnittstelle in einem benutzerdefinierten Klassenladeprogramm mit absteigenden Hauptversionsnummern des Klassenformats zu definieren. Der erste, der kein wirft, java.lang.UnsupportedClassVersionErrorentspricht der VM-Version.

Peter Taylor
quelle
Zählte 84 Spielsteine. Habe es aber trotzdem nicht getestet.
Victor Stafusa
Ihre Antwort ist freundlich. Könnte sich mit trivialerweise auf 83 Token reduzieren String... args.
Victor Stafusa
@ Victor, das würde die Frage, ob es 7 verschiedene Versionen unterstützt, noch komplizierter machen. Mir ist kein Compiler bekannt, der Java 5-Syntax unterstützt und in Java 1-kompatible Klassendateien kompiliert.
Peter Taylor
Guter Punkt. Ich habe das vergessen.
Victor Stafusa
1
Java 1.1.8 (in MRJ 2.2.6) konnte dies kompilieren, bis ich 17 weitere Token hinzugefügt: protected Class loadClass(String name, boolean resolve) { return Object.class; }. In den aktuellen API-Dokumenten wird nicht erwähnt, dass dies vor Java 1.2 eine abstrakte Methode war. Ich gebe Object.class zurück, weil die Methode einen Aufruf für "java.lang.Object" erhält.
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);
    }
}

Der interne Algorithmus wurde zwischen Java 6 und 7 geändert. Siehe /programming//a/7224864/540552

XMLGregorianCalendar.equals (null) wurde verwendet, um NullPointerException in Java 5 auszulösen, dies wurde jedoch in Java 6 behoben. Siehe http://bugs.sun.com/view_bug.do?bug_id=6285370

100 96 92 87 85 Token hier. Vielen Dank an Peter Taylor für die Reduzierung von 7 Token.

Victor Stafusa
quelle
1
Sie können 3 Token speichern, indem Sie die Versionsnummer in s1 speichern. Sie können wahrscheinlich weitere 2 sparen, indem Sie Throwable direkt abfangen, unter der Annahme, dass DatatypeConfigurationExceptiones nicht geworfen wird.
Peter Taylor
1
Oder besser, behalten Sie es bei, int aaber initialisieren Sie es sofort, damit der ifBlock leer wird. Annullieren Sie die Bedingung, entfernen Sie das else und verwenden Sie --anstelle der direkten Zuordnung zu a.
Peter Taylor