# HG changeset patch # User jlahoda # Date 1496235062 -7200 # Node ID 170696ddb37775108ad1b5cd0e88af7e232ce5b7 # Parent 8e00390dfbb4a77fb148e04300fb36a611db9224 8172880: Incorrect line number for NPE generated by instance-bound method reference Summary: Synthetic null checks should have a LineNumberTable entry. Reviewed-by: mcimadamore diff -r 8e00390dfbb4 -r 170696ddb377 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.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()); diff -r 8e00390dfbb4 -r 170696ddb377 langtools/test/tools/javac/linenumbers/NullCheckLineNumberTest.java --- /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 actualEntries = findEntries(); + List 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 findEntries() throws IOException, ConstantPoolException { + ClassFile self = ClassFile.read(NullCheckLineNumberTest.Test.class.getResourceAsStream("NullCheckLineNumberTest$Test.class")); + for (Method m : self.methods) { + if ("".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); + } + +}