Community
Participate
Working Groups
Similarly to javac, ajc compiles anonymous and local classes into classes with scrambled names. Type patterns in pointcuts will treat these classes as member classes with a name identical to their scrambled name. Thus, such a class will be matched by a pattern if it happens to have a scrambled name that textually matches the pattern. Since scrambled names are made up by the compiler and may change if more anonymous or local classes are added, this leads to nonintuitive and inconsistent behaviour. Consider this class: public class MatchingAnonymous { static class Foo { void foo() { System.out.println("Foo.foo:"); } } static Foo x = new Foo() { void foo() { System.out.println("Anonymous.foo:"); } }; static void m() { class Local { void foo() { System.out.println("Local.foo:"); } } new Local().foo(); } public static void main(String[] args) { new Foo().foo(); x.foo(); m(); } } woven with this aspect: public aspect MA_Aspect { after(): execution(void MatchingAnonymous.*.foo()) { System.out.println(".*"); } after(): execution(void MatchingAnonymous..*.foo()) { System.out.println("..*"); } after(): execution(void MatchingAnonymous.*1.foo()) { System.out.println(".*1"); } after(): execution(void MatchingAnonymous.*1*.foo()) { System.out.println(".*1*"); } } Running the program will produce this output: Foo.foo: .* ..* Anonymous.foo: .* ..* .*1 .*1* Local.foo: ..* from which it is evident that ajc considers the anonymous class to be a member class named "1". If the line Object o = new Object() {}; is added at the beginning of the class body of MatchingAnonymous, the output changes to Foo.foo: .* ..* Anonymous.foo: .* ..* Local.foo: ..* The pattern no longer matches, since the class now happens to be named "2", rather than "1". Furthermore, different naming schemes from different compilers show themselves in the matching. If the above class is compiled using javac, and then woven with the aspect, the output becomes: Foo.foo: .* ..* Anonymous.foo: .* ..* .*1 .*1* Local.foo: .* ..* .*1* The local class is now called MatchingAnonymous$1Local (javac naming convention) rather than MatchingAnonymous$1$Local (ajc naming convention). Thus, ajc now considers it to be a direct member class of MatchingAnonymous, called "1Local", where it was otherwise considered a member class of a member class called "1", called "Local". To sum up, there should be a consistent way of matching anonymous and local classes that does not depend on the naming conventions of the compiler.
The ideal scenario would be that anonymous classes are never matched by type patterns in pointcuts, other than the pattern "*". (They're anonymous, you are not supposed to be able to refer to them by name - which is what any pattern other than "*" attempts to do). Unfortunately, since the naming choice for anonymous classes was left open to implementors (as you point out) there is no way to tell the difference between an anonymous class called "1" and a named class called "1" (bear in mind that AspectJ gives consistent semantics for both source and binary weaving, and binary input could come from any Java compiler). In addition to not being able to distinguish by name, there is also no indication in the class file (for example, a constant pool attribute) that I can see to indicate that a class is anonymous. Therefore it is not possible to implement the ideal scenario at present. So, that leaves us with the not unreasonable statement that you should not rely on type pattern matching against anonymous classes (and probably don't want to write patterns such as Outer.*1 unless you really mean it - ie. you have named classes you need to match). I suspect this restriction is perfectly acceptable for many, many programs. Did you arrive at this bug as a result of a real program behaving strangely, or as a result of reasoning about the behaviour of type patterns with anonymous classes. Java 5 introduces a new class attribute in the class file, "EnclosingMethod", which is present if and only if the class is a local or anonymous class. So in Java 5 compatible mode, we may be able to implement the "ideal case" I described. I am changing this to an enhancement request to consider this as part of our Java 5 support work in AspectJ.
We _should_ have enough information about an anonymous class from the enclosing class's InnerClasses attribute (JVM 4.7.5) http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996 in particular: inner_name_index If C is anonymous, the value of the inner_name_index item must be zero. I know I've previously used this fact, so I'm pretty sure it's generally properly implemented in popular compilers. Also (and I'd have to verify that this is correctly generated by the popular compilers), local classes should be findable by this: outer_class_info_index If C is not a member, the value of the outer_class_info_index item must be zero. I'm pretty sure I used these two bits of information (mainly the second) in ajc 1.0 to build a containment tree, and only allowed by-name matching of nested types where every enclosing type was either package level or had a non-zero outer_class_info_index (you can get into trouble because anononymous/local classes can have member types. Weird.). Anyway, I believe this is implementable for anybody that generates correct InnerClassAttribute information, which should be something like language level 1.3.
there was a lot of interest in inner and anonymous class matching at AOSD 2005. We should look at this in the M3 timeframe.
pushing to M4 ... gotta get M3 with generics out.
I've moved to P2 temporarily just to make sure I take a look at this again before RC1
I've just commited the fix for this and it will be available in the next published build. Anonymous types are now only matched by the name pattern "*" - any other pattern fails to match since anonymous types are treated as having no name.