JDK-8233268 : Improve integration of Objects.requireNonNull and JEP 358 Helpful NPE
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 14
  • Priority: P3
  • Status: New
  • Resolution: Unresolved
  • Submitted: 2019-10-30
  • Updated: 2020-01-02
Related Reports
Relates :  
Relates :  
Relates :  
Description
The methods of java.util.Objects.requireNonNull were introduced to improve the timeliness and quality of information about NullPointerExceptions.
They perform null checks early and provides a recognizable point in the code to check and to throw exceptions with additional useful information.

JEP 358: Helpful NullPointerExceptions has access to the bytecode and its messages identify the source of the null based on the bytecode.  

The use of Objects.requireNonNull methods has the effect of disabling JEP 358; the information about the origin of the NPE is not available to Objects.requireNonNull.

These two mechanisms are not well integrated.

Note that JEP 358 introduces some stack frame analysis which collects the extra diagnostic information.  In the case of Objects.requireNonNull, this information should be sought relative to the code which pushed its argument on the stack, in the caller of Objects.requireNonNull.  This requires an extension to the existing analysis.

Possible fix #1:   Define an annotation (private to java.base) which tells the stack walking logic to skip the frame and seek the problem in the caller frame.  Apply the annotation to Objects.requireNonNull and/or its first argument.  Perhaps this annotation's function could be merged with an existing @Hidden marker that tells stack walkers to elide marked methods, since this is arguably an elision request.

Possible fix #2:   Up-level more of the stack walking logic from JSR 358 from the JVM to library code, using the stack walker API.  Write ad hoc Java code to skip Objects.requireNonNull, and walk the bytecodes of the caller.  (This might require a new API for cracking methods found during stack walking.)

As an undesirable workaround, users of Objects.requireNonNull could be asked to add their own hand-written diagnostic string as the optional second argument.  This is thought to be cumbersome and potentially inaccurate, as well as adding extra noise to code that should be easy to read.
Comments
Objects.requireNonNull() per design leads to the expression that is null. Having the code at hand it's obvious what is null. Thus, the benefit of a generated message is much less. But with the algorithm of JEP 358 the expression passed to requireNonNull() can be printed. E.g., the following message could be generated: B myB = new B(); myB.a = null; Objects.requireNonNull(myB.a); java.lang.NullPointerException: myB.a, passed from testGeneratedMessage() to requireNonNull(), is null at java.base/java.util.Objects.requireNonNull(Objects.java:222) at RequireNonNullTest.testGeneratedMessage(RequireNonNullTest.java:97) The non-trivial part in the message is printing "myB.a". To compute this expression, the code printing the "because" part of JEP 358 can be used. See prototype: http://cr.openjdk.java.net/~goetz/wr20/8233268-requireNonNull/ > users of Objects.requireNonNull could be asked to add their own diagnostic string There already is requireNonNull(Object o, String msg).
02-01-2020