Community
Participate
Working Groups
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.
I think that we'll benefit from Ramnivas' feedback on this, so I'm adding him as a CC.
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
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.