# HG changeset patch # User goetz # Date 1593596705 -7200 # Wed Jul 01 11:45:05 2020 +0200 # Node ID 15bab0b3f7822d5bbc3f7e5064256989a9dcc602 # Parent d0b86d63c00f7e00362284628af09b0355b6e326 8248476: No helpful NullPointerException message after calling fillInStackTrace Summary: reported by christoph.dreis@freenet.de Reviewed-by: diff --git a/src/java.base/share/classes/java/lang/NullPointerException.java b/src/java.base/share/classes/java/lang/NullPointerException.java --- a/src/java.base/share/classes/java/lang/NullPointerException.java +++ b/src/java.base/share/classes/java/lang/NullPointerException.java @@ -70,6 +70,16 @@ super(s); } + private transient int numStackTracesFilledIn; + + /** + * {@inheritDoc} + */ + public synchronized Throwable fillInStackTrace() { + numStackTracesFilledIn++; + return super.fillInStackTrace(); + } + /** * Returns the detail message string of this throwable. * @@ -88,7 +98,9 @@ */ public String getMessage() { String message = super.getMessage(); - if (message == null) { + // If the stack trace was changed the extended NPE algorithm + // will compute a wrong message. + if (message == null && numStackTracesFilledIn == 1) { return getExtendedNPEMessage(); } return message; diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java --- a/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java @@ -28,7 +28,7 @@ * @summary Test extended NullPointerException message for * classfiles generated with debug information. In this case the name * of the variable containing the array is printed. - * @bug 8218628 + * @bug 8218628 8248476 * @modules java.base/java.lang:open * java.base/jdk.internal.org.objectweb.asm * @library /test/lib @@ -41,7 +41,7 @@ * @summary Test extended NullPointerException message for class * files generated without debugging information. The message lists * detailed information about the entity that is null. - * @bug 8218628 + * @bug 8218628 8248476 * @modules java.base/java.lang:open * java.base/jdk.internal.org.objectweb.asm * @library /test/lib @@ -1281,8 +1281,76 @@ String msg = new String("A pointless message"); Asserts.assertTrue(new NullPointerException(msg).getMessage() == msg); + // If the stack trace was set by the user, the message should not be + // created. + // This holds for explicitly crated NPEs, but also for implicilty + // thrown ones. + System.out.println(new NullPointerException().fillInStackTrace().getMessage()); + System.out.println(new NullPointerException(msg).fillInStackTrace().getMessage()); + + Asserts.assertNull(new NullPointerException().fillInStackTrace().getMessage()); + Asserts.assertTrue(new NullPointerException(msg).fillInStackTrace().getMessage() == msg); + + NullPointerException ex = new NullPointerException(); + Throwable t = ex.fillInStackTrace(); + + Asserts.assertNull(t.getMessage()); + + ex = new NullPointerException(msg); + t = ex.fillInStackTrace(); + Asserts.assertTrue(t.getMessage() == msg); + + F f = null; + try { + f.i = 17; + } catch (NullPointerException e) { + checkMessage(e, "f.i = 17;", e.getMessage(), + "Cannot assign field \"i\" because " + + (hasDebugInfo ? "\"f\"" : "\"\"") + " is null"); + t = e.fillInStackTrace(); + } + checkMessage(t, "e.fillInStackTrace()", t.getMessage(), null); + + // Make sure a new exception thrown when calling fillInStackTrace() + // gets the correct message. + ex = null; + try { + ex.fillInStackTrace(); + } catch (NullPointerException e) { + checkMessage(e, "ex.fillInStackTrace()", e.getMessage(), + "Cannot invoke \"java.lang.NullPointerException.fillInStackTrace()\" because " + + (hasDebugInfo ? "\"ex\"" : "\"\"") + " is null"); + } + + // setStackTrace does not affect computing the message. + // Message and stack trace won't match, though. + F f1 = null; + F f2 = null; + NullPointerException e1 = null; + NullPointerException e2 = null; + try { + f1.i = 18; + } catch (NullPointerException e) { + checkMessage(e, "f1.i = 18;", e.getMessage(), + "Cannot assign field \"i\" because " + + (hasDebugInfo ? "\"f1\"" : "\"\"") + " is null"); + e1 = e; + } + try { + f2.i = 19; + } catch (NullPointerException e) { + checkMessage(e, "f2.i = 19;", e.getMessage(), + "Cannot assign field \"i\" because " + + (hasDebugInfo ? "\"f2\"" : "\"\"") + " is null"); + e2 = e; + } + e1.setStackTrace(e2.getStackTrace()); + checkMessage(e1, "f1.i = 18;", e1.getMessage(), + "Cannot assign field \"i\" because " + + (hasDebugInfo ? "\"f1\"" : "\"\"") + " is null"); + // If created via reflection, the message should not be generated. - Exception ex = NullPointerException.class.getDeclaredConstructor().newInstance(); + ex = NullPointerException.class.getDeclaredConstructor().newInstance(); Asserts.assertNull(ex.getMessage()); }