Summary: | [compiler] Inner class compiles but IllegalAccessError if splitted with two output folders | ||
---|---|---|---|
Product: | [Eclipse Project] JDT | Reporter: | Thierry Herrmann <thierry.herrmann> |
Component: | Core | Assignee: | Philipe Mulet <philippe_mulet> |
Status: | VERIFIED FIXED | QA Contact: | |
Severity: | major | ||
Priority: | P3 | CC: | eclipse, markus.kell.r |
Version: | 3.2 | ||
Target Milestone: | 3.5 M3 | ||
Hardware: | PC | ||
OS: | Windows XP | ||
Whiteboard: | |||
Bug Depends on: | |||
Bug Blocks: | 158606 |
Description
Thierry Herrmann
2006-02-19 19:02:37 EST
The difference between our behavior and javac is that the access method on C doesn't use the same invocation for the outerMethod() call. They do the call using C as the declaring class and we do it using A. I found something quite interesting. If you move the classes from the two output folders into the same directory and you run from there, it works fine. We generate exactly the same bytecodes in both cases. The VM should behave the same. Might be a VM bug. Changing the class A to be public is fixing the issue as well. Our access method looks like this: // Method descriptor #33 (Lpackage2/C;)V // Stack: 1, Locals: 1 static synthetic void access$0(package2.C arg0); 0 aload_0 1 invokevirtual package1.A.foo() : void [35] 4 return Line numbers: [pc: 0, line: 1] javac access method looks like that: // Method descriptor #23 (Lpackage2/C;)V // Stack: 1, Locals: 1 static synthetic void access$100(package2.C arg0); 0 aload_0 1 invokevirtual package2.C.foo() : void [1] 4 return Line numbers: [pc: 0, line: 5] Note the different declaring class. Changing this should be enough to fix this issue. Nice work Olivier!
Yes, I also found out that moving the classes in the same package solves the problem! Interesting indeed.
I also found that making A public solves the problem. This is how I'm temporarily working with Eclipse. In our project, I've privately checked-out several classes as a workaround to this problem. But I can't work that way in the long term since everyone don't use Eclipse in my team and my co-workers are reluctant to make A public since this would violate encapsulation and since having A package private is perfectly valid.
When you write:
> Note the different declaring class. Changing this should be enough to fix this
> issue.
does it mean that the Eclipse team will change the generated bytecode to use C as the declaring class, as does javac ?
Overall, I think IllegalAccessError shoudn't occur if the compiler said it is OK. I think they should occur only when trying to violate access rules with reflection or if the bytecode was changed or if some code hasn't been recompiled...etc...
Thanks again for your fantastic work.
We now have the same problem in HEAD of eclipse with the following mapping: package1.A => org.eclipse.jface.viewers.BaseLabelProvider package1.B => org.eclipse.jface.viewers.LabelProvider package2.C => org.eclipse.jdt.ui.examples.JavaElementLightweightDecorator (in org.eclipse.jdt.ui.tests plug-in) As I see it, this is a VM bug, since JLS3 6.6.2.1 explicitly allows this situation. Reproduced with Sun jdk1.6.0_10-beta. Reproduced with IBM jre 1.6.0_07-b06 I believe this is our bug, we should not reference a non accessible type in our bytecode. The fact it works at times feel like unspecified behavior from the VM. Added regression test: InnerEmulationTest#test156 Fix got integrated with fix for bug 247292 (prereq). Once method and declaringClass can be disconnected, the fix is to use the access method declaring class instead of the target method declaring class. See CodeStream#generateSyntheticMethodAccess(...) invoke(Opcodes.OPC_invokevirtual, targetMethod, accessMethod.declaringClass); // target method declaring class may not be accessible (128563) Released for 3.5M3. Fixed Note that similar problem also exists with field access, the fix for this variation will be integrated into fix for bug 247612 (which provides the necessary infrastructure for an easy fix). e.g. package1/A.java [ package package1; abstract class A { protected int outerField; { } } ] package1/B.java [ package package1; public class B extends A { } ] package2/C.java [ package package2; import package1.B; public class C extends B { private final MyInner myInner = new MyInner(); private class MyInner { public void innerMethod() { int j = C.this.outerField; } } public static void main(String[] args) { final C c = new C(); c.myInner.innerMethod(); } } Added regression test: InnerEmulationTest#test157 Also added: InnerEmulationTest#test158-160 Tests InnerEmulationTest#test157-160 are actually attached to bug 249107 where comment 10 got forked into Also added InnerEmulationTest#test165 Verified for 3.5M3 using I20081026-2000 build. |