62

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?

NullUserException
  • 83,810
  • 28
  • 209
  • 234
  • 4
    Interesting question - but why would you want to be able to throw a non-throwable? (Is it just to open a bug?) – RonK Apr 21 '11 at 21:25
  • @RonK It's mostly curiosity; I want to know what's the reason why Java forces us to throw objects with a larger footprint than they could be. Note I don't intend to use this in real code. – NullUserException Apr 21 '11 at 21:34
  • @RonK: Valid usecases include designing a new java dialect, or a new JVM language. – charlie Aug 15 '16 at 11:32

3 Answers3

74

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.

NullUserException
  • 83,810
  • 28
  • 209
  • 234
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
8

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!
soc
  • 27,983
  • 20
  • 111
  • 215
5

[...] disable the bytecode verifier [...]

The bytecode verification is part of the JVM spec, so if you disable this (or tamper with the JVM in other ways), you can, depending on implementation, do just about anything (including throwing primitives etc) I would assume.

Quote from the JVM specification:

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.

I.e., your question can be interpreted as "If a JVM deviates from the specification, can it do weird stuff such as throwing primitivs" and the answer is of course, yes.

aioobe
  • 413,195
  • 112
  • 811
  • 826