2014-09-02 7 views
14

Ist es möglich, dass JavaCache unerreichbar Bytecode für die folgende Prozedur generiert?versuchen Sie mit Ressourcen einführen unerreichbar Bytecode

public void ex06(String name) throws Exception { 
    File config = new File(name); 
    try (FileOutputStream fos = new FileOutputStream(config); 
      PrintWriter writer = new PrintWriter(new OutputStreamWriter(
        fos , "rw"))) { 
     bar(); 
    } 
} 

Wenn ich die Ausnahmetabelle des Bytecode Blick in (javap -v) gibt es folgende Einträge, die seltsam aussehen:

43 48 86 Class java/lang/Throwable 
43 48 95 any 

und

21 135 170 Class java/lang/Throwable 
21 135 179 any 

Das Problem ist jetzt dass ein Code nur erreichbar ist, wenn die Ausnahmen vom Typ "any" anstelle von Throwable abgefangen werden. Gibt es Situationen, in denen dies tatsächlich passieren kann?

====== EDIT ====== Danke für die Antworten bisher. Lassen Sie mich noch ein Stück Beweise geben zu zeigen, dass ich wirklich Handhabung nicht verstehen Ausnahme: Betrachten Sie die folgende Prozedur

Object constraintsLock; 
private String[] constraints; 
private String constraint; 
public void fp01() { 
    // Add this constraint to the set for our web application 
    synchronized (constraintsLock) { 
     String results[] = 
      new String[constraints.length + 1]; 
     for (int i = 0; i < constraints.length; i++) 
      results[i] = constraints[i];    
     results[constraints.length] = constraint; 
     constraints = results; 
    } 
} 

Wenn Sie in der Bytecode suchen Sie haben:

65: astore  4 
    67: aload_1  
    68: monitorexit 
    69: aload   4 

und die Ausnahme Tabelle

Exception table: 
    from to target type 
     7 62 65 any 
     65 69 65 any 

Bedeutet das, dass dieser Typ für immer Schleife?

+0

Bitte senden Sie das vollständige Bytecode. –

+0

https://dl.dropboxusercontent.com/u/26793257/example.txt –

+0

und hier ist der Jimple-Code (der von Ruß erzeugt wird): https://dl.dropboxusercontent.com/u/26793257/Beispiel. jimple.txt Ich habe zwei Kommentare eingefügt, um auf die relevanten Zeilen hinzuweisen. –

Antwort

15

Die Tatsache, dass jedes Throwable eine Instanz von java.lang.Throwable ist, wird an verschiedenen Stellen des Java-Byte-Codes/JVM impliziert. Auch wenn Handler für jede gedacht waren, etwas möglicherweise außerhalb der Throwable Typ Hierarchie darzustellen, scheitert die Idee wie die heutigen Klassendateien ein StackMapTable für Methoden enthalten Exception-Handler haben müssen und dass StackMapTable zum jeder throwable als Instanz beziehen von java.lang.Throwable1.

Auch mit dem alten Typ Inferenzabschnitts Verifizierer, eine Prozedur, die wieder wirft ein throwable implizit die Behauptung enthält, daß jeder throwable eine Instanz von java.lang.Throwable ist wie das einzige Objekt ist athrow erlaubt ist, zu werfen.

http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.athrow

The objectref müssen reference Typ sein und auf ein Objekt verweisen, das eine Instanz der Klasse ist Throwable oder eine Unterklasse von Throwable.

Kurze Antwort: Nein, es ist unmöglich, eine Situation zu haben, wo etwas anderes als eine Instanz von java.lang.Throwable (oder einer Unterklasse) geworfen werden kann oder gefangen.

Ich habe versucht, ein minimales Beispiel für eine Try-with-Resource-Anweisung zu erstellen, um die Ausgabe von javac zu analysieren. Das Ergebnis zeigt deutlich, dass die Struktur ein Artefakt davon ist, wie javac intern arbeitet, aber nicht beabsichtigt sein kann.

Das Beispiel sieht wie folgt aus:

public static void tryWithAuto() throws Exception { 
    try (AutoCloseable c=dummy()) { 
     bar(); 
    } 
} 
private static AutoCloseable dummy() { 
    return null; 
} 
private static void bar() { 
} 

(I mit jdk1.8.0_20 kompiliert)

ich die Ausnahmebehandlungstabelle zu Beginn des resultierenden Byte-Code eingeben, damit es einfacher ist, an den Ort, während zu beziehen in der Befehlsfolge suchen:

Exception table: 
    from to target type 
    17 23 26 Class java/lang/Throwable 
     6  9 44 Class java/lang/Throwable 
     6  9 49 any 
    58 64 67 Class java/lang/Throwable 
    44 50 49 any 

nun mit den Anweisungen:

Der Beginn ist einfach, zwei lokale Variablen werden verwendet, man den AutoCloseable (Index 0), das andere für die mögliche throwable (Index 1, initialisiert mit null) zu halten. dummy() und bar() aufgerufen wird, dann wird die AutoCloseable für null geprüft, um zu sehen, ob es geschlossen werden muss.

 0: invokestatic #2   // Method dummy:()Ljava/lang/AutoCloseable; 
    3: astore_0 
    4: aconst_null 
    5: astore_1 
    6: invokestatic #3   // Method bar:()V 
    9: aload_0 
    10: ifnull  86 

Wir bekommen hier, wenn die AutoCloseable nicht null und das erste seltsame Sache passiert ist, die throwable, die auf jeden Fall ist null geprüft wird für null

13: aload_1 
    14: ifnull  35 

Der folgende Code wird das AutoCloseable, bewacht von schließen der erste Ausnahmebehandler aus der obigen Tabelle, der addSuppressed aufruft. Da zu diesem Zeitpunkt, 1 Variable # ist null ist dieser dead-Code:

17: aload_0 
    18: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    23: goto   86 
    26: astore_2 
    27: aload_1 
    28: aload_2 
    29: invokevirtual #6   // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    32: goto   86 

Beachten Sie, dass die letzte Anweisung des toten Code ist goto 86, eine Verzweigung zu einem return so, wenn der Code oben nicht tot Code war sowieso wir konnten, starten Sie fragen sich, warum addSuppressed auf einem Throwable Aufruf plagen, die gleich danach ignoriert.

folgt nun den Code, wenn die Variable # 1 ausgeführt wird, ist null (lesen, immer). Es ruft einfach close und verzweigt in die return Anweisung, keine Ausnahme abfangen, so dass eine durch close() geworfene Ausnahme propagiert den Anrufer:

35: aload_0 
    36: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    41: goto   86 

Jetzt treten wir in den zweiten Exception-Handler, für den Körper der try Aussage, erklärt zu fangen Throwable, lesen Sie alle Ausnahmen. Es speichert die Throwable wie erwartet in die Variable # 1, speichert sie aber auch in der veralteten Variablen # 2. Dann wirft es erneut die Throwable.

44: astore_2 
    45: aload_2 
    46: astore_1 
    47: aload_2 
    48: athrow 

Der folgende Code ist das Ziel von zwei Ausnahmeroutinen. Erstens ist es das Ziel der überflüssige jede Exception-Handler, der den gleichen Bereich wie die Throwable Handler deckt damit, wie Sie vermutet, wird diese Prozedur nichts tun. Ferner ist es das Ziel des vierten Ausnahmebehandlers, der alles abfängt und den obigen Ausnahmebehandler abdeckt, so dass wir die erneut geworfene Ausnahme des Befehls 48 direkt eine Anweisung später abfangen.Um die Dinge noch lustiger zu machen, deckt der Ausnahmebehandler mehr als den obigen Handler ab; auf Platz # 50, exklusive endet, umfasst es auch die erste Anweisung von selbst:

49: astore_3 

Das erste, was ist es, eine dritte Variable einzuführen, um die gleiche throwable zu halten. Jetzt wird die AutoCloseable auf null überprüft.

50: aload_0 
    51: ifnull  84 

