Bug 288658 - [compiler][1.5] Annotations visibility issues
Summary: [compiler][1.5] Annotations visibility issues
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.6   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.6 M7   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-09-04 11:26 EDT by akrieg CLA
Modified: 2013-02-16 04:47 EST (History)
5 users (show)

See Also:


Attachments
Test project (3.27 KB, application/x-zip-compressed)
2009-09-05 02:27 EDT, Walter Harley CLA
no flags Details
Test project v2 (3.89 KB, application/x-zip-compressed)
2009-09-05 02:35 EDT, Walter Harley CLA
no flags Details
Patch under consideration (12.16 KB, patch)
2010-03-17 06:40 EDT, Srikanth Sankaran CLA
no flags Details | Diff
Revised patch under test (12.06 KB, patch)
2010-03-23 07:51 EDT, Srikanth Sankaran CLA
no flags Details | Diff
Final patch (11.52 KB, patch)
2010-03-23 10:34 EDT, Srikanth Sankaran CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description akrieg CLA 2009-09-04 11:26:49 EDT
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
Comment 1 Walter Harley CLA 2009-09-05 02:27:54 EDT
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.
Comment 2 Walter Harley CLA 2009-09-05 02:35:58 EDT
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.
Comment 3 Olivier Thomann CLA 2009-09-09 12:43:22 EDT
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?
Comment 4 Kent Johnson CLA 2009-09-21 14:02:14 EDT
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.
Comment 5 Kent Johnson CLA 2009-09-22 10:58:24 EDT
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.
Comment 6 Srikanth Sankaran CLA 2010-03-17 01:06:48 EDT
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 {

}
Comment 7 Srikanth Sankaran CLA 2010-03-17 01:25:53 EDT
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 {

}
Comment 8 Srikanth Sankaran CLA 2010-03-17 01:53:55 EDT
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.
Comment 9 Srikanth Sankaran CLA 2010-03-17 06:40:16 EDT
Created attachment 162259 [details]
Patch under consideration
Comment 10 Srikanth Sankaran CLA 2010-03-23 07:51:56 EDT
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.
Comment 11 Srikanth Sankaran CLA 2010-03-23 08:22:19 EDT
See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=126712
Comment 12 Srikanth Sankaran CLA 2010-03-23 10:34:39 EDT
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()
Comment 13 Srikanth Sankaran CLA 2010-03-23 10:35:36 EDT
Satyam, can you please review ? Thanks!
Comment 14 Satyam Kandula CLA 2010-03-24 08:03:05 EDT
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!
Comment 15 Satyam Kandula CLA 2010-03-24 08:04:39 EDT
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!
Comment 16 Satyam Kandula CLA 2010-03-24 23:53:59 EDT
Apart from that, the changes look good to me.
Comment 17 Srikanth Sankaran CLA 2010-03-25 01:46:24 EDT
(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
Comment 18 Srikanth Sankaran CLA 2010-03-25 01:47:20 EDT
(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 ...
Comment 19 Olivier Thomann CLA 2010-04-26 14:35:14 EDT
Verified for 3.6M7 using I20100425-2000
Comment 20 Stephan Herrmann CLA 2013-02-16 04:47:19 EST
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.