8172880: Incorrect line number for NPE generated by instance-bound method reference
Summary: Synthetic null checks should have a LineNumberTable entry.
Reviewed-by: mcimadamore
--- 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);
+ }
+
+}