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 aroundJDK-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 aroundJDK-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)
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
The issue I found occurs when a bridge method candidate qualifies as a hypothetical bridge *and* is necessary to work around
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)
- duplicates
-
JDK-8203488 Remove error generation from TransTypes
- Resolved
-
JDK-8216196 No visibility bridges are created for generic methods
- Closed
- relates to
-
JDK-8132776 Redundant generic bridges are generated in some cases
- Open
-
JDK-8216196 No visibility bridges are created for generic methods
- Closed