What can you throw in Java?

NullUserException picture NullUserException · Apr 21, 2011 · Viewed 8k times · Source

Conventional wisdom says you can only throw objects that extend Throwable in Java, but is it possible to disable the bytecode verifier and get Java to compile and run code that throws arbitrary objects - or even primitives?

I looked up the JVM's athrow and it will pop the first objref on the operand stack; but would it check if said reference points to a Throwable at run time?

Answer

John Kugelman picture John Kugelman · Apr 21, 2011

It depends on your JVM implementation. According to the Java VM specification it is undefined behavior if the object is not Throwable.

The objectref must be of type reference and must refer to an object that is an instance of class Throwable or of a subclass of Throwable.

In section 6.1, "The Meaning of 'Must'":

If some constraint (a "must" or "must not") in an instruction description is not satisfied at run time, the behavior of the Java virtual machine is undefined.

I wrote a test program using the Jasmin assembler which does the equivalent of throw new Object(). The Java HotSpot Server VM throws a VerifyError:

# cat Athrow.j 
.source Athrow.j
.class public Athrow
.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
    .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.