Was können Sie in Java werfen?

Lesezeit: 4 Minuten

Benutzer-Avatar
NullUserException

Herkömmliche Weisheit besagt, dass Sie nur Objekte werfen können, die sich ausdehnen Throwable in Java, aber ist es möglich, den Bytecode-Verifizierer zu deaktivieren und Java dazu zu bringen, Code zu kompilieren und auszuführen, der beliebige Objekte – oder sogar Primitive – auslöst?

Ich habe die JVMs nachgeschlagen athrow und es wird das erste objref auf dem Operandenstapel erscheinen; aber würde es prüfen, ob die Referenz auf a zeigt Throwable zur Laufzeit?

  • Interessante Frage – aber warum sollten Sie in der Lage sein wollen, ein nicht werfbares Objekt zu werfen? (Ist es nur, um einen Fehler zu öffnen?)

    – RonK

    21. April 2011 um 21:25 Uhr

  • @RonK Es ist meistens Neugier; Ich möchte wissen, warum Java uns dazu zwingt, Objekte mit einem größeren Fußabdruck zu werfen, als sie sein könnten. Hinweis: Ich beabsichtige nicht, dies in echtem Code zu verwenden.

    – NullUserException

    21. April 2011 um 21:34 Uhr


  • @RonK: Zu den gültigen Anwendungsfällen gehört das Entwerfen eines neuen Java-Dialekts oder einer neuen JVM-Sprache.

    – Charlie

    15. August 2016 um 11:32 Uhr

Benutzer-Avatar
John Kugelmann

Dies hängt von Ihrer JVM-Implementierung ab. Gemäß der Java-VM-Spezifikation handelt es sich um ein undefiniertes Verhalten, wenn das Objekt dies nicht ist Throwable.

Die Objektref muss vom Typ Referenz sein und auf ein Objekt verweisen, das eine Instanz der Klasse Throwable oder einer Unterklasse von Throwable ist.

Im Abschnitt 6.1, „Die Bedeutung von ‚Muss‘“:

Wenn eine Bedingung (ein “Muss” oder “Darf nicht”) in einer Befehlsbeschreibung zur Laufzeit nicht erfüllt ist, ist das Verhalten der Java Virtual Machine undefiniert.

Ich habe ein Testprogramm mit dem geschrieben Jasmin Assembler was das Äquivalent von tut throw new Object(). Die Java-HotSpot-Server-VM wirft a VerifyError:

# cat Athrow.j .source Athrow.j .class public Athrow .super java/lang/Object .method public ()V aload_0 invokenonvirtual java/lang/Object/()V return .end method .method öffentliches statisches main([Ljava/lang/String;)V
    .limit stack 2

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V
    athrow

    return
.end method

# java -jar jasmin.jar Athrow.j 
Generated: Athrow.class

# java Athrow
Exception in thread "main" java.lang.VerifyError: (class: Athrow, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects

Disabling the bytecode verifier allows the athrow to execute and the JVM appears to crash when it tries to print the exception’s details. Compare these two programs, the first which throws an Exception, the second which is the above test program which throws an Object. Notice how it exits in the middle of a printout:

# java -Xverify:none examples/Uncaught
Exception in thread "main" java.lang.Exception
        at examples.Uncaught.main(Uncaught.j)
# java -Xverify:none Athrow
Exception in thread "main" #

Of course, disabling the bytecode verifier is dangerous. The VM proper is written to assume that bytecode verification has been performed and therefore does not have to typecheck instruction operands. Beware: the undefined behavior that you invoke when you circumvent bytecode verification is much like the undefined behavior in C programs; anything at all can happen, including demons flying out of your nose.

As mentioned in John’s answer, you can disable verification (placing the class on the bootclasspath should also work ), load and execute a class throwing a non-Throwable class successfully.

Surprisingly, it doesn’t necessarily lead to a crash!

As long as you don’t call Throwable methods implicitly or explicitly, everything will work perfectly fine:

.source ThrowObject.j
.class public ThrowObject
.super java/lang/Object

.method public <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()V
    return
.end method

.method public static main([Ljava/lang/String;)V

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V

  BeforeThrow:
    athrow

  AfterThrow:

    return

  CatchThrow:

    getstatic java/lang/System/out Ljava/io/PrintStream;
    ldc "Thrown and catched Object successfully!"
    invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
    return

  .catch all from BeforeThrow to AfterThrow using CatchThrow
.end method

Result:

% java -Xverify:none ThrowObject
Thrown and catched Object successfully!

user avatar
aioobe

[…] Deaktivieren Sie den Bytecode-Verifier […]

Die Bytecode-Überprüfung ist Teil der JVM-Spezifikation. Wenn Sie diese also deaktivieren (oder die JVM auf andere Weise manipulieren), können Sie je nach Implementierung fast alles tun (einschließlich des Auslösens von Primitives usw.), würde ich annehmen.

Zitat aus der JVM-Spezifikation:

Die Objektref muss eine Typreferenz sein und muss auf ein Objekt verweisen, das eine Instanz der Klasse Throwable ist oder einer Unterklasse von Throwable.

Dh, Ihre Frage kann interpretiert werden als “Wenn eine JVM von der Spezifikation abweicht, kann sie seltsame Dinge tun, z. B. Primitiven werfen” und die Antwort ist natürlich, ja.

1084480cookie-checkWas können Sie in Java werfen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy