Bug 72766 - AspectJ features for Java 5
Summary: AspectJ features for Java 5
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: Adrian Colyer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-08-27 05:48 EDT by Adrian Colyer CLA
Modified: 2005-08-17 15:00 EDT (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Adrian Colyer CLA 2004-08-27 05:48:37 EDT
This enhancement request captures design notes for the extension of the AspectJ language and weaver 
to support the new Java 5 features.

This discussion needs to get pushed onto aspectj-dev soon...

Here are the list of Tiger features/changes that I think will impact our design or implementation. 
**Warning** this list may not be complete.

* covariance - what should the call and execution pcds match for covariant methods?
* generics - lots to say on this topic, see below
* enum types - especially their interaction with inter-type declarations
* autoboxing/unboxing - any implications for pcd matching? Definitely has method resolution 
implications for ITDMs
* varargs - support for ... in signature matching. Treatment of ... vs [] in weaver. 
* annotations (of course) - in type patterns, in pcd matching, declare annotation. weaving semantics 
issues for attributes with different retention policies
* changes to the class file format and introduction of new attributes such as ACC_BRIDGE
* Miscellaneous bug fixes and changes to the bytecodes generated by the Tiger compiler - we know of 
at least one issue in this category: the invokevirtual instruction for a call to clone() on an array has a 
different target in tiger (the array type as opposed to Object in 1.4).

COVARIANCE
========================

given the types

class Super {
  Car getCar() {...}
}

class Sub extends Super {
  @Override      // AMC - is this annotation *required* here?
  FastCar getCar() {...}
}

what do the following call and execution pcds match?

call(* getCar())   // no change, matches as today

call(* Super.getCar())  // matches a call to Super.getCar.
                                  // also matches a call to Sub.getCar

call(Car getCar())            // these two only match calls to Super.getCar() ?
call(Car Super.getCar())  // ie. if the static type of the receiver is Sub, it will *not* match?

call(Car Sub.getCar())     // does not match anything?? Even though Car getCar is declared in
                                     // a super-type of Sub?


call(FastCar getCar())             // matches only Sub.getCar()
call(FastCar Sub.getCar())      // ""

call(FastCar Super.getCar())  // does not match anything

call(Car+ getCar())         // matches Sub.getCar and Super.getCar
call(Car+ Sub.getCar())  // ""

(similar exercise to be done for execution pcds)

note that most call/exe pcd's don't specify a return type (I suspect). For those that do, covariance 
becomes an issue. The rules I've given allow you to match join points for any combination of Super and 
Sub methods (Car, FastCar, and Car+). Will Type+ become the new idiom for return type patterns when 
you want to take covariance into account?

GENERICS
========================

If you though pcds could get messy before...

Three areas to consider:

*Extension of signature matching in pcds to account for generic types
* ITD of generic methods and fields
* Generic aspects??? Not initially for sure, and maybe not ever. Tied up in aspect instantiation.

Biggest issue is matching of generics in pcds.

- everywhere we currently allow a type pattern, we should allow  TypePattern<TypePattern 
[,TypePattern]>. To say it more precisely,

  AspectJ13TypePattern ::= AspectJ12TypePattern[GenericPattern]
  GenericPattern ::= <AspectJ13TypePattern [,AspectJ13TypePattern]*>

(and remember, generics don't exsist in bytecode - a wonderful thing called 'erasure' - so this could all 
be quite good fun in the weaver... - I believe there are some attributes around to help us though)

Need to consider how we treat e.g. List<?> - you can write this in source, what does it look like in 
bytecode?? A type pattern List<?> is distinct to List<*>.

For target, args, and this we don't allow type patterns, only a concrete type specification. So we will 
allow a generic type specification e.g. List<String>.

Note that List<FastCar> cannot be cast to (is not an instance of) List<Car>. LinkedList<FastCar> can 
be cast to List<Car> - in other words the hierarchy is based on the base type, not the parameter type. 
This will need calling out to users, and is the reason we need to allow people to write things like 
List<Car+> in type patterns.

ITD of generic fields (not static) and methods is conceptually straightforward, but might present some 
interesting additional type-checking requirements.

ENUMS
========================

In bytecode they're just classes... with the enum attribute.

Spec explicitly forbids extending the set of enum values in an enum type in any way. I don't think we 
should allow this either (certainly not initially) via an ITD form - note, if we did allow it, in addition to 
the semantic issues, it's an implementation nightmare in the case that the enum is declared in the same 
compilation unit as a switch statement that uses it. 

Enums can have methods and fields, so we should allow ITDM and ITDF for enums. ITDC for enums 
should be forbidden - this doesn't make sense. No *abstract* ITDMs for enums - these have special 
meaning, requiring each value to implement them - but there's no way to do that from the declaring 
aspect.

Enums can implement interfaces, so decp implements would be allowed. decp extends with an enum 
should be forbidden (it's not allowed in source) - you can neither extend an enum nor make an enum 
(or java.lang.Enum) the supertype of any other type using decp.

AUTOBOXING
========================

Does this affect our isAssignableFrom and isCoercibleFrom tables in any way?

Should we support autoboxing / unboxing for args pcd? (probably) 

e.g. args(int) would then match a call to foo(Integer i).

Proceed with caution here, but it's what Tiger does...!

Autoboxing (and varargs) messes with the method resolution algorithm. We need to fit ITDMs into the 
new resolution process. The process is as roughly follows:

1) Attempt to locate the correct method without considering boxing, unboxing or vararg invocations. 
ITDMs should be considered at this matching stage before progressing to stage 2.

2) If no match found, try again but also considering boxing and unboxing (but not varargs). ITDMs 
should again be considered at this stage

3) Finally try again allowing both boxing and varargs, once again considering ITDMs.

Consequences are that a directly matching ITDM will take precedence over a method declared locally in 
the target class that matches via boxing and so on. 

VARARGS
========================

*More* pcd fun.

void foo(String s, Car... cars) is now a legal declaration.

We need to allow TypePattern... , but only as the last argument within the signature specification of a 
call/exe/init pcd. 

e.g. 

call(* foo(String, Car...)) is legal

call(* foo(Car..., String) is not legal

call(Car... foo(..)) is not legal

We should also allow Type... as the last entry in an args pcd.

in bytecode foo(String s, Car... cars) ends up with a signature foo(String,Car[]). We therefore have to be 
quite careful in join point matching.

a call to foo(s, carArray) where carArray is of type Car[] does NOT MATCH foo(String,Car...) (I believe). A 
call to foo(s) CAN MATCH foo(String,Car...). Such fun!

ANNOTATIONS
========================

And you thought type patterns had got messed up enough with generics...

Annotations have different retention times - source code, class file, and runtime, with class file being 
the default. Do we ignore source code only attributes?? (for consistent semantics across weave times)

Annotations apply to a wide variety of elements (type, field, method, parameter, constructor, local 
variables, packages, annotation types). How do we fit this neatly into signature patterns? 

A simple example to start:

call(@Oneway * foo(..))

matches a call to a method foo that has a method attribute "@Oneway".

Annotations should probably work like modifiers here... so today you can write

call(private protected * foo(..))  

to match either private or protected (and you can also write e.g. !public). So with annotations you 
should be able to write

call(@Oneway @Twoway * foo(..))

or call(!@Oneway * foo(..))

positionally, for method, constructor, and field signature patterns, I believe annotations should come 
before modifiers:

eg call(@Oneway public * foo(..))  rather than call(public @Oneway * foo(..)) as this is the order they 
would appear in the method declaration.

To match field attributes the style would be similar:

set(@Sensitive * *)

Next comes the question of qualifying types by annotation.

Instead of call(* Foo+.foo(..))

we can write call(* (@Sausages Foo+).foo(..))

The parens around (@Sausages Foo+) are required in this case. Note that as before you could also write

pointcut breakfastOrder() ; call(* (@Sausages @Bacon @Egg !@Tomato Foo+).foo(..)) etc..

To specify *any* type with the @Sausages annotation, you must write (@Sausages *) as the type pattern, 
not (@Sausages).

Qualifying parameter types looks like this

call(* foo((@Sausages Order),..))

same rules as for declaring types.

If you really feel the need to qualify based on package annotations you could write

call (* (@SpecialPackage org.xyz..*).Foo+.foo(..))

the parens around the package pattern would once more be required in this case.

It's not done yet....

what about runtime type matching based on annotations as opposed to static type matching. In other 
words, I should be able to write

args(@Sausages Foo) etc..

the requirements are that you *must* still specify a type (not a type pattern). This is an error to specify 
(as is usage in this, target) if @Sausages does not have runtime retention. The weaver adds a runtime 
test for the @Sausages attribute on the actual argument where static analysis cannot show that it 
definitely will or will not be present (e.g. when match returns FuzzyBoolean.MAYBE).

It's still no done yet...

annotations can have values. for a single valued annotation I should be able to write

@Eggs("sunnySideUp")

which should match any Eggs annotation where the value includes "sunnySideUp"

@Eggs({"sunnySideUp","freeRange"})  matches any Eggs annotation where the value includes 
"sunnySideUp" AND "freeRange" (AND possibly other values too?). (So is there a way to say ONLY these 
values?? Do we have to introduce a * wildcard for values and make the former match when ONLY the 
two values are present???)

@Eggs("sunnySideUp") @Eggs("freeRange")   matches sunnySideUp OR freeRange.

These annotations are equivalent to e.g. @Eggs(value="sunnySideUp") so that should be allowed too.

Full annotations (as opposed to single-value annotations) can have multiple attributes. So I can write 
e.g. @Eggs(cookingStyle="sunnySideUp", chef="Fred") and it is reasonable to expect to match on that 
too. This whole thing raises the dreadful spectre of patterns in attribute values too. e.g. can I write 
@Eggs(chef="Fred*") ???

We could defer annotation value matching until a follow-on and just match based on annotation name 
for the first pass?

With all these complicating additions to patterns, we are going to need to write out a pointcut grammar 
for our 1.5 support to make sure we get it right and to clearly document what we support.

The flip side of the coin.... whilst the world is going annotation crazy, it won't be long before you want 
to use declare annotation to enable your POJO to be free of framework specific annotations (when is a 
POJO not a POJO - when it's got annotations outside of its domain). We could call it an AFPOJO.... 

to handle this we introduce declare annotation:

declare annotation : TypePattern : @Annotation(...);

because annotation can be on so many element types we also need to support

declare annotation : TypePattern.idPattern(argPattern) : @Annotation

for methods and constructors (idPattern is "new" for a cons).

declare annotation : TypePattern.idPattern : @Annotation

for fields.

The last hurrah.

We should support annotations on ITDs.

e.g.
@YourPlaceOrMine
private void Foo.foo() {...}

CLASS FILE CHANGES
========================

In which bridge methods and ACC_BRIDGE attributes appear and we need to figure out what join points 
if any these things possess.

MISC
========================

Gather miscellaneous changes we come across in this category. So far, just the change in the receiving 
type of a call to clone for an array in the invokevirtual instruction.
Comment 1 Mik Kersten CLA 2004-09-13 13:47:57 EDT
I think that we'll benefit from Ramnivas' feedback on this, so I'm adding him 
as a CC.
Comment 2 Ramnivas Laddad CLA 2004-11-08 14:16:21 EST
I have added my thoughts on how metadata should be supported in AspectJ as a
blog entry:
http://ramnivas.com/blog/index.php?p=10
Comment 3 Adrian Colyer CLA 2005-08-17 15:00:56 EDT
All of the items in this report are now implemented in the AspectJ 5 code base (with a good number of 
design issues resolved along the way). The AspectJ 5 Developer's Notebook contains the much longer 
summary of the design we ended up with.