Feature request: NullPointer with several objects in same source code line - easily spot the object being null
Krystal Mo
krystal.mo at oracle.com
Tue Mar 5 21:36:22 PST 2013
Hi Josef,
Generally it's hard to implement such feature in an efficient way. It
may not be as simple as you might have expected.
Your best option is to make good use of
java.util.Objects.requiresNotNull [1], which does throw an NPE and with
an appropriate message that you can specify.
First of all, NPE is "null Pointer exception", not "null Object
exception". Only Java references can be null; Java objects can't.
Second, what you're asking for is actually "let the JVM blame the
variable or parameter causing the NullPointerException". Which isn't
easy to do.
Not all references are stored in named variables; temporary ones don't,
so there would be no "name" to map the NPE onto. e.g.
foo.bar.baz.someMethod(); // will throw NPE if bar is null
foo.bar().baz(); // will throw NPE if bar() returned null
Looking at the problem from the abstract conceptual JVM level, you can
only dereference a Java reference on the operand stack (and thus throw
NPE when the reference is null). Even if a reference came from a local
variable (or method parameter), it's still not always statically-known
which variable it came from. e.g.
public class Foo {
public static int demo(boolean cond, String s1, String s2) {
return (cond ? s1 : s2).length();
}
}
Foo.demo() would be compiled into:
public static int demo(boolean, java.lang.String, java.lang.String);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=3
0: iload_0
1: ifeq 8
4: aload_1
5: goto 9
8: aload_2
9: invokevirtual #2 // Method
java/lang/String.length:()I
12: ireturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 cond Z
0 13 1 s1 Ljava/lang/String;
0 13 2 s2 Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 8 /* same */
frame_type = 64 /* same_locals_1_stack_item */
stack = [ class java/lang/String ]
The only bytecode instruction that can throw a NPE is invokevirtual
java.lang.String.length(), which pops a reference off the top of the
operand stack, and dereference it to do the virtual method invocation.
At that point, the reference on the top of the operand stack could
either be from method parameter s1 or s2, depending on the value of
cond. Since this can't be deduced statically, a JVM will have to some
sort of flow tracking to know where a reference came from, which is hard
and slow.
That said, it may be nice if the Throwable core library classes can
expose more details of an exception, such as the bytecode index of the
exception site. That way you'd at least get an idea which bytecode
instruction went wrong, and use that as a starting point to manually
analyze which reference was null.
- Kris
[1]:
http://docs.oracle.com/javase/7/docs/api/java/util/Objects.html#requireNonNull(T,
java.lang.String)
On 2013/2/21 10:07, Josef Lehner wrote:
> Dear Hotspot-Runtime team,
>
> every once in a while every Java programmer encounters a
> NullPointerException and it is often hard to find out which object is
> null, especially in production where debugging isn't possible.
>
> It would be fantastic if the jvm could blame the object causing the
> NullPointerException.
>
> Thanks in advance.
>
> Best regards
>
> Josef
>
> String s1=null;
> String s2=null;
> s1.concat(s2.toString()); // Line 7
>
> Exception in thread "main" java.lang.NullPointerException <<< should
> be something like >Exception in thread "main"
> java.lang.NullPointerException: s2 is null<
> at stuff.Example.main(Example.java:7)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> at java.lang.reflect.Method.invoke(Method.java:487)
> at
> com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
More information about the hotspot-runtime-dev
mailing list