Summary: | [annotations][itds] declare @method is broken for call() | ||
---|---|---|---|
Product: | [Tools] AspectJ | Reporter: | Andrew Clement <aclement> |
Component: | Compiler | Assignee: | aspectj inbox <aspectj-inbox> |
Status: | NEW --- | QA Contact: | |
Severity: | normal | ||
Priority: | P3 | CC: | aclement |
Version: | DEVELOPMENT | ||
Target Milestone: | --- | ||
Hardware: | PC | ||
OS: | Windows XP | ||
Whiteboard: |
Description
Andrew Clement
2006-04-10 09:19:05 EDT
Initial triage... Complete program: ===========8<============== import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @interface Ann {String value();} aspect Aspect { // Methods with out the Ann annotation but in an Ann annotated type get Ann declare @method: !@Ann * (@Ann *).*(..) : @Ann("introduced"); // Call to an annotated method pointcut annotated(Ann b) : call(@Ann * *(..)) && @annotation(b); // Top level call to an annotated method pointcut annotatedTop(Ann b) : annotated(b) && !cflowbelow(annotated(Ann)); // Non top level call pointcut annotatedNotTop(Ann b, Ann bTopo) : annotated(b) && cflowbelow(annotatedTop(bTopo)); before(Ann b, Ann bTopo) : annotatedNotTop(b, bTopo) { System.out.println("Non-top:"); System.out.println("\tJoin point: " + thisJoinPointStaticPart); System.out.println("\tEnclosing join point: " + thisEnclosingJoinPointStaticPart); System.out.println("\tAnn: " + b); System.out.println("\tTop annotation: " + bTopo); } before(Ann b) : annotatedTop(b) { System.out.println("Top:"); System.out.println("\tJoin point: " + thisJoinPointStaticPart); System.out.println("\tEnclosing join point: " + thisEnclosingJoinPointStaticPart); System.out.println("\tAnn: " + b); } } public class A { @Ann("A.foo") void foo() { new B().foo(); } public static void main(String[] args) { new A().foo(); } } @Ann("B") class B { // The Ann is injected here! void foo() { } } ===========8<============== Problem is: if method foo in type B is annotated directly, rather than through dec @method then the program when executed shows two matches: Top: Join point: call(void A.foo()) Enclosing join point: execution(void A.main(String[])) Ann: @Ann(value=A.foo) Non-top: Join point: call(void B.foo()) Enclosing join point: execution(void A.foo()) Ann: @Ann(value=foo) Top annotation: @Ann(value=A.foo) If the program is compiled adding the annotation via dec @method then you only see one: Top: Join point: call(void A.foo()) Enclosing join point: execution(void A.main(String[])) Ann: @Ann(value=A.foo) ok, I have created proper failing testcases with a much simplied set of source files (committed into CVS). The problem is an ordering problem, suppose you are weaving advice into class A depending on whether it makes calls to annotated methods in B. Suppose you have an aspect X that applies annotations to methods in B. If you compile them in the order: X,B,A then it all works, the declare @method/field are applied to B before weaving A and so you match as expected. If you compile them in the order: X,A,B then by the time you are weaving A we haven't yet added the annotations to the methods in B and your advice doesn't match. Unlike other ordering problems, this can't be solved by ensuring some hierarchy is processed top-down - since these types are unrelated in that sense. The problem arises because declare @method/ctor/field are treated as 'special' - kind of semi type/shadow mungers. If they were proper type mungers they'd be getting done up front when the type hierarchy is fixed up. If they were shadow mungers then they'd not have an affect on pointcut matching. This seems to reveal a case where they need to be treated as full type mungers and done up front. re-investigate for 1.5.4 but might get bumped to 1.6.0 when sized unsetting the target field which is currently set for something already released |