Helpful NullPointerException (JVM)

Just saw on the notes for Java 14 a little gem:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
    at NullPointerExample.main(NullPointerExample.java:5)

This seems to be a JVM behavior, not related to the Java language… so I guess all JVM languages might benefit? Anybody tried it yet?

See https://blogs.oracle.com/javamagazine/java-14-arrives-with-a-host-of-new-features

It requires a new option to be enabled -XX:+ShowCodeDetailsInExceptionMessages

It originates from this proposal https://openjdk.java.net/jeps/358 which also explains how/when the extra detail can be computed. It will be interesting to see how this works using a higher-level language like Clojure, compiled down to bytecode…

2 Likes

Well, that got me curious, and it’s easy to test so…

> JAVA_HOME=$OPENJDK14_HOME clj -J-XX:+ShowCodeDetailsInExceptionMessages
Clojure 1.10.1
user=> (System/getProperty "java.version")
"14"
user=> (require '[clojure.string :as str])
nil
user=> (str/lower-case nil)
Execution error (NullPointerException) at user/eval5 (REPL:1).
Cannot invoke "Object.toString()" because "s" is null
user=> (* nil 42)
Execution error (NullPointerException) at user/eval7 (REPL:1).
Cannot invoke "Object.getClass()" because "x" is null
user=> (* 42 nil)
Execution error (NullPointerException) at user/eval9 (REPL:1).
Cannot invoke "Object.getClass()" because "x" is null
user=> (def n nil)
#'user/n
user=> (n 1 2 3)
Execution error (NullPointerException) at user/eval12 (REPL:1).
Cannot invoke "clojure.lang.IFn.invoke(Object, Object, Object)" because the return value of "clojure.lang.Var.getRawRoot()" is null

I’d say that’s a wash because the exception messages are deduced from the Clojure runtime that is written in Java so it’s a bit cryptic in places.

EDIT: I dug a bit deeper and some of the cryptic aspects come from how the compiler generates chained function calls so I suspect it would slow Clojure down to refactor the generated code in ways that might improve the messages.

2 Likes

Ouch - the last ones are worse than what we have today :cry:

1 Like

What I understood from some tweets, is that the purpose was restricted to the logs from the JVM itself, not application/Clojure code.

As you can see, you get additional information in nearly all null pointer exception messages, if the new option is enabled. That additional information is derived from the JVM bytecode so all languages that run on the JVM would be affected. Whether that extra information is useful is definitely subjective. The examples given above come from a mixture of the Clojure runtime – which is written in Java – and the generated JVM bytecode from the Clojure compiler – which isn’t even actual code.

Certainly, for programs written in Java, the additional information is going to make more sense since it is going to be able to come much closer to the actual source code that triggered the NPE. It’s a big improvement in those cases.

1 Like