Community
Participate
Working Groups
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729) Build Identifier: 20090619-0625 Eclipse deviates from javac in dealing with annotation visibility. In javac, when an annotation is defined on a non public class that is inherited by a public class, the annotation will not be visible on the public class's method outside of the package. In Eclipse, the annotation IS visible outside the package. Reproducible: Always Steps to Reproduce: 1. Define a package default super class Super.java with an annotated method public void setFoo(String foo) in a separate package 2. In the same package, define a public subclass Sub.java that extends the superclass. 3. In a separate package, define a test case that does Sub.class.getMethod("setFoo",String.class); The method will have the annotation. If you run this through ant, it will not have the annotation
Created attachment 146547 [details] Test project I've attached a test project representing what I think you're describing. Please take a look and see if I've correctly understood you; if not, please attach a corrected test project. With this test project, I get the same results whether I compile and run p2.Test from Eclipse or via javac and java at the command line; in both cases, the annotation is found. I am using Java version 1.6.0_05, running on Windows XP SP3. I used the @Deprecated annotation, which is @Retention(RetentionPolicy.RUNTIME); if you use an annotation with a different retention, or a different JDK, I could imagine getting different results, so please specify what you're using.
Created attachment 146548 [details] Test project v2 Sorry, my mistake, bug in my project. With the updated project I can reproduce this. When compiled and run with javac and java, if the annotation has RUNTIME retention policy, java says the annotation is not found; Eclipse says that it is found. The difference is in the compiler; the same JVM is being used in either case, and anyway if I use java at the command line against the Eclipse-compiled classes the behavior is the same as if I run within Eclipse. I am not sure what the right answer is (i.e. whether the bug is in Java or Eclipse); I'll reassign this to the JDT Core folks for investigation.
javac adds a bridge method for setFoo in p1.Sub. This would explain the visibility issue that you are seeing. The bridge method doesn't have any annotation. Kent, could you please investigate?
Actually this is a side-effect of javac adding a bridge method when a non-public superclass defines a public method (no annotation is required). So the annotation is 'lost' since the subclass answers its bridge method & not its superclass' annotated method. What is strange is the Sun VM does not need the subclass to have its own bridge method, it will walk the hierarchy & execute the superclass' annotated method.
Also note that javac 1.5 did NOT add the bridge method on the subclass. This was a change made in 1.6 and only affects inherited public methods, and not inherited protected methods.
Simplified test case suitable for cut & paste into package explorer: import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; public class Test { public static void main(String[] args) { try { Method m = Sub.class.getMethod("setFoo", String.class); Annotation a = m.getAnnotation(Anno.class); System.out.println("Annotation was " + (a == null ? "not " : "") + "found"); } catch (Exception e) { e.printStackTrace(); } } } class Super { @Anno public void setFoo(String foo) {} } class Sub extends Super { } @Retention(RetentionPolicy.RUNTIME) @interface Anno { }
Test case in comment#6 is wrong, It misses out on some of the nuance mentioned in comment#0. Use this instead to reproduce the problem on HEAD (javac5 matches eclipse, 6,7 generate the bridge) import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; public class Test extends Super { public static void main(String[] args) { try { Method m = Test.class.getMethod("setFoo", String.class); Annotation a = m.getAnnotation(Anno.class); System.out.println("Annotation was " + (a == null ? "not " : "") + "found"); } catch (Exception e) { e.printStackTrace(); } } } class Super { @Anno public void setFoo(String foo) {} } @Retention(RetentionPolicy.RUNTIME) @interface Anno { }
Please see: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342411 which started introducing the bridge methods from javac6. When test case attached to comment#2 is modified to invoke the public method off of Sub as in: package p2; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import a.Anno; import p1.Sub; public class Test { public static void main(String[] args) { try { Method m = Sub.class.getMethod("setFoo", String.class); m.invoke(new Sub(), new Object[] { "Blah" }); Annotation a = m.getAnnotation(Anno.class); System.out.println("Annotation was " + (a == null ? "not " : "") + "found"); } catch (Exception e) { e.printStackTrace(); } } } I get java.lang.IllegalAccessException: Class p2.Test can not access a member of class p1.Super with modifiers "public" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at p2.Test.main(Test.java:14) So, in order to be compatible with javac6,7 eclipse needs to generate a bridge method too. Under development.
Created attachment 162259 [details] Patch under consideration
Created attachment 162761 [details] Revised patch under test This patch fixes a problem with the earlier patch having to do with inappropriate invokevirtual byte code being generated where invokespecial should have been generated instead.
See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=126712
Created attachment 162769 [details] Final patch Revised patch that introduces a bridge method only at 1.6 or above so as to be compatible with javac. All JDT core tests pass with this patch. New tests added via: org.eclipse.jdt.core.tests.compiler.regression.MethodVerifyTest.test208() org.eclipse.jdt.core.tests.compiler.regression.MethodVerifyTest.test208a()
Satyam, can you please review ? Thanks!
Srikanth, Can you look at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6888164? This bug is not fixed but talks about the side effect of fixing http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342411. This bug seems to be more like 6888164!
Apart from that, the changes look good to me.
(In reply to comment #15) > Srikanth, > Can you look at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6888164? > This bug is not fixed but talks about the side effect of fixing > http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6342411. > This bug seems to be more like 6888164! Thanks for bringing this to my attention. I failed to see the related defects listed there. For the amount I'll release the fix as it is so we are compatible with javac 6&7 (and since there is a fairly straightforward work around to the open javac issue). We will watch how things evolve and react as needed. Released in HEAD for 3.6M7
(In reply to comment #17) > (In reply to comment #15) > For the amount I'll release the fix as it is so we are compatible I meant to say for the moment ...
Verified for 3.6M7 using I20100425-2000
While working on bug 400710 I found a relation to this bug. As of today two javac bugs in this area are still unresolved: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6888164 Bridge methods break obtaining of declaring class annotations http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6924232 (reflect) IllegalAccessException on public final methods defined in local class The latter states that no super access is generated for a final method, while interestingly the same has been fixed for normal bridge methods: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6245699 Apparently, no action is required on our side.