Bug 167775

Summary: Annotation pointcut fails with supertype reference
Product: [Tools] AspectJ Reporter: Karl Schaefer <schaefkg>
Component: CompilerAssignee: aspectj inbox <aspectj-inbox>
Status: RESOLVED INVALID QA Contact:
Severity: normal    
Priority: P3 CC: aclement
Version: 1.5.3RC1   
Target Milestone: 1.6.1   
Hardware: PC   
OS: Windows XP   
Whiteboard:
Attachments:
Description Flags
Annotation based aspect.
none
The sample annotation.
none
A simple class.
none
Baz' subclass.
none
Example test demonstrating the reference problem
none
Complete Test Case none

Description Karl Schaefer CLA 2006-12-12 20:39:22 EST
If you create an object that is a subclass of interface implementation and reference it via the superclass or interface the compiler does not recognize the poincut on the child/implementation.

In the attachment set:
Foo is the aspect that defines a pointcut on the annotation Bar.
Bar is a method-based annotation.
Baz is a simple class.
ChildOfBaz extends Baz, overriding Baz' only method execute and annotating it with Bar.
Test runs some execute methods.

Test produces the following output:
In Baz.
In ChildOfBaz
call(void ChildOfBaz.execute())
In ChildOfBaz
Comment 1 Karl Schaefer CLA 2006-12-12 20:40:11 EST
Created attachment 55543 [details]
Annotation based aspect.
Comment 2 Karl Schaefer CLA 2006-12-12 20:40:28 EST
Created attachment 55544 [details]
The sample annotation.
Comment 3 Karl Schaefer CLA 2006-12-12 20:40:50 EST
Created attachment 55545 [details]
A simple class.
Comment 4 Karl Schaefer CLA 2006-12-12 20:42:03 EST
Created attachment 55546 [details]
Baz' subclass.
Comment 5 Karl Schaefer CLA 2006-12-12 20:43:50 EST
Created attachment 55547 [details]
Example test demonstrating the reference problem
Comment 6 Karl Schaefer CLA 2006-12-12 23:46:14 EST
Created attachment 55561 [details]
Complete Test Case
Comment 7 Karl Schaefer CLA 2006-12-12 23:47:43 EST
Created an all-in-one test case and added some better output.  Test case now returns:

false
Baz
true
ChildOfBaz
call(void ChildOfBaz.execute())
true
ChildOfBaz
Comment 8 Andrew Clement CLA 2006-12-13 03:29:03 EST
the program is working as designed.  In your example the 'baz' variable is of type 'Baz'.  The pointcut is matching based on the static type information available at the call join points.  You make 3 calls to execute - only at the last one, after the cast, do we statically know that you are calling execute on the subclass, hence it matches.  Your second call to execute is identified as a call to Baz.execute() because that is the type of the variable 'baz'.  I know the subclass implementation runs because the runtime type held in baz is ChildOfBaz, but at compile time we don't know that and it isn't used in the matching process.
Comment 9 Karl Schaefer CLA 2006-12-13 08:24:29 EST
OK.  Expected for you, not for me.  I guess I had some faulty assumptions about how the compiling was done.

Here's the problem perhaps you can provide a solution.  I have a Command interface with an execute method.  I have many commands that implement this interface and I have an annotation for security checks.  I want to not have to annotate the interface execute method since some commands should just work, and even if I wanted to, I'm having difficulty imagining the correct "generic" annotation.  However, I typically pass these back and forth as Command instances, so how do I get this to work as I expected it to (ie supertype references still allow subtype annotation pointcuts to work)?
Comment 10 Andrew Clement CLA 2008-06-11 17:28:37 EDT
Better late than never....

If you want to match based on runtime information, then you need to use a runtime matching pointcut, like @annotation().  Here is a sample program where the @Secured execute method is checked.

import java.lang.annotation.*;

public class Secure {
  public static void main(String []argv) {
    new SecureCommand().execute();
    new NormalCommand().execute();
  }
}


interface ICommand {
  void execute();
}


class SecureCommand implements ICommand {
  @Secured
  public void execute() {System.out.println("Secure command running"); }
}

class NormalCommand implements ICommand {
  public void execute() {System.out.println("Normal command running"); }
}

@Retention(RetentionPolicy.RUNTIME)
@interface Secured {}

aspect X {
  before(): execution(* ICommand.execute(..)) && @annotation(Secured) {
    System.out.println("Running security check");
  }
}

However, i'm not sure how that would work in the face of your super calls...  I doubt you still care after all this time, but I thought I'd comment before I close it :)  Let me know if you still want to discuss by reopening this.