JDK-8173373 : C1: NPE is thrown instead of LinkageError when accessing inaccessible field on NULL receiver
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 8,9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2017-01-25
  • Updated: 2017-11-29
  • Resolved: 2017-01-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10 JDK 8 JDK 9
10Fixed 8u152Fixed 9 b157Fixed
If a field access that always throws NPE, e.g. ((Foo)null).f, is compiled by HotSpot then NPE is thrown regardless of whether Foo.f can be resolved or not.
This differs from the behavior of the interpreter which always performs resolution before the null-check.

public class TestGet version 52:0 {
    public static Method main:"([Ljava/lang/String;)V" stack 5 locals 1 {
        getfield Field T.f:I; // T does not exist
$ java -cp out/ TestGet
Exception in thread "main" java.lang.NoClassDefFoundError: T
        at TestGet.main(TestGet.jasm)
Caused by: java.lang.ClassNotFoundException: T
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:532)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:186)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:473)
        ... 1 more

$ java -cp out/ -Xcomp TestGet
Exception in thread "main" java.lang.NullPointerException
        at TestGet.main(TestGet.jasm)

Fix verified by regression test.

[~afomin], yes, this most likely even affects JDK 7. I just added the 8-bp label because this problem violates the Java specification and affects JDK 8 as well.

This is not a recent regression for 8uX. It's reproducible with 8u111. Not a tck-red for 8uX, I guess. We just don't have a such tests in JCK 8b

An unresolved field access on a null receiver throws a NPE instead of a NoClassDefFoundError if the method is compiled by C1. The problem is that C1 inserts the null check before the field access that gets patched at runtime. The PatchingStub that would throw a NoClassDefFoundError is only executed after the null check: 0x00007efc985eb2ec: movabs $0x0,%rsi ; {oop(NULL)} 0x00007efc985eb2f6: cmp (%rsi),%rax ; implicit exception: dispatches to 0x00007efc985eb312 [some nops] 0x00007efc985eb300: jmpq 0x00007efc985eb415 ; implicit exception: dispatches to 0x00007efc985eb38e 0x00007efc985eb305: nop ;*getfield f {reexecute=0 rethrow=0 return_oop=0} ; - compiler.c1.TestUnresolvedField::test@1 The jmpq jumps to the PatchingStub and is patched at runtime to a mov (see Runtime1::patch_code) with the correct field offset. I think there are multiple ways to fix this: 1) We could omit the explicit null check when the receiver is unresolved at compile time and only insert the check after patching. This is complex and would require lots of changes to the patching code. 2) We could deoptimize if the class is not loaded and the receiver is null by setting CodeEmitInfo::_deoptimize_on_exception = true for the load: http://cr.openjdk.java.net/~thartmann/8173373/webrev.00/ This has the disadvantage that we call Runtime1::predicate_failed_trap() which sets the nmethod to non-entrant. 3) We could emit an explicit null check if the receiver is not resolved at compile time and call the deoptimize stub with Action_none if the check fails: http://cr.openjdk.java.net/~thartmann/8173373/webrev.01/ This way we don't make the nmethod non-entrant but have an explicit instead of an implicit null check.

Affects 8u as well.

ILW = TCK failure, 44 tests, disable C1 = HMM = P2

This issue is a carbon copy of JDK-8166302, but about fields rather than methods.