diff -r 11a574b352d0 -r e3618c902d17 test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NullPointerException/NullPointerExceptionTest.java Mon Oct 14 11:36:17 2019 +0200 @@ -0,0 +1,1782 @@ +/* + * 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. + * @bug 8218628 + * @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. + * @bug 8218628 + * @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.lang.invoke.MethodHandles.Lookup; +import java.util.ArrayList; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.test.lib.Asserts; + +import static java.lang.invoke.MethodHandles.lookup; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; +import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; +import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; +import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; +import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; +import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1; +import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2; +import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; +import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; +import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; + +/** + * 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("\nSource code:\n " + expression + "\n\nOutput:"); + t.printStackTrace(System.out); + if (obtainedMsg != expectedMsg && // E.g. both are null. + !obtainedMsg.equals(expectedMsg)) { + 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; + } + + @SuppressWarnings("null") + 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"); + } + // invokevirtual + 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"); + } + // invokeinterface + try { + NullPointerExceptionTest obj = this; + Object val = obj.dag.getArray(); + Asserts.assertNull(val); + Asserts.fail(); + } catch (NullPointerException e) { + checkMessage(e, "Object val = obj.dag.getArray();", e.getMessage(), + "Cannot invoke \"NullPointerExceptionTest$DoubleArrayGen.getArray()\" because " + + (hasDebugInfo ? "\"obj" : "\"") + ".dag\" is null"); + } + // invokespecial + G g = null; + try { + byte[] classBytes = G.generateSub2GTestClass(); + Lookup lookup = lookup(); + Class clazz = lookup.defineClass(classBytes); + g = (G) clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + e.printStackTrace(); + Asserts.fail("Generating class Sub2G failed."); + } + try { + g.m2("Beginning"); + } catch (NullPointerException e) { + checkMessage(e, "return super.m2(x).substring(2); // ... where super is null by bytecode manipulation.", e.getMessage(), + "Cannot invoke \"G.m2(String)\" because \"null\" 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; + @SuppressWarnings("unused") + 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; + @SuppressWarnings("unused") + 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; + @SuppressWarnings("unused") + 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"); + } + } + + @SuppressWarnings("null") + static void test_aload() { + F f0 = null; + F f1 = null; + F f2 = null; + F f3 = null; + @SuppressWarnings("unused") + 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; + } + + + @SuppressWarnings("null") + 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(); + } + } + + @SuppressWarnings("null") + 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"); + } + } + + @SuppressWarnings("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"); + } + } + + @SuppressWarnings("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"); + } + // invokespecial + try { + SubG g = new SubG(); + g.m2("Beginning"); + } catch (NullPointerException e) { + checkMessage(e, "return super.m2(x).substring(2);", e.getMessage(), + "Cannot invoke \"String.substring(int)\" because " + + "the return value of \"G.m2(String)\" 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(); + } + + // Assure 64 parameters are printed as 'parameteri'. + public String manyParameters( + int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, + int i11, int i12, int i13, int i14, int i15, int i16, int i17, int i18, int i19, int i20, + int i21, int i22, int i23, int i24, int i25, int i26, int i27, int i28, int i29, int i30, + int i31, int i32, int i33, int i34, int i35, int i36, int i37, int i38, int i39, int i40, + int i41, int i42, int i43, int i44, int i45, int i46, int i47, int i48, int i49, int i50, + int i51, int i52, int i53, int i54, int i55, int i56, int i57, int i58, int i59, int i60, + int i61, int i62, int i63, int i64, int i65, int i66, int i67, int i68, int i69, int i70) { + String[][][][] ar5 = new String[1][1][1][1]; + int[][][] idx3 = new int[1][1][1]; + int[][] idx2 = new int[1][1]; + return ar5[i70] + [idx2[i65][i64]] + [idx3[i63][i62][i47]] + [idx3[idx2[i33][i32]][i31][i17]] + .substring(2); + } + + // 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 { + @SuppressWarnings("unused") + 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"); + } + + // If no debug information is available, only 64 parameters (this and i1 through i63) + // will be reported in the message as 'parameteri'. Others will be reported as 'locali'. + try { + manyParameters(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } catch (NullPointerException e) { + checkMessage(e, "return ar5[i70][idx2[i65][i64]][idx3[i63][i62][i47]][idx3[idx2[i33][i32]][i31][i17]].substring(2);", e.getMessage(), + "Cannot invoke \"String.substring(int)\" because " + + (hasDebugInfo ? + "\"ar5[i70][idx2[i65][i64]][idx3[i63][i62][i47]][idx3[idx2[i33][i32]][i31][i17]]\"" : + "\"[][[][]][[][]" + + "[]][[[][]][][]]\"") + + " 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. + @SuppressWarnings("null") + 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); + } + } + + @SuppressWarnings("null") + 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; }; + + @SuppressWarnings({ "unused", "null" }) + 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; + @SuppressWarnings("unused") + 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; + @SuppressWarnings("unused") + 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; + // } + // public void throwNPE_reuseStackSlot1(String s1) { + // System.out.println(s1.substring(1)); + // String s1_2 = null; // Reuses slot 1. + // System.out.println(s1_2.substring(1)); + // } + // public void throwNPE_reuseStackSlot4(String s1, String s2, String s3, String s4) { + // System.out.println(s4.substring(1)); + // String s4_2 = null; // Reuses slot 4. + // System.out.println(s4_2.substring(1)); + // } + // } + // + // This code was adapted from output of + // java jdk.internal.org.objectweb.asm.util.ASMifier E0.class + 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(); + } + + { + mv = cw.visitMethod(ACC_PUBLIC, "throwNPE_reuseStackSlot1", "(Ljava/lang/String;)V", null, null); + mv.visitCode(); + Label label0 = new Label(); + mv.visitLabel(label0); + mv.visitLineNumber(7, label0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ICONST_1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + mv.visitLabel(label1); + mv.visitLineNumber(8, label1); + mv.visitInsn(ACONST_NULL); + mv.visitVarInsn(ASTORE, 1); + Label label2 = new Label(); + mv.visitLabel(label2); + mv.visitLineNumber(9, label2); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ICONST_1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label3 = new Label(); + mv.visitLabel(label3); + mv.visitLineNumber(10, label3); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + + { + mv = cw.visitMethod(ACC_PUBLIC, "throwNPE_reuseStackSlot4", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", null, null); + mv.visitCode(); + Label label0 = new Label(); + mv.visitLabel(label0); + mv.visitLineNumber(12, label0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 4); + mv.visitInsn(ICONST_1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label1 = new Label(); + mv.visitLabel(label1); + mv.visitLineNumber(13, label1); + mv.visitInsn(ACONST_NULL); + mv.visitVarInsn(ASTORE, 4); + Label label2 = new Label(); + mv.visitLabel(label2); + mv.visitLineNumber(14, label2); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 4); + mv.visitInsn(ICONST_1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + Label label3 = new Label(); + mv.visitLabel(label3); + mv.visitLineNumber(15, label3); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 6); + mv.visitEnd(); + } + + cw.visitEnd(); + + return cw.toByteArray(); + } + + // Assign to a parameter. + // Without debug information, this will print "parameter1" if a NPE + // is raised in the first line because null was passed to the method. + // It will print "local1" if a NPE is raised in line three. + public void assign_to_parameter(String s1) { + System.out.println(s1.substring(1)); + s1 = null; + System.out.println(s1.substring(2)); + } + + // 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"); + } + + // Optimized bytecode can reuse local variable slots for several + // local variables. + // If there is no variable name information, we print 'parameteri' + // if a parameter maps to a local slot. Once a local slot has been + // written, we don't know any more whether it was written as the + // corresponding parameter, or whether another local has been + // mapped to the slot. So we don't want to print 'parameteri' any + // more, but 'locali'. Similary for 'this'. + + // Expect message saying "parameter0". + try { + e.throwNPE_reuseStackSlot1(null); + } catch (NullPointerException ex) { + checkMessage(ex, "s1.substring(1)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + // Expect message saying "local0". + try { + e.throwNPE_reuseStackSlot1("aa"); + } catch (NullPointerException ex) { + checkMessage(ex, "s1_2.substring(1)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + // Expect message saying "parameter4". + try { + e.throwNPE_reuseStackSlot4("aa", "bb", "cc", null); + } catch (NullPointerException ex) { + checkMessage(ex, "s4.substring(1)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + // Expect message saying "local4". + try { + e.throwNPE_reuseStackSlot4("aa", "bb", "cc", "dd"); + } catch (NullPointerException ex) { + checkMessage(ex, "s4_2.substring(1)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + + // Unfortunately, with the fix for optimized code as described above + // we don't write 'parameteri' any more after the parameter variable + // has been assigned. + + if (!hasDebugInfo) { + // Expect message saying "parameter1". + try { + assign_to_parameter(null); + } catch (NullPointerException ex) { + checkMessage(ex, "s1.substring(1)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + // The message says "local1" although "parameter1" would be correct. + try { + assign_to_parameter("aaa"); + } catch (NullPointerException ex) { + checkMessage(ex, "s1.substring(2)", + ex.getMessage(), + "Cannot invoke \"String.substring(int)\" because \"\" is null"); + } + } + } +} + +// Helper interface for test cases needed for generateTestClass(). +interface E0 { + public int throwNPE(F f); + public void throwNPE_reuseStackSlot1(String s1); + public void throwNPE_reuseStackSlot4(String s1, String s2, String s3, String s4); +} + +// Helper class for test cases needed for generateTestClass(). +class F { + int i; +} + +// For invokespecial test cases. +class G { + public String m2(String x) { + return null; + } + + // This generates the following class: + // + // class Sub2G extends G { + // public String m2(String x) { + // super = null; // Possible in raw bytecode. + // return super.m2(x).substring(2); // Uses invokespecial. + // } + // } + // + // This code was adapted from output of + // java jdk.internal.org.objectweb.asm.util.ASMifier Sub2G.class + static byte[] generateSub2GTestClass() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(50, ACC_SUPER, "Sub2G", null, "G", null); + + { + mv = cw.visitMethod(0, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "G", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "m2", "(Ljava/lang/String;)Ljava/lang/String;", null, null); + mv.visitCode(); + mv.visitInsn(ACONST_NULL); // Will cause NPE. + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESPECIAL, "G", "m2", "(Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitInsn(ICONST_2); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(I)Ljava/lang/String;", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + cw.visitEnd(); + + return cw.toByteArray(); + } +} +class SubG extends G { + public String m2(String x) { + return super.m2(x).substring(2); + } +}