Community
Participate
Working Groups
Pointcuts should match call joint points in advice. However AspectJ does not match calls to _private_ methods in around advice. The same applies to get/set pointcuts for _private fields. This is probably because in the testcase below a call to "privateMethod()" in the body of the around advice is a call to an accessor method: ajc$inlineAccessMethod$bug_nnnn_JoinPointInAroundAdvice$bug_nnnn_JoinPointInAro undAdvice$privateMethod("around"); public aspect JoinPointInAroundAdvice { private static Set privateCalls = new HashSet(); private static Set publicCalls = new HashSet(); pointcut internalCall () : call(* JoinPointInAroundAdvice.privateMethod(..)); before () : internalCall () { privateCalls.add(thisJoinPoint); } pointcut externalCall () : call(* JoinPointInAroundAdvice.publicMethod(..)); before () : externalCall () { publicCalls.add(thisJoinPoint); } pointcut execTest () : execution(* JoinPointInAroundAdvice.test()); before () : execTest () { privateMethod("before"); publicMethod("before"); } void around () : execTest () { privateMethod("around"); publicMethod("around"); proceed(); } // void around () : execTest () { // Runnable runnable = new Runnable () { // public void run () { // privateMethod("around closure"); // publicMethod; // proceed(); // } // }; // runnable.run(); // } after () : execTest () { privateMethod("after"); publicMethod("after"); } private static void privateMethod (String from) { System.out.println("? privateMethod() " + from); } public static void publicMethod (String from) { System.out.println("? publicMethod() " + from); } public static void test () { System.out.println("? test()"); privateMethod("test"); publicMethod("test"); } public static void main (String[] args) { test(); if (privateCalls.size() != publicCalls.size()) { throw new RuntimeException("Missing join point"); } else { System.out.println("Success."); } } }
I better write up where I am before I forget!!! I thought this was going to be one of those bugs where you start diagnosing it and discover there is a fatal flaw in AspectJ. I think like this about most hard bugs, but every single time (including this one) I discover that there is a perfectly ideal framework for solving the bug, someone just forgot to fill in this piece of it. The compiler has a notion of an effective signature which is used for inter type declarations - it enables you to do something clever (like call an accessor method on another class) but pretend it casts an entirely different join point shadow (like a field access). As Matthew points out, the problem is that there is a grotty generated accessor method: ajc$inlineAccessMethod$bug_nnnn_JoinPointInAroundAdvice$bug_nnnn_JoinPointInAro undAdvice$privateMethod("around"); that is being called which just delegates to the real 'privateMethod()'. My first plan of attack was to work purely in the weaver and when I saw something like this I'd try and undo the generated name (i know, i know) and create an effective signature of 'privateMethod()' which would then be matched. Yes, it was insane, I was young and foolish yesterday when I was trying that. Little did I know there was a grotty testcase in the suite that would never let me do it that way. Look at this: aspect EnsureShipIsAlive { void around (Ship ship): Ship.helmCommandsCut(ship) { if ( ship.isAlive() ) { proceed(ship); } } } in SpaceWar. The call 'ship.isAlive()' is another example of a missing shadow that we haven't been able to match on. That kind of call created a *ReAlLy* horrible accessor, and isAlive() isn't even declared on ship, it is on the superclass of ship, SpaceObject. So, forget that, what I need to do is remember what the accessor was generated for at compilation time and use that at weave time when trying to create this 'fake shadow'. All I have to do to make this work is create an effectivesignature attribute against the generated accessor method and then the magic in BcelClassWeaver.matchInvokeInstruction() will pick up the attribute I stuffed into the file and use it to create a shadow we can match against. This worked perfectly ... and then ... I hit a problem with thisJoinPoint. Objects that represent TJPs throughout the code are usually created PRIVATE STATIC FINAL. The problem is that I had now surfaced some new join points in code that might get inlined, and if you advised those join points and used thisJoinPoint then in the advice, we attempted to reference a PRIVATE STATIC FINAL TJP instance in the class where the shadow existed before inlining. Usually resulting in a verify error. So I modified the code that generates TJP instances to allow them to be public if the enclosing method for the shadow is inside around advice (I didn't do before and after advice as well, because that broke more tests and I believe we don't inline before or after advice). Was that it? Well, that covers calls to private methods from around advice... what it doesnt cover is sets or gets of private fields in around advice. Aha, I thought, this will be easy, I just create an effectivesignature at compile time and the backend will do the right thing. But no ... at compile time I haven't quite found a way (at the point we generate the attribute) to know if it is a set or a get. I know it is a field access but not what kind. How about I put in a compile time flag so you can choose which you want? ... only joking - I will get this fixed soon as I can.
Fixed. I rejiggled the fix in AspectDeclaration. Rather than generating new attributes in the generateMethod() method, you can pass an optional set of additional attributes when you call generateMethod() and these will be attached to the generated code. If the method being generated is an inlineAccessMethod for a private method call, we have a MethodCall effective signature attribute. For inlineAccessMethods that represent field accesses we add either the field_set or field_get attribute. Creating the attributes outside of the generateMethod() attribute enabled me to know enough to differentiate between field set and field get. Fix checked in, waiting for build before closing.
Fix available: BUILD COMPLETE - build.331 Date of build: 08/09/2004 11:57:04 Time to build: 117 minutes 38 seconds Last changed: 08/09/2004 11:27:04 Last log entry: Fix for Bug 71377: Cannot advise private method call in around advice Latest good AspectJ jar available at: download.eclipse.org/technology/ajdt/dev/aspectj-DEVELOPMENT.jar
Fix released as part of AspectJ 1.2.1