JDK-6684387 : IllegalAccessError for code passed by compiler
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Future Project
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-04-04
  • Updated: 2011-09-20
  • Resolved: 2010-07-29
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
When an abstract class calls a method that is specified in an interface and implemented by a subclass, and a private method with the same signature exists in a superclass, the code compiles fine but an IllegalAccessException is thrown at runtime. From the JLS, it appears that any "not inherited" method should not have any effect on method resolution at all. It seems that the subclass method should be called in this case, but I may be missing something.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile code. Run.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect "2" to be printed to standard out.
ACTUAL -
An IllegalAccessException is thrown.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IllegalAccessError: tried to access method A1.getI()I from class A2
	at A2.test(AccessBug.java:19)
	at AccessBug.main(AccessBug.java:3)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class AccessBug {
   public static void main(String[] args) {
      new A3().test();
   }
}
   
class A1 {
   private int getI() {
      return 1;
   }
}
   
interface I1 {
    int getI();
}
   
abstract class A2 extends A1 implements I1 {
   public void test() {
      System.out.println(getI());
   }
}
   
class A3 extends A2 {
   public int getI() {
      return 2;
   }
}
---------- END SOURCE ----------

Comments
EVALUATION This bug comes from a discrepancy between JLS/JVMS, as described in 6691741. In particular, the compiler emit an invokevirtual opcode for call to getI() inside A2.test(). This could be seen as worng, as A2 doesn't supply any implementation for getI() (note that A1.getI() is not inherited from A2 since it's declared as private in A1). When the JVM executes this code, it has to resolve A2.getI(). JVMS 5.4.3.3 (method resolution) should be applied (since this is an invokevirtual): "When resolving a method reference: 1. Method resolution checks whether C is a class or an interface. * If C is an interface, method resolution throws an IncompatibleClassChangeError. 2. Method resolution attempts to look up the referenced method in C and its superclasses: * If C declares a method with the name and descriptor specified by the method reference, method lookup succeeds. * Otherwise, if C has a superclass, step 2 of method lookup is recursively invoked on the direct superclass of C. 3. Otherwise, method lookup attempts to locate the referenced method in any of the superinterfaces of the specified class C. * If any superinterface of C declares a method with the name and descriptor specified by the method reference, method lookup succeeds. * Otherwise, method lookup fails. If method lookup fails, method resolution throws a NoSuchMethodError. If method lookup succeeds and the method is abstract, but C is not abstract, method resolution throws an AbstractMethodError. Otherwise, if the referenced method is not accessible (��5.4.4) to D, method resolution throws an IllegalAccessError." It can be seen that the JVM first searchs into superclasses and, *if nothing is found* then it searches into superinterfaces. In this case, when the JVM looks for a suitable implementation for getI() in A2' superclass (A1), an inaccessible method is resolved (A1.getI()), causing the IllegalAccessError to be thrown.
22-04-2008