/* * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @summary Test extended NullPointerException message for * classfiles generated with debug information. In this case the name * of the variable containing the array is printed. * @modules java.base/java.lang:open * java.base/jdk.internal.org.objectweb.asm * @library /test/lib * @compile -g NullPointerExceptionTest.java * @run main/othervm -XX:MaxJavaStackTraceDepth=1 -XX:+ShowCodeDetailsInExceptionMessages NullPointerExceptionTest hasDebugInfo */ /** * @test * @summary Test extended NullPointerException message for class * files generated without debugging information. The message lists * detailed information about the entity that is null. * @modules java.base/java.lang:open * java.base/jdk.internal.org.objectweb.asm * @library /test/lib * @compile NullPointerExceptionTest.java * @run main/othervm -XX:MaxJavaStackTraceDepth=1 -XX:+ShowCodeDetailsInExceptionMessages NullPointerExceptionTest */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import jdk.test.lib.Asserts; import java.lang.reflect.*; import java.lang.invoke.MethodHandles.Lookup; import static java.lang.invoke.MethodHandles.*; import static java.lang.invoke.MethodHandles.Lookup.*; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.Label; import static jdk.internal.org.objectweb.asm.Opcodes.*; /** * Tests NullPointerExceptions */ public class NullPointerExceptionTest { // Some fields used in the test. static Object nullStaticField; NullPointerExceptionTest nullInstanceField; static int[][][][] staticArray; static long[][] staticLongArray = new long[1000][]; DoubleArrayGen dag; ArrayList names = new ArrayList<>(); ArrayList curr; static boolean hasDebugInfo = false; static { staticArray = new int[1][][][]; staticArray[0] = new int[1][][]; staticArray[0][0] = new int[1][]; } public static void checkMessage(Throwable t, String expression, String obtainedMsg, String expectedMsg) { //System.out.println(" thrown msg: " + obtainedMsg); System.out.println("\nSource code:\n " + expression + "\n\nOutput:"); t.printStackTrace(System.out); if (obtainedMsg != expectedMsg && // E.g. both are null. !obtainedMsg.equals(expectedMsg)) { // GL: need to fix the test cases! System.out.println("expected msg: " + expectedMsg); Asserts.assertEquals(expectedMsg, obtainedMsg); } System.out.println("\n----"); } public static void main(String[] args) throws Exception { NullPointerExceptionTest t = new NullPointerExceptionTest(); if (args.length > 0) { hasDebugInfo = true; } System.out.println("Tests for the first part of the message:"); System.out.println("========================================\n"); // Test the message printed for the failed action. t.testFailedAction(); System.out.println("Tests for the second part of the message:"); System.out.println("=========================================\n"); // Test the method printed for the null entity. t.testNullEntity(); System.out.println("Further tests:"); System.out.println("==============\n"); // Test if parameters are used in the code. // This is relevant if there is no debug information. t.testParameters(); // Test that no message is printed for exceptions // allocated explicitly. t.testCreation(); // Test that no message is printed for exceptions // thrown in native methods. t.testNative(); // Test that two calls to getMessage() return the same // message. // It is a design decision that it returns two different // String objects. t.testSameMessage(); // Test serialization. // It is a design decision that after serialization the // the message is lost. t.testSerialization(); // Test that messages are printed for code generated // on-the-fly. t.testGeneratedCode(); // Some more interesting complex messages. t.testComplexMessages(); } // Helper method to cause test case. private double callWithTypes(String[][] dummy1, int[][][] dummy2, float dummy3, long dummy4, short dummy5, boolean dummy6, byte dummy7, double dummy8, char dummy9) { return 0.0; } public void testFailedAction() { int[] ia1 = null; float[] fa1 = null; Object[] oa1 = null; boolean[] za1 = null; byte[] ba1 = null; char[] ca1 = null; short[] sa1 = null; long[] la1 = null; double[] da1 = null; // iaload try { int val = ia1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "int val = ia1[0];", e.getMessage(), "Cannot load from int array because " + (hasDebugInfo ? "'ia1'" : "''") + " is null."); } // faload try { float val = fa1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "float val = fa1[0];", e.getMessage(), "Cannot load from float array because " + (hasDebugInfo ? "'fa1'" : "''") + " is null."); } // aaload try { Object val = oa1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "Object val = oa1[0];", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'oa1'" : "''") + " is null."); } // baload (boolean) try { boolean val = za1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "boolean val = za1[0];", e.getMessage(), "Cannot load from byte/boolean array because " + (hasDebugInfo ? "'za1'" : "''") + " is null."); } // baload (byte) try { byte val = ba1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "byte val = ba1[0];", e.getMessage(), "Cannot load from byte/boolean array because " + (hasDebugInfo ? "'ba1'" : "''") + " is null."); } // caload try { char val = ca1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "char val = ca1[0];", e.getMessage(), "Cannot load from char array because " + (hasDebugInfo ? "'ca1'" : "''") + " is null."); } // saload try { short val = sa1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "short val = sa1[0];", e.getMessage(), "Cannot load from short array because " + (hasDebugInfo ? "'sa1'" : "''") + " is null."); } // laload try { long val = la1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "long val = la1[0];", e.getMessage(), "Cannot load from long array because " + (hasDebugInfo ? "'la1'" : "''") + " is null."); } // daload try { double val = da1[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "double val = da1[0];", e.getMessage(), "Cannot load from double array because " + (hasDebugInfo ? "'da1'" : "''") + " is null."); } // iastore try { ia1[0] = 0; System.out.println(ia1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "ia1[0] = 0;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'ia1'" : "''") + " is null."); } // fastore try { fa1[0] = 0.7f; System.out.println(fa1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "fa1[0] = 0.7f;", e.getMessage(), "Cannot store to float array because " + (hasDebugInfo ? "'fa1'" : "''") + " is null."); } // aastore try { oa1[0] = new Object(); System.out.println(oa1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "oa1[0] = new Object();", e.getMessage(), "Cannot store to object array because " + (hasDebugInfo ? "'oa1'" : "''") + " is null."); } // bastore (boolean) try { za1[0] = false; System.out.println(za1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "za1[0] = false;", e.getMessage(), "Cannot store to byte/boolean array because " + (hasDebugInfo ? "'za1'" : "''") + " is null."); } // bastore (byte) try { ba1[0] = 0; System.out.println(ba1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "ba1[0] = 0;", e.getMessage(), "Cannot store to byte/boolean array because " + (hasDebugInfo ? "'ba1'" : "''") + " is null."); } // castore try { ca1[0] = 0; System.out.println(ca1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "ca1[0] = 0;", e.getMessage(), "Cannot store to char array because " + (hasDebugInfo ? "'ca1'" : "''") + " is null."); } // sastore try { sa1[0] = 0; System.out.println(sa1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "sa1[0] = 0;", e.getMessage(), "Cannot store to short array because " + (hasDebugInfo ? "'sa1'" : "''") + " is null."); } // lastore try { la1[0] = 0; System.out.println(la1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "la1[0] = 0;", e.getMessage(), "Cannot store to long array because " + (hasDebugInfo ? "'la1'" : "''") + " is null."); } // dastore try { da1[0] = 0; System.out.println(da1[0]); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "da1[0] = 0;", e.getMessage(), "Cannot store to double array because " + (hasDebugInfo ? "'da1'" : "''") + " is null."); } // arraylength try { int val = za1.length; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, " int val = za1.length;", e.getMessage(), "Cannot read the array length because " + (hasDebugInfo ? "'za1'" : "''") + " is null."); } // athrow try { RuntimeException exc = null; throw exc; } catch (NullPointerException e) { checkMessage(e, "throw exc;", e.getMessage(), "Cannot throw exception because " + (hasDebugInfo ? "'exc'" : "''") + " is null."); } // monitorenter try { synchronized (nullInstanceField) { // desired } } catch (NullPointerException e) { checkMessage(e, "synchronized (nullInstanceField) { ... }", e.getMessage(), "Cannot enter synchronized block because " + "'this.nullInstanceField' is null."); } // monitorexit // No test available // getfield try { Object val = nullInstanceField.nullInstanceField; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "Object val = nullInstanceField.nullInstanceField;", e.getMessage(), "Cannot read field 'nullInstanceField' because " + "'this.nullInstanceField' is null."); } // putfield try { nullInstanceField.nullInstanceField = new NullPointerExceptionTest(); System.out.println(nullInstanceField.nullInstanceField); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "nullInstanceField.nullInstanceField = new NullPointerExceptionTest();", e.getMessage(), "Cannot assign field 'nullInstanceField' because " + "'this.nullInstanceField' is null."); } // invoke try { String val = nullInstanceField.toString(); System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "String val = nullInstanceField.toString();", e.getMessage(), "Cannot invoke 'Object.toString()' because " + "'this.nullInstanceField' is null."); } // Test parameter and return types try { boolean val = (nullInstanceField.callWithTypes(null, null, 0.0f, 0L, (short)0, false, (byte)0, 0.0, 'x') == 0.0); Asserts.assertTrue(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "boolean val = (nullInstanceField.callWithTypes(null, null, 0.0f, 0L, (short)0, false, (byte)0, 0.0, 'x') == 0.0);", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest.callWithTypes(String[][], int[][][], float, long, short, boolean, byte, double, char)' because " + "'this.nullInstanceField' is null."); } } static void test_iload() { int i0 = 0; int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int[][] a = new int[6][]; // iload_0 try { a[i0][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[i0][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[i0]'" : "'[]'") + " is null."); } // iload_1 try { a[i1][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[i1][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[i1]'" : "'[]'") + " is null."); } // iload_2 try { a[i2][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[i2][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[i2]'" : "'[]'") + " is null."); } // iload_3 try { a[i3][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[i3][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[i3]'" : "'[]'") + " is null."); } // iload try { a[i5][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[i5][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[i5]'" : "'[]'") + " is null."); } } // Other datatyes than int are not needed. // If we implement l2d and similar bytecodes, we can print // long expressions as array indexes. Then these here could // be used. static void test_lload() { long long0 = 0L; // l0 looks like 10. Therefore long0. long long1 = 1L; long long2 = 2L; long long3 = 3L; long long4 = 4L; long long5 = 5L; int[][] a = new int[6][]; // lload_0 try { a[(int)long0][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)long0][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // lload_1 try { a[(int)long1][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)long1][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // lload_2 try { a[(int)long2][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)long2][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // lload_3 try { a[(int)long3][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)long3][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // lload try { a[(int)long5][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)long5][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } } static void test_fload() { float f0 = 0.0f; float f1 = 1.0f; float f2 = 2.0f; float f3 = 3.0f; float f4 = 4.0f; float f5 = 5.0f; int[][] a = new int[6][]; // fload_0 try { a[(int)f0][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)f0][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // fload_1 try { a[(int)f1][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)f1][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // fload_2 try { a[(int)f2][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)f2][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // fload_3 try { a[(int)f3][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)f3][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } // fload try { a[(int)f5][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)f5][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[...]'" : "'[...]'") + " is null."); } } static void test_aload() { F f0 = null; F f1 = null; F f2 = null; F f3 = null; F f4 = null; F f5 = null; // aload_0 try { f0.i = 33; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "f0.i = 33;", e.getMessage(), "Cannot assign field 'i' because " + (hasDebugInfo ? "'f0'" : "''") + " is null."); } // aload_1 try { f1.i = 33; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "f1.i = 33;", e.getMessage(), "Cannot assign field 'i' because " + (hasDebugInfo ? "'f1'" : "''") + " is null."); } // aload_2 try { f2.i = 33; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "f2.i = 33;", e.getMessage(), "Cannot assign field 'i' because " + (hasDebugInfo ? "'f2'" : "''") + " is null."); } // aload_3 try { f3.i = 33; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "f3.i = 33;", e.getMessage(), "Cannot assign field 'i' because " + (hasDebugInfo ? "'f3'" : "''") + " is null."); } // aload try { f5.i = 33; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "f5.i = 33;", e.getMessage(), "Cannot assign field 'i' because " + (hasDebugInfo ? "'f5'" : "''") + " is null."); } } // Helper class for test cases. class A { public B to_b; public B getB() { return to_b; } } // Helper class for test cases. class B { public C to_c; public B to_b; public C getC() { return to_c; } public B getBfromB() { return to_b; } } // Helper class for test cases. class C { public D to_d; public D getD() { return to_d; } } // Helper class for test cases. class D { public int num; public int[][] ar; } public void testArrayChasing() { int[][][][][][] a = null; try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a is null", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'a'" : "''") + " is null."); } a = new int[1][][][][][]; try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a[0] is null", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null."); } a[0] = new int[1][][][][]; try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a[0][0] is null", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'a[0][0]'" : "'[0][0]'") + " is null."); } a[0][0] = new int[1][][][]; try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a[0][0][0] is null", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'a[0][0][0]'" : "'[0][0][0]'") + " is null."); } try { System.out.println(a[0][0][0].length); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0].length; // a[0][0][0] is null", e.getMessage(), "Cannot read the array length because " + (hasDebugInfo ? "'a[0][0][0]'" : "'[0][0][0]'") + " is null."); } a[0][0][0] = new int[1][][]; try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a[0][0][0][0] is null", e.getMessage(), "Cannot load from object array because " + (hasDebugInfo ? "'a[0][0][0][0]'" : "'[0][0][0][0]'") + " is null."); } a[0][0][0][0] = new int[1][]; // Reaching max recursion depth. Prints . try { a[0][0][0][0][0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0][0][0][0] = 99; // a[0][0][0][0][0] is null", e.getMessage(), "Cannot store to int array because " + "'[0][0][0][0][0]' is null."); } a[0][0][0][0][0] = new int[1]; try { a[0][0][0][0][0][0] = 99; } catch (NullPointerException e) { Asserts.fail(); } } public void testPointerChasing() { A a = null; try { a.to_b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.to_b.to_c.to_d.num = 99; // a is null", e.getMessage(), "Cannot read field 'to_b' because " + (hasDebugInfo ? "'a'" : "''") + " is null."); } a = new A(); try { a.to_b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.to_b.to_c.to_d.num = 99; // a.to_b is null", e.getMessage(), "Cannot read field 'to_c' because " + (hasDebugInfo ? "'a.to_b'" : "'.to_b'") + " is null."); } a.to_b = new B(); try { a.to_b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.to_b.to_c.to_d.num = 99; // a.to_b.to_c is null", e.getMessage(), "Cannot read field 'to_d' because " + (hasDebugInfo ? "'a.to_b.to_c'" : "'.to_b.to_c'") + " is null."); } a.to_b.to_c = new C(); try { a.to_b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.to_b.to_c.to_d.num = 99; // a.to_b.to_c.to_d is null", e.getMessage(), "Cannot assign field 'num' because " + (hasDebugInfo ? "'a.to_b.to_c.to_d'" : "'.to_b.to_c.to_d'") + " is null."); } } public void testMethodChasing() { A a = null; try { a.getB().getBfromB().getC().getD().num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().num = 99; // a is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$A.getB()' because " + (hasDebugInfo ? "'a" : "'") + "' is null."); } a = new A(); try { a.getB().getBfromB().getC().getD().num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().num = 99; // a.getB() is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$B.getBfromB()' because " + "the return value of 'NullPointerExceptionTest$A.getB()' is null."); } a.to_b = new B(); try { a.getB().getBfromB().getC().getD().num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().num = 99; // a.getB().getBfromB() is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$B.getC()' because " + "the return value of 'NullPointerExceptionTest$B.getBfromB()' is null."); } a.to_b.to_b = new B(); try { a.getB().getBfromB().getC().getD().num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().num = 99; // a.getB().getBfromB().getC() is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$C.getD()' because " + "the return value of 'NullPointerExceptionTest$B.getC()' is null."); } a.to_b.to_b.to_c = new C(); try { a.getB().getBfromB().getC().getD().num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().num = 99; // a.getB().getBfromB().getC().getD() is null", e.getMessage(), "Cannot assign field 'num' because " + "the return value of 'NullPointerExceptionTest$C.getD()' is null."); } } public void testMixedChasing() { A a = null; try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$A.getB()' because " + (hasDebugInfo ? "'a'" : "''") + " is null."); } a = new A(); try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB() is null", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$B.getBfromB()' because " + "the return value of 'NullPointerExceptionTest$A.getB()' is null."); } a.to_b = new B(); try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB() is null", e.getMessage(), "Cannot read field 'to_c' because " + "the return value of 'NullPointerExceptionTest$B.getBfromB()' is null."); } a.to_b.to_b = new B(); try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c is null", e.getMessage(), "Cannot read field 'to_d' because " + "'NullPointerExceptionTest$B.getBfromB().to_c' is null."); } a.to_b.to_b.to_c = new C(); try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d is null", e.getMessage(), "Cannot read field 'ar' because " + "'NullPointerExceptionTest$B.getBfromB().to_c.to_d' is null."); } a.to_b.to_b.to_c.to_d = new D(); try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d.ar is null", e.getMessage(), "Cannot load from object array because " + "'NullPointerExceptionTest$B.getBfromB().to_c.to_d.ar' is null."); } try { a.getB().getBfromB().getC().getD().ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().ar[0][0] = 99; // a.getB().getBfromB().getC().getD().ar is null", e.getMessage(), "Cannot load from object array because " + "'NullPointerExceptionTest$C.getD().ar' is null."); } a.to_b.to_b.to_c.to_d.ar = new int[1][]; try { a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().to_c.to_d.ar[0][0] = 99; // a.getB().getBfromB().to_c.to_d.ar[0] is null", e.getMessage(), "Cannot store to int array because " + "'NullPointerExceptionTest$B.getBfromB().to_c.to_d.ar[0]' is null."); } try { a.getB().getBfromB().getC().getD().ar[0][0] = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.getB().getBfromB().getC().getD().ar[0][0] = 99; // a.getB().getBfromB().getC().getD().ar[0] is null", e.getMessage(), "Cannot store to int array because " + "'NullPointerExceptionTest$C.getD().ar[0]' is null."); } } // Helper method to cause test case. private Object returnNull(String[][] dummy1, int[][][] dummy2, float dummy3) { return null; } // Helper method to cause test case. private NullPointerExceptionTest returnMeAsNull(Throwable dummy1, int dummy2, char dummy3) { return null; } // Helper interface for test cases. static interface DoubleArrayGen { public double[] getArray(); } // Helper class for test cases. static class DoubleArrayGenImpl implements DoubleArrayGen { @Override public double[] getArray() { return null; } } // Helper class for test cases. static class NullPointerGenerator { public static Object nullReturner(boolean dummy1) { return null; } public Object returnMyNull(double dummy1, long dummy2, short dummy3) { return null; } } // Helper method to cause test case. public void ImplTestLoadedFromMethod(DoubleArrayGen gen) { try { (gen.getArray())[0] = 1.0; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "(gen.getArray())[0] = 1.0;", e.getMessage(), "Cannot store to double array because " + "the return value of 'NullPointerExceptionTest$DoubleArrayGen.getArray()' is null."); } } public void testNullEntity() { int[][] a = new int[820][]; test_iload(); test_lload(); test_fload(); // test_dload(); test_aload(); // aload_0: 'this' try { this.nullInstanceField.nullInstanceField = new NullPointerExceptionTest(); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "this.nullInstanceField.nullInstanceField = new NullPointerExceptionTest();", e.getMessage(), "Cannot assign field 'nullInstanceField' because 'this.nullInstanceField' is null."); } // aconst_null try { throw null; } catch (NullPointerException e) { checkMessage(e, "throw null;", e.getMessage(), "Cannot throw exception because 'null' is null."); } // iconst_0 try { a[0][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[0][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null."); } // iconst_1 try { a[1][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[1][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[1]'" : "'[1]'") + " is null."); } // iconst_2 try { a[2][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[2][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[2]'" : "'[2]'") + " is null."); } // iconst_3 try { a[3][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[3][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[3]'" : "'[3]'") + " is null."); } // iconst_4 try { a[4][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[4][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[4]'" : "'[4]'") + " is null."); } // iconst_5 try { a[5][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[5][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[5]'" : "'[5]'") + " is null."); } // long --> iconst try { a[(int)0L][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[(int)0L][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[0]'" : "'[0]'") + " is null."); } // bipush try { a[139 /*0x77*/][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[139][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[139]'" : "'[139]'") + " is null."); } // sipush try { a[819 /*0x333*/][0] = 77; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a[819][0] = 77;", e.getMessage(), "Cannot store to int array because " + (hasDebugInfo ? "'a[819]'" : "'[819]'") + " is null."); } // aaload, with recursive descend. testArrayChasing(); // getstatic try { boolean val = (((float[]) nullStaticField)[0] == 1.0f); Asserts.assertTrue(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "boolean val = (((float[]) nullStaticField)[0] == 1.0f);", e.getMessage(), "Cannot load from float array because 'NullPointerExceptionTest.nullStaticField' is null."); } // getfield, with recursive descend. testPointerChasing(); // invokestatic try { char val = ((char[]) NullPointerGenerator.nullReturner(false))[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "char val = ((char[]) NullPointerGenerator.nullReturner(false))[0];", e.getMessage(), "Cannot load from char array because " + "the return value of 'NullPointerExceptionTest$NullPointerGenerator.nullReturner(boolean)' is null."); } // invokevirtual try { char val = ((char[]) (new NullPointerGenerator().returnMyNull(1, 1, (short) 1)))[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "char val = ((char[]) (new NullPointerGenerator().returnMyNull(1, 1, (short) 1)))[0];", e.getMessage(), "Cannot load from char array because " + "the return value of 'NullPointerExceptionTest$NullPointerGenerator.returnMyNull(double, long, short)' is null."); } // Call with array arguments. try { double val = ((double[]) returnNull(null, null, 1f))[0]; System.out.println(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "double val = ((double[]) returnNull(null, null, 1f))[0];", e.getMessage(), "Cannot load from double array because " + "the return value of 'NullPointerExceptionTest.returnNull(String[][], int[][][], float)' is null."); } // invokeinterface ImplTestLoadedFromMethod(new DoubleArrayGenImpl()); try { returnMeAsNull(null, 1, 'A').dag = new DoubleArrayGenImpl(); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "returnMeAsNull(null, 1, 'A').dag = new DoubleArrayGenImpl();", e.getMessage(), "Cannot assign field 'dag' because " + "the return value of 'NullPointerExceptionTest.returnMeAsNull(java.lang.Throwable, int, char)' is null."); } testMethodChasing(); // Mixed recursive descend. testMixedChasing(); } // The double placeholder takes two slots on the stack. public void testParametersTestMethod(A a, double placeholder, B b, Integer i) throws Exception { try { a.to_b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "a.to_b.to_c.to_d.num = 99; // to_c is null, a is a parameter.", e.getMessage(), "Cannot read field 'to_d' because '" + (hasDebugInfo ? "a" : "") + ".to_b.to_c' is null."); } try { b.to_c.to_d.num = 99; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "b.to_c.to_d.num = 99; // b is null and b is a parameter.", e.getMessage(), "Cannot read field 'to_c' because " + // We expect number '3' for the parameter. (hasDebugInfo ? "'b'" : "''") + " is null."); } try { int my_i = i; } catch (NullPointerException e) { checkMessage(e, "int my_i = i; // i is a parameter of type Integer.", e.getMessage(), "Cannot invoke 'java.lang.Integer.intValue()' because " + (hasDebugInfo ? "'i'" : "''") + " is null."); } } public void testParameters() throws Exception { A a = new A(); a.to_b = new B(); testParametersTestMethod(a, 0.0, null, null); } public void testCreation() throws Exception { // If allocated with new, the message should not be generated. Asserts.assertNull(new NullPointerException().getMessage()); String msg = new String("A pointless message."); Asserts.assertTrue(new NullPointerException(msg).getMessage() == msg); // If created via reflection, the message should not be generated. Exception ex = NullPointerException.class.getDeclaredConstructor().newInstance(); Asserts.assertNull(ex.getMessage()); } public void testNative() throws Exception { // If NPE is thrown in a native method, the message should // not be generated. try { Class.forName(null); Asserts.fail(); } catch (NullPointerException e) { Asserts.assertNull(e.getMessage()); } } // Test we get the same message calling npe.getMessage() twice. public void testSameMessage() throws Exception { Object null_o = null; String expectedMsg = "Cannot invoke 'Object.hashCode()' because " + (hasDebugInfo ? "'null_o" : "'") + "' is null."; try { null_o.hashCode(); Asserts.fail(); } catch (NullPointerException e) { String msg1 = e.getMessage(); checkMessage(e, "null_o.hashCode()", msg1, expectedMsg); String msg2 = e.getMessage(); Asserts.assertTrue(msg1.equals(msg2)); // It was decided that getMessage should generate the // message anew on every call, so this does not hold. //Asserts.assertTrue(msg1 == msg2); Asserts.assertFalse(msg1 == msg2); } } public void testSerialization() throws Exception { // NPE without message. Object o1 = new NullPointerException(); ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); ObjectOutputStream oos1 = new ObjectOutputStream(bos1); oos1.writeObject(o1); ByteArrayInputStream bis1 = new ByteArrayInputStream(bos1.toByteArray()); ObjectInputStream ois1 = new ObjectInputStream(bis1); Exception ex1 = (Exception) ois1.readObject(); Asserts.assertNull(ex1.getMessage()); // NPE with custom message. String msg2 = "A useless message"; Object o2 = new NullPointerException(msg2); ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); ObjectOutputStream oos2 = new ObjectOutputStream(bos2); oos2.writeObject(o2); ByteArrayInputStream bis2 = new ByteArrayInputStream(bos2.toByteArray()); ObjectInputStream ois2 = new ObjectInputStream(bis2); Exception ex2 = (Exception) ois2.readObject(); Asserts.assertEquals(ex2.getMessage(), msg2); // NPE with generated message. Object null_o3 = null; Object o3 = null; String msg3 = null; try { int hc = null_o3.hashCode(); System.out.println(hc); Asserts.fail(); } catch (NullPointerException npe3) { o3 = npe3; msg3 = npe3.getMessage(); checkMessage(npe3, "int hc = null_o3.hashCode();", msg3, "Cannot invoke 'Object.hashCode()' because " + (hasDebugInfo ? "'null_o3'" : "''") + " is null."); } ByteArrayOutputStream bos3 = new ByteArrayOutputStream(); ObjectOutputStream oos3 = new ObjectOutputStream(bos3); oos3.writeObject(o3); ByteArrayInputStream bis3 = new ByteArrayInputStream(bos3.toByteArray()); ObjectInputStream ois3 = new ObjectInputStream(bis3); Exception ex3 = (Exception) ois3.readObject(); // It was decided that getMessage should not store the // message in Throwable.detailMessage or implement writeReplace(), // thus it can not be recovered by serialization. //Asserts.assertEquals(ex3.getMessage(), msg3); Asserts.assertEquals(ex3.getMessage(), null); } static int index17 = 17; int getIndex17() { return 17; }; public void testComplexMessages() { try { staticLongArray[0][0] = 2L; Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "staticLongArray[0][0] = 2L;", e.getMessage(), "Cannot store to long array because " + "'NullPointerExceptionTest.staticLongArray[0]' is null."); } try { NullPointerExceptionTest obj = this; Object val = obj.dag.getArray().clone(); Asserts.assertNull(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "Object val = obj.dag.getArray().clone();", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest$DoubleArrayGen.getArray()' because " + (hasDebugInfo ? "'obj" : "'") + ".dag' is null."); } try { int indexes[] = new int[1]; NullPointerExceptionTest[] objs = new NullPointerExceptionTest[] {this}; Object val = objs[indexes[0]].nullInstanceField.returnNull(null, null, 1f); Asserts.assertNull(val); Asserts.fail(); } catch (NullPointerException e) { checkMessage(e, "Object val = objs[indexes[0]].nullInstanceField.returnNull(null, null, 1f);", e.getMessage(), "Cannot invoke 'NullPointerExceptionTest.returnNull(String[][], int[][][], float)' because " + (hasDebugInfo ? "'objs[indexes" : "'[") + "[0]].nullInstanceField' is null."); } try { int indexes[] = new int[1]; NullPointerExceptionTest[][] objs = new NullPointerExceptionTest[][] {new NullPointerExceptionTest[] {this}}; synchronized (objs[indexes[0]][0].nullInstanceField) { Asserts.fail(); } } catch (NullPointerException e) { checkMessage(e, "synchronized (objs[indexes[0]][0].nullInstanceField) { ... }", e.getMessage(), "Cannot enter synchronized block because " + (hasDebugInfo ? "'objs[indexes" : "'[" ) + "[0]][0].nullInstanceField' is null."); } try { // If we can get the value from more than one bci, we cannot know which one // is null. Make sure we don't print the wrong value. String s = null; byte[] val = (Math.random() < 0.5 ? s : (new String[1])[0]).getBytes(); } catch (NullPointerException e) { checkMessage(e, "byte[] val = (Math.random() < 0.5 ? s : (new String[1])[0]).getBytes();", e.getMessage(), "Cannot invoke 'String.getBytes()'."); } try { // If we can get the value from more than one bci, we cannot know which one // is null. Make sure we don't print the wrong value. Also make sure if // we don't print the failed action we don't print a string at all. int[][] a = new int[1][]; int[][] b = new int[2][]; long index = 0; int val = (Math.random() < 0.5 ? a[(int)index] : b[(int)index])[13]; } catch (NullPointerException e) { checkMessage(e, "int val = (Math.random() < 0.5 ? a[(int)index] : b[(int)index])[13]", e.getMessage(), "Cannot load from int array."); } try { // If we can get the value from more than one bci, we cannot know which one // is null. Make sure we don't print the wrong value. Also make sure if // we don't print the failed action we don't print a string at all. int[][] a = new int[1][]; int[][] b = new int[2][]; long index = 0; int val = (Math.random() < 0.5 ? a : b)[(int)index][13]; } catch (NullPointerException e) { checkMessage(e, "int val = (Math.random() < 0.5 ? a : b)[(int)index][13]", e.getMessage(), "Cannot load from int array because '[...]' is null."); } try { C c1 = new C(); C c2 = new C(); (Math.random() < 0.5 ? c1 : c2).to_d.num = 77; } catch (NullPointerException e) { checkMessage(e, "(Math.random() < 0.5 ? c1 : c2).to_d.num = 77;", e.getMessage(), "Cannot assign field 'num' because 'to_d' is null."); } // Static variable as array index. try { staticLongArray[index17][0] = 2L; } catch (NullPointerException e) { checkMessage(e, "staticLongArray[index17][0] = 2L;", e.getMessage(), "Cannot store to long array because " + "'NullPointerExceptionTest.staticLongArray[NullPointerExceptionTest.index17]' is null."); } // Method call as array index. try { staticLongArray[getIndex17()][0] = 2L; } catch (NullPointerException e) { checkMessage(e, "staticLongArray[getIndex17()][0] = 2L;", e.getMessage(), "Cannot store to long array because " + "'NullPointerExceptionTest.staticLongArray[NullPointerExceptionTest.getIndex17()]' is null."); } // Unboxing. Integer a = null; try { int b = a; } catch (NullPointerException e) { checkMessage(e, "Integer a = null; int b = a;", e.getMessage(), "Cannot invoke 'java.lang.Integer.intValue()' because " + (hasDebugInfo ? "'a'" : "''") + " is null."); } // Unboxing by hand. Has the same message as above. try { int b = a.intValue(); } catch (NullPointerException e) { checkMessage(e, "Integer a = null; int b = a.intValue();", e.getMessage(), "Cannot invoke 'java.lang.Integer.intValue()' because " + (hasDebugInfo ? "'a'" : "''") + " is null."); } } // Generates: // class E implements E0 { // public int throwNPE(F f) { // return f.i; // } // } static byte[] generateTestClass() { ClassWriter cw = new ClassWriter(0); MethodVisitor mv; cw.visit(50, ACC_SUPER, "E", null, "java/lang/Object", new String[] { "E0" }); { mv = cw.visitMethod(0, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cw.visitMethod(ACC_PUBLIC, "throwNPE", "(LF;)I", null, null); mv.visitCode(); Label label0 = new Label(); mv.visitLabel(label0); mv.visitLineNumber(118, label0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(GETFIELD, "F", "i", "I"); mv.visitInsn(IRETURN); Label label1 = new Label(); mv.visitLabel(label1); mv.visitLocalVariable("this", "LE;", null, label0, label1, 0); mv.visitLocalVariable("f", "LE;", null, label0, label1, 1); mv.visitMaxs(1, 2); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } // Tests that a class generated on the fly is handled properly. public void testGeneratedCode() throws Exception { byte[] classBytes = generateTestClass(); Lookup lookup = lookup(); Class clazz = lookup.defineClass(classBytes); E0 e = (E0) clazz.getDeclaredConstructor().newInstance(); try { e.throwNPE(null); } catch (NullPointerException ex) { checkMessage(ex, "return f.i;", ex.getMessage(), "Cannot read field 'i' because 'f' is null."); } } } // Helper interface for test cases needed for generateTestClass(). interface E0 { public int throwNPE(F f); } // Helper class for test cases needed for generateTestClass(). class F { int i; }