8172880: Incorrect line number for NPE generated by instance-bound method reference
authorjlahoda
Wed, 31 May 2017 14:51:02 +0200
changeset 45316 170696ddb377
parent 45315 8e00390dfbb4
child 45318 50e95c11aa99
8172880: Incorrect line number for NPE generated by instance-bound method reference Summary: Synthetic null checks should have a LineNumberTable entry. Reviewed-by: mcimadamore
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java
langtools/test/tools/javac/linenumbers/NullCheckLineNumberTest.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Fri May 26 01:00:44 2017 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Wed May 31 14:51:02 2017 +0200
@@ -1894,7 +1894,7 @@
             case NULLCHK:
                 result = od.load();
                 code.emitop0(dup);
-                genNullCheck(tree.pos());
+                genNullCheck(tree);
                 break;
             default:
                 Assert.error();
@@ -1903,12 +1903,13 @@
     }
 
     /** Generate a null check from the object value at stack top. */
-    private void genNullCheck(DiagnosticPosition pos) {
+    private void genNullCheck(JCTree tree) {
+        code.statBegin(tree.pos);
         if (allowBetterNullChecks) {
-            callMethod(pos, syms.objectsType, names.requireNonNull,
+            callMethod(tree.pos(), syms.objectsType, names.requireNonNull,
                     List.of(syms.objectType), true);
         } else {
-            callMethod(pos, syms.objectType, names.getClass,
+            callMethod(tree.pos(), syms.objectType, names.getClass,
                     List.nil(), false);
         }
         code.emitop0(pop);
@@ -2087,7 +2088,7 @@
                 base.drop();
             } else {
                 base.load();
-                genNullCheck(tree.selected.pos());
+                genNullCheck(tree.selected);
             }
             result = items.
                 makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/linenumbers/NullCheckLineNumberTest.java	Wed May 31 14:51:02 2017 +0200
@@ -0,0 +1,87 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8172880
+ * @summary  Wrong LineNumberTable for synthetic null checks
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ */
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.LineNumberTable_attribute;
+
+import java.io.IOException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class NullCheckLineNumberTest {
+
+    //test data:
+    static class Test {
+
+        public Test() {
+            String a = "", b = null;
+
+            Stream.of("x")
+                  .filter(a::equals)
+                  .filter(b::equals)
+                  .count();
+        }
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        List<Entry> actualEntries = findEntries();
+        List<Entry> expectedEntries = List.of(
+                new SimpleEntry<>(29, 0),
+                new SimpleEntry<>(30, 4),
+                new SimpleEntry<>(32, 9),
+                new SimpleEntry<>(33, 16),
+                new SimpleEntry<>(34, 32),
+                new SimpleEntry<>(35, 46),
+                new SimpleEntry<>(36, 52)
+        );
+        if (!Objects.equals(actualEntries, expectedEntries)) {
+            error(String.format("Unexpected LineNumberTable: %s", actualEntries.toString()));
+        }
+
+        try {
+            new Test();
+        } catch (NullPointerException npe) {
+            if (Arrays.stream(npe.getStackTrace())
+                      .noneMatch(se -> se.getFileName().contains("NullCheckLineNumberTest") &&
+                                       se.getLineNumber() == 34)) {
+                throw new AssertionError("Should go through line 34!");
+            }
+        }
+    }
+
+    static List<Entry> findEntries() throws IOException, ConstantPoolException {
+        ClassFile self = ClassFile.read(NullCheckLineNumberTest.Test.class.getResourceAsStream("NullCheckLineNumberTest$Test.class"));
+        for (Method m : self.methods) {
+            if ("<init>".equals(m.getName(self.constant_pool))) {
+                Code_attribute code_attribute = (Code_attribute)m.attributes.get(Attribute.Code);
+                for (Attribute at : code_attribute.attributes) {
+                    if (Attribute.LineNumberTable.equals(at.getName(self.constant_pool))) {
+                        return Arrays.stream(((LineNumberTable_attribute)at).line_number_table)
+                                     .map(e -> new SimpleEntry<> (e.line_number, e.start_pc))
+                                     .collect(Collectors.toList());
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    static void error(String msg) {
+        throw new AssertionError(msg);
+    }
+
+}