JDK-8133247 : Use of HYPOTHETICAL flag in bridge generation logic leads to missing bridges in some cases
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7u85,8,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • Submitted: 2015-08-10
  • Updated: 2019-06-10
  • Resolved: 2018-08-06
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 11
11 b16Resolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
 think I found a bug in the interaction between 'hypothetical' bridge methods and the workaround for JDK-6342411.

A hypothetical bridge method "is not strictly necessary in the binary, but is represented in the symbol table to detect erasure clashes." [1]

Bridges are required to work around JDK-6342411 when a non-public super class declares a public method that's inherited into a public derived class. Without the bridge, reflectively invoking the method on the derived class fails with `IllegalAccessException: can not access a member with modifiers "public"`. [2][3]

The issue I found occurs when a bridge method candidate qualifies as a hypothetical bridge *and* is necessary to work around JDK-6342411. The path for hypothetical bridges wins, so the bridge is entered in the symbol table but not generated in the binary, and attempts to invoke it reflectively fail.

Attached is a patch with a regression test and a possible fix.

Here's the repro:

=== ./p/I.java ===
package p;

public interface I<T> {
  void f(T t);
}
=== ./p/B.java ===
package p;

public class B extends A<String> {}
=== ./p/A.java ===
package p;

class A<T> implements I<T> {
  public void f(T t) {}
}
=== ./Test.java ===
import java.lang.reflect.Method;

public class Test {
  public static void main(String[] args) throws Exception {
    Method m = Class.forName("p.B").getMethod("f", Object.class);
    m.invoke(new p.B(), new Object[]{null});
  }
}
===

$ javac p/I.java p/A.java p/B.java Test.java
$ java Test
Exception in thread "main" java.lang.IllegalAccessException: Class Test can not access a member of class p.A with modifiers "public"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:101)
        at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:295)
        at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:287)
        at java.lang.reflect.Method.invoke(Method.java:476)
        at Test.main(Test.java:6)
Comments
This was fixed by the changes in JDK-8203488, including adding a regression test for this issue.
06-08-2018