Nun ist die throwable der Variable # 1 ist für null geprüft. Es kann nur null sein, wenn das hypothetische Throwable kein Throwable ist. Aber beachten Sie, dass der gesamte Code würde in diesem Fall durch den Gutachter abgelehnt werden, da die StackMapTable alle Variablen und Operanden erklärt Einträge Stapel der jede throwable Halte Zuordnung kompatibel sein zu java.lang.Throwable

54: aload_1 
    55: ifnull  78 
    58: aload_0 
    59: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    64: goto   84 

Last but not least haben wir der Ausnahmebehandler, der die Ausnahme behandelt, die durch Schließen ausgelöst wird, wenn eine ausstehende Ausnahme vorhanden ist, die addSuppressed aufruft und die primäre Ausnahme erneut auslöst. Es führt eine andere lokale Variablen ein, die anzeigt, dass javacindeed never uses swap sogar wo angemessen ist.

67: astore  4 
    69: aload_1 
    70: aload   4 
    72: invokevirtual #6   // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    75: goto   84 

So sind die beiden folgenden Anweisungen sind nur dann, wenn Fang aufgerufen jede etwas anderes als java.lang.Throwable bedeuten könnte, das nicht der Fall ist. Der Code-Pfad verbindet sich bei # 84 mit dem regulären Fall.

78: aload_0 
    79: invokeinterface #4, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    84: aload_3 
    85: athrow 

    86: return 

So lautet das Fazit ist, dass die zusätzlichen Exception-Handler für jeden nur für toten Code von vier Befehlen verantwortlich sind, # 54, # 55, # 78 und 79 #, während es noch mehr toter Code ist für andere Gründe (# 17 - # 32), plus ein merkwürdiger "Wurf-und-Fang" (# 44 - # 48) Code, der auch ein Artefakt der Idee sein könnte, irgendein anders als Throwable zu behandeln. Außerdem hat ein Ausnahmebehandler einen falschen Bereich, der sich auf "Strange exception table entry produced by Sun's javac" als suggested in the comments bezieht.


Als Randbemerkung, produziert von Eclipse einfachen Code nimmt nur 60 Bytes statt 87 für die Befehlsfolge, nur die beiden erwarteten Ausnahmebehandler und mit drei lokalen Variablen anstelle von fünf Jahren. Und innerhalb dieses kompakteren Codes behandelt es den möglichen Fall, dass die Ausnahme, die vom Körper geworfen wird, dieselbe sein kann wie die eine durch close geschüttete Ausnahme, wobei addSuppressed nicht aufgerufen werden muss. Der javac generierte Code kümmert sich nicht darum.

 0: aconst_null 
    1: astore_0 
    2: aconst_null 
    3: astore_1 
    4: invokestatic #18  // Method dummy:()Ljava/lang/AutoCloseable; 
    7: astore_2 
    8: invokestatic #22  // Method bar:()V 
    11: aload_2 
    12: ifnull  59 
    15: aload_2 
    16: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    21: goto   59 
    24: astore_0 
    25: aload_2 
    26: ifnull  35 
    29: aload_2 
    30: invokeinterface #25, 1 // InterfaceMethod java/lang/AutoCloseable.close:()V 
    35: aload_0 
    36: athrow 
    37: astore_1 
    38: aload_0 
    39: ifnonnull  47 
    42: aload_1 
    43: astore_0 
    44: goto   57 
    47: aload_0 
    48: aload_1 
    49: if_acmpeq  57 
    52: aload_0 
    53: aload_1 
    54: invokevirtual #30  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V 
    57: aload_0 
    58: athrow 
    59: return 

Exception table: 
    from to target type 
     8 11 24 any 
     4 37 37 any 
+1

Große Analyse. Ich kam zu mehr oder weniger denselben Schlussfolgerungen, als ich versuchte, diese letzte Woche zu beantworten.Es scheint wahrscheinlich, dass es ein ~ Bug ~ ist. In jedem Fall +1 und alle. Vielen Dank! –