Bug 73050 - Type patterns with numbers incorrectly match anonymous and local classes.
Summary: Type patterns with numbers incorrectly match anonymous and local classes.
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: All All
: P2 enhancement (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Adrian Colyer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-09-01 08:59 EDT by Aske Simon Christensen CLA
Modified: 2005-11-04 08:15 EST (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aske Simon Christensen CLA 2004-09-01 08:59:32 EDT
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.
Comment 1 Adrian Colyer CLA 2004-09-08 08:52:40 EDT
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.
Comment 2 Erik Hilsdale CLA 2004-09-09 10:45:55 EDT
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. 
Comment 3 Adrian Colyer CLA 2005-03-23 08:07:21 EST
there was a lot of interest in inner and anonymous class matching at AOSD 2005.
We should look at this in the M3 timeframe.
Comment 4 Andrew Clement CLA 2005-08-24 10:33:13 EDT
pushing to M4 ... gotta get M3 with generics out.
Comment 5 Adrian Colyer CLA 2005-10-28 08:21:22 EDT
I've moved to P2 temporarily just to make sure I take a look at this again before RC1
Comment 6 Adrian Colyer CLA 2005-11-04 08:15:01 EST
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.