Bug 137496 - pointcut not recognized when using generics in interface method
Summary: pointcut not recognized when using generics in interface method
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.1   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: 1.5.2   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-04-19 10:29 EDT by Philippe Larouche CLA
Modified: 2006-05-05 06:00 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Philippe Larouche CLA 2006-04-19 10:29:33 EDT
I use AspectJ version 1.5.0.20051220093604 and I also tried it with version 1.5.120060410063641.  

When we have an interface that uses generics and that defines a method with generics as follow:

public interface ParentGeneric<ENTITY> {
	public ENTITY callParentMethod(ENTITY myEntity); 
}




If another interface extends this interface, for example:

public interface Child extends ParentGeneric<String> {
	public void callChildMethod();
}



... and we use this interface to call the parent's method, the aspectJ compiler doesn't recognize the call.  For example, assume the following method:

public static void main(String[] args) {
	Child test = (Child)new Object();		
	test.callParentMethod("myString");
}




In such an example, if I declare a pointcut to catch up the call to "callParentMethod", the compiler doesn't recognize it and no match exists for the defined advises.  In this example, the following pointcut doesn't match the call made by our main method:

pointcut parentMock() :
	call(* callParentMethod(..));



In the main method, if I use a class that implements Child and I use this class instead of a Child instance, the compiler recognize the call defined by our pointcut.  Here's an example of something that would work:

public class MyClass implements Child {
	public void callChildMethod() {}

	public String callParentMethod(String myString) {
		return null;
	}
}

public class MyMainClass {
	public static void main(String[] args) {
		MyClass test = new MyClass();
		test.callParentMethod("myString");
	}
}




Again, if instead we use the interface, the pointcut doesn't match anything.


Thanks
Phil
Comment 1 Andrew Clement CLA 2006-04-19 11:25:18 EDT
I've compressed it to one test program:

interface P<T> {
  public T pm(T t);
}

interface C extends P<String> {
  public void cm();
}

public class B {

  static class CImpl implements C {
    public void cm() {}
    public String pm(String s) { return s;}
  }

  public static void main(String []argv) {
    C test = new CImpl();
    test.pm("foo");
  }
}

aspect X {
  before(): call(* pm(..)) {}
}

Problem is looking for the first defining type for pm, we don't find anything because we are looking for 'Object pm(Object)' when it is defined as 'String pm(String)' (with a bridge method from the erased version...)

ought to fix for 1.5.2
Comment 2 Andrew Clement CLA 2006-04-21 12:12:40 EDT
Yes, the bytecode for the call to the parent method pm() looks like this:

invokeinterface #27,  2; //InterfaceMethod C.pm:(Ljava/lang/Object;)Ljava/lang/Object;

which is a call to the erasure of the method (which will delegate to the actual implementation where Object is replaced by String).

The lookup code in ResolvedType.lookupMember() never finds a method with that signature so returns null.  What it does find is the correct 'String P<String>.pm(String)' which is really what it was looking for ...

With a quick hack to get it to return this parameterized method as what it was looking for, the program works, but you get a misleading weave info message!

"Join point 'method-call(java.lang.Object C.pm(java.lang.Object))' in Type 'B' (B.java:20) advised by before advice from 'X' (B.java:26)"

which reports the join point as the call to the bridge method.


If I change the body of the main method to use the implementing class rather than the interface type:

public static void main(String []argv) {
    CImpl test = new CImpl();
    test.pm("foo");
}

then the bytecode is different:

invokevirtual   #25; //Method CImpl.pm:(Ljava/lang/String;)Ljava/lang/String;

and the pointcut matches no problem.

i'm thinking we need to realize that the parameterized method matches ... but not quite sure about that weaving message...
Comment 3 Andrew Clement CLA 2006-04-21 14:10:50 EDT
Fix committed.  Will be in the next dev build.  I added a few more test scenarios I was worried about and they seem to be working OK.  The weaving message isn't perfect but it is accurate.
Comment 4 Andrew Clement CLA 2006-04-21 18:43:56 EDT
fix available in latest AJ dev build.
Comment 5 Andrew Clement CLA 2006-04-24 13:52:08 EDT
Philippe has told me (and I've confirmed) there is a very closely related problem that occurs once this bug is fixed.  The problem is if around advice is used on a join point that matches one of these pointcuts.  Because the signature used for the extracted method containing the join point shadow is using the signature of the resolved member (String blah(String)) rather than the signature of the shadow (Object blah(Object))...
Comment 6 Andrew Clement CLA 2006-04-25 07:15:46 EDT
i've put in a fix for around advice now - should be in the next build.
Comment 7 Andrew Clement CLA 2006-05-05 06:00:58 EDT
fixed.