From d842c4f1139629c1f062b74ba818d233b2c31043 Mon Sep 17 00:00:00 2001 From: wisberg Date: Mon, 16 Dec 2002 17:58:19 +0000 Subject: initial version --- docs/dist/doc/porting.html | 1785 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1785 insertions(+) create mode 100644 docs/dist/doc/porting.html (limited to 'docs/dist/doc/porting.html') diff --git a/docs/dist/doc/porting.html b/docs/dist/doc/porting.html new file mode 100644 index 000000000..0a4d558a3 --- /dev/null +++ b/docs/dist/doc/porting.html @@ -0,0 +1,1785 @@ + + + + + +AspectJ 1.0.6 Reference - Porting Notes + + + + +
+© Copyright 1998-2002 Palo Alto Research Center Incorporated. All rights reserved. +
+ +

Porting Notes

+ + + +

Porting pre-1.0.4 code

+ +

In versions of AspectJ prior to 1.0.4, the compiler was not +correctly implementing the AspectJ-1.0 language design for some uses +of after returning advice. +

+ +

The main change that was made was of after returning advice for +constructor execution join points. Previously, this advice was legal: +

+ +
+after() returning (Foo f): execution(Foo.new(..)) { ... }
+
+ +

However, it has always been a part of the 1.0 language design (and +of Java's language design) that constructors themselves (as opposed to +constructor calls) do not return the value of the new object. Rather, +this is bound to the new object, and the constructor +behaves like a void method. With that in mind, any code like the +above should be conveted to the form.

+ +
+after(Foo f) returning: this(f) && execution(Foo.new(..)) { ... }
+
+ +

In compilers prior to 1.0.4, the following advice could pick out +join points +

+ +
+after() returning (String s): call(void foo()) { ... }
+
+ +

This is no longer picked out. This pattern was most commonly used +in highly polymorphic contexts, such as +

+ +
+after() returning (String s): call(* foo()) { ... }
+
+ +

If you want to capture all calls, binding null objects for those +that would otherwise have no value, you must use the +Object type. +

+ +
+after() returning (Object o): call(* foo()) { ... }
+
+ +

Uses of both of these forms are highleted with compiler warnings +in the 1.0.4 compiler. +

+ + +
+ +

Porting pre-1.0rc1 code

+ +

Aspects can no longer be declared to implement the +Serializable or Cloneable interfaces. If +you previously used serializable or cloneable aspects, you should +refactor your code to keep the state you need to serialize or clone in +objects associated with the aspects. +

+ +
+ +

Porting pre-1.0beta1 code

+ +

The static modifier is no longer allowed on pointcut +declarations anywhere. Porting is simple; just remove the static +declarations when you find them. +

+ +

Also, though the returns modifier on pointcuts has +not been part of the language since 1.0alpha1, the compiler still +accepted them until now. If you used this feature, now is the right +time to remove the returns modifier when the compiler +complains about it. +

+ +
+ +

Porting pre-1.0alpha1 code

+ + +

The release of AspectJ 1.0alpha1 involved sweeping cleanups of the +language to bring it to 1.0 status.

+ + + +

Pointcuts

+ +

Removing the "s" from pointcuts

+ +

One of the most pervasive changes in porting code written before +1.0alpha1 is the change in some of the pointcut names from plural to +singular, that is, they lose an "s". In one sense, making this change +in your programs is easy: just go through and whever you see uses of +the pointcuts +

+ +
calls executions gets sets handlers initializations +staticinitializations
+ +

Just take off the final "s", to make one of +

+ +
call execution get set handler initialization +staticinitialization
+ +

Often, there will be other changes you should make for each of +these pointcuts, but as for the name, just take off the "s".

+ +

One risk you will have when doing this is creating name conflicts. +If, for example, you named a parameter of a pointcut "set", you should +(for your own sanity -- the compiler doesn't require it) rename it in +the rewritten pointcut.

+ +
+pointcut sort(Collection set): calls(void addAll(set));
+==>
+pointcut sort(Collection mySet): call(void addAll(mySet));
+
+ +

While converting to use singular nouns for the primitive +pointcuts, you may also want to remove the "s" from your user-defined +pointcuts.

+ +
+pointcut publicCalls(): calls(public * *(..));
+==>
+pointcut publicCall(): call(public * *(..));
+
+ +

Of course, your naming conventions are your own, but throughout +these porting notes we will be making these changes in our example +ports.

+ + +

Removing the receptions pointcut

+ +

Perhaps the largest semantic change in the 1.0 language is the +removal of receptions join points. They have been merged with call +join points in AspectJ 1.0, so now a call join point doesn't represent +the "caller-side" of a call, but the call itself, both caller and +receiver.

+ +

Changing code that used the receptions pointcut should be +fairly straightforward, depending on whether the pointcut exposed state or +not.

+ +
Not exposing state
+ +

Receptions pointcuts that did not expose state can simply be +replaced by the new call and target pointcuts:

+ +
+receptions(void Foo.m())
+==>
+target(Foo) && call(void m())
+
+ +
Exposing state
+ +

Some receptions pointcuts exposed the receiving object by +replacing the receiving type with a pointcut formal. These PCDs +should be rewritten to use the new target pointcut to expose +the receiving object.

+ +
+pointcut fooCallees(Foo f): receptions(void f.m());
+==>
+pointcut fooCallee(Foo f): target(f) && call(void m());
+
+ +

Like other pointcuts, +receptions pointcuts that exposed one or more arguments should be +rewritten to use the args pointcut:

+ +
+pointcut intPassers(int i, int j): receptions(void Foo.m(i, j));
+==> 
+pointcut intPasser(int i, int j):
+    args(i, j) && target(Foo) && call(void m(int, int));
+
+ +
Constructor receptions
+ +

There are two issues with constructor receptions in +particular.

+ +

Like constructor calls, +constructor receptions pointcuts had a dynamic character, in that +receptions(C.new()) would capture constructions of not +only C classes, but also of classes that extended C.

+ +

If you want this behaviour, then you need to use the new subtypes +operator, +, on the type name in question. So, +

+ +
+receptions(C.new())  
+==>
+call(C+.new())
+
+ +

Also like constructor calls, +constructor receptions allowed access to the constructed object in the +same way as any other object. Since the only advice possible on +constructor receptions join points was after returning +advice, the object was always guaranteed to be there. But since +constructor call join points allow all kinds of advice it may be that +the object isn't constructed yet (say, in before or around advice). +This is a benefit, in that it allows caching constructed objects

+ +
+aspect Singleton {
+    private C theC = null;
+
+    C around(): call(C.new(..)) {
+        if (c == null) theC = proceed();
+        return theC;
+    }
+}
+
+ +

but it does require some rewriting. The new object can be +accessed as the return value in after returning advice. So,

+ +
+after(Point p) returning (): receptions(p.new(int, int)) { ... }
+==>
+after() returning (Point p): call(Point+.new(int, int)) { ... }
+
+ +

Fixing state access

+ +

In previous versions of AspectJ, state such as the currently +executing object or a particular argument of a method call could be +accessed from the signatures of many pointcuts, leading to +difficult-to-read forms. In AspectJ 1.0, all state accesses now use +only three pointcuts

+ +
args this target
+ +

which pick out argument values, the currently executing object, +and the target object of a method call or field operation, +respectively.

+ +
Using args
+ +

Any time you have a pointcut that has a signature where one of the +arguments was a pointcut or advice formal, just replace that formal +with its type and add an args pointcut. +

+ +
+pointcut intPassers(int i, int j): calls(void Foo.m(i, j));
+==>
+pointcut intPasser(int i, int j): args(i, j) && call(void Foo.m(int, int));
+
+ +
+pointcut stringPassers(String s): receptions(void Foo.m(s, ..));
+==>
+pointcut stringPasser(String s): args(s, ..) && call(void Foo.m(String, ..));
+
+ +
Rewriting calls
+ +

If a calls pointcut exposed the the receiving object, such as

+ +
+pointcut fooCallees(Foo f): calls(void f.m());
+
+ +

then the new version should use the target pointcut +to get at that object +

+ +
+pointcut fooCallee(Foo f): target(f) && call(void Foo.m());
+
+ +

AspectJ's calls pointcut previously allowed the new object to be +exposed, even though it may not have been constructed yet. AspectJ +1.0 no longer allows this; you can access the new instance only in +after returning advice, when it is guaranteed that the object was +successfully constructed. So instead of using the target +pointcut to expose the value, you should use the normal after +returning mechanism: +

+ +
+after(Point p) returning (): calls(p.new(int, int)) { ... }
+==>
+after() returning (Point p): call(Point+.new(int, int)) { ... }
+
+ + +
Rewriting gets and sets
+ +

Exposing the target object of a gets or +sets pointcut should be done the same way it was for +calls pointcuts, with the new target +pointcut.

+ +
+before(Frame f): gets(Color f.color) { ... }
+==>
+before(Frame f): target(f) && get(Color Frame.color) { ... }
+
+ +
+before(Frame f): sets(Color f.color) { ... }
+==>
+before(Frame f): target(f) && set(Color Frame.color) { ... }
+
+ +

In addition, the clumsy syntax for getting the old value of the +field has been eliminated. For before advice, the port is simple; +just access the field yourself in the body. Depending on the rest of +your system, you may need to restrict the advice from the aspect body +to eliminiate the circularity.

+ +
+aspect A {
+    before(Frame f, Color c): gets(Color f.color)[c] { ... }
+}
+==>
+aspect A {
+    before(Frame f):
+            target(f) && get(Color Frame.color) && !within(A) {
+        Color c = f.color;
+        ...
+    }
+}
+
+ +

The same can be done for around advice. However, the +only way to port after advice that needs the old value is to convert +it to around advice. +

+ +
+aspect A {
+    after(Frame f, Color c) returning (): gets(Color f.color)[c] { ... }
+}
+==>
+aspect A {
+    void around(Frame f):
+            target(f) && get(Color Frame.color) && !within(A) {
+        Color c = f.color;
+        proceed(f);
+        ...
+    }
+}
+
+ +

When porting sets pointcuts, the new value of a field +is still available, but not the way it was previously. Instead of +using the square bracket syntax, we use an args pointcut. +All set join points are assumed to have exactly one argument, which +holds the new value. So,

+ +
+after(Color newColor): sets(Color Frame.color)[][newColor] { ... }
+==>
+after(Color newColor): args(newColor) && set(Color Frame.color) { ... }
+
+ +

Also, if the field was declared private, in order to get at its +old value the aspect must be declared privileged. +

+ +
Rewriting handlers
+ +

The value of the exception at an exception handler join point is +now accessed through the args pointcut; all exception +handler join points are treated as having exactly one argument, the +exception value. So, +

+ +
+before(NotFoundException e): handlers(e) { ... }
+==> 
+before(NotFoundException e): args(e) && handler(NotFoundException) { ... }
+
+ +
Rewriting within
+ +

The within pointcut was not typically used to export +context. Though it was accidentally possible to do so in versions of +AspectJ before 1.0, it often didn't do what users expected it to. +This loophole has now been closed, and within can only take type +patterns, not pointcut or advice formals. A use of the +this pointcut will capture what previous implementations +did:

+ +
+pointcut usesFoo(Foo f): within(f);
+==>
+pointcut usesFoo(Foo f): this(f) && within(Foo);
+
+ +

Understanding signatures

+ +

Now that we have this, target, and +args pointcuts, all of our signatures are composed of +just types, names, and wildcards; there are no more parameters. +

+ +

Also, now that we have the + wildcard to pick out +subtypes, we can make signature +matching much more uniform.

+ +

Previously, some signatures matched based on subtypes, some based +on instanceof, and some exactly. Now, we have made all signatures +match exactly. +

+ +

What does this mean for your program? Well, it means that you +may have to add + to some of your signatures, depending +on what you meant them to match. +

+ +

For example, the pointcut +

+ +
+calls(void m(Object))
+
+ +

previously picked out all method calls to a method named m that +took one argument, which was a subtype of Object. Now, however, it +will only pick out method calls to methods that are defined to take +exactly the type Object, which may be a lot fewer join points. If you +want the old behaviour, simply convert to

+ +
+call(void m(Object+))
+
+ +

Removing the instanceof pointcut

+ +

The intanceof pointcut has been split into two different +pointcuts, this and target.

+ +

Typically, the instanceof pointcut would only exist in a compound +pointcut, composed (with &&) with another +pointcut. If the other pointcut was a receptions +pointcut, then instanceof should be converted to +target (and receptions converted to +call). So,

+ +
+pointcut stateChanges(Subject s): 
+    instanceof(s) && receptions(void Button.click());
+==>
+pointcut stateChange(Subject s): 
+    target(s) && call(void Button.click());
+
+ +

In all other cases, instanceof referred to the +currently executing object, and so should be converted into +this

+ +
+before(Point p): instanceof(p) && executions(* makePolar(..)) { ... }
+==>
+before(Point p): this(p) && execution(* makePolar(..)) { ... }
+
+ +
+pointcut setup(Client c): instanceof(c) && calls(Remote Naming.lookup(String));
+==>
+pointcut setup(Client c): this(c) && calls(Remote Naming.lookup(String));
+
+ +

Rewriting the initializations pointcut

+ +

Object initialization join points are now more complicated, and +more true to Java's execution model. Now they bracket all of the +initialization that a class can do, after the return of its super +constructor call (before which no initialization can happen). Previous +versions of AspectJ had object initialization join points that only +included initialization that was made in dynamic initializers and +fields.

+ +

The old behaviour can be recovered with a simple rewrite. +

+ +
+initializations(A)
+==>
+initialization(A.new(..)) && !execution(A.new(..))
+
+ +

Understanding constructor calls

+ +

Previously, constructor call join points were matched by subtypes, +so calls(Foo.new()) would match both calls to create new +Foo objects, and new SubFoo objects. The +new call pointcut designator matches types exactly, so if +you want the old behaviour, you should write +call(Foo+.new()).

+ +

Similarly, constructor execution join points were matched by +subtypes. So the old executions(Foo.new()) is now +represented by execution(Foo+.new()). +

+ +

In both of these cases, think before using the + operator; it may +be that you didn't intend subtype matching in the first place.

+ +

Removing the hasaspect pointcut

+ +

The hasaspect pointcut is no longer defined, but you +can get the same behaviour using the new if pointcut. +

+ +

If the aspect whose presense you are checking for was defined +of eachcflow, of eachcflowbelow, or, more +unlikely, of eachJVM(), then the conversion is simple: +

+ +
+hasaspect(A)
+==>
+if(A.hasAspect())
+
+ +

If the aspect was defined of eachobject, then you +will have to expose the current object in your pointcut or advice +parameters:

+ +
+pointcut cut(): hasaspect(A) ... ;
+==>
+pointcut cut(Object o): this(o) && if(A.hasAspect(o)) ... ;
+or
+pointcut cut(Object o): target(o) && if(A.hasAspect(o)) ... ;
+
+ +

If you were using the hasaspect pointcut to expose +the state of the aspect, then you can get the same state by using +A.aspectOf() in the body of the advice. For example, if +the aspect A were defined of eachcflow, then +

+ +
+before(A myA): hasaspect(myA) {
+    myA.checkStatus();
+}
+==>
+before(): if(A.hasAspect()) {
+    A myA = A.aspectOf();
+    myA.checkStatus();
+}
+
+ +

Removing the withinall pointcut

+ +

The withinall poinctut is no longer defined. You can use a +combination of within and the new +subtypes operator, +, instead. You'll save two characters and be +using a simpler and more orthogonal language.

+ +
+withinall(Foo)
+==>
+within(Foo+)
+
+ +

Removing returns modifier from pointcuts

+ +

The returns keyword is no longer necessary for user-defined +pointcuts. Simply remove it when you find it.

+ +
+pointcut publicIntCalls() returns int: calls(public int *(..));
+==>
+pointcut publicIntCall(): call(public int *(..));
+
+ +

Making some pointcuts static

+ +

In Java, only static members may be accessed by their declaring +type name, like the static method Math.max() can be +accessed.

+ +

Pointcuts now have that property too. Pointcuts may be declared +to be static, in which case they can be accessed like +MyAspect.move(), or they can be left non-static, in which +case they can be overridden by a subaspect.

+ +

In addition, while pointcuts can still be defined in classes, only +static pointcuts can be defined in classes.

+ +

Porting should be straightforward; just make all your pointcuts in +classes static, and make any pointcut with a qualified +reference static. +

+ +

Type patterns

+ +

Understanding * and .. in type patterns

+ +

Previous versions of AspectJ treated * and .. too cleverly in type +patterns, placing restrictions based on what is a package and what is +a type, and basing their meanings on the definition of a package +hierarchy.

+ +

In AspectJ 1.0, both of these wildcards are defined simply, and +textually: +

+ + + +

That's it. +

+ +

This change won't affect most programs, but it will make +understanding programs easier. There is one ugly idiom, however, that +this change disposes of. If your program includes the type pattern +*..*, which used to match all types, you can replace it with the +much simpler *.

+ +
+pointcut unaryVoidMethods(): call(void *(*..*));
+==>
+pointcut unaryVoidMethod(): call(void *(*));
+
+ +

Fixing subtypes in introduction

+ +

The new + operator is used to normalize the many places you want +to use subtypes of some types. +

+ +

In introduction forms, you will need to replace +subtypes(TypePattern) type patterns with the +new subtype operator, +. In the case where you wrote +subtypes(Foo), i.e., the subtypes of a single type, +simply replace this with Foo+. Otherwise, use the ++ operator as appropriate in TypePattern.

+ +
+public void (subtypes(Target0 || Target1)).accept(Visitor v) {
+    v.visit(this);
+}
+==>
+public void (Target0+ || Target1+).accept(Visitor v) {
+    v.visit(this);
+}
+
+ +

Advice

+ +

Moving the return type of around

+ +

The returns keyword is no longer used for around advice. Instead, +the return type is declared as it is for methods. So,

+ +
+around(Point p) returns void: setters(p) { ... }
+==>
+void around(Point p): setter(p) { ... }
+
+ +

Adding a throws clause to around

+ +

Around advice must now declare the checked exceptions it throws +with a throws clause, much like a method. +

+ +
+char around(char c) throws java.io.CharConversionException: converter(c) {
+    char result;
+    try { result = proceed(); }
+    catch (Exception e) {
+        throw new java.io.CharConversionException();
+    }
+    if (result == 0) throw new java.io.CharConversionException();
+    return result;
+}
+
+ +

Understanding advice precedence

+ +

In previous versions of AspectJ, advice precedence within an +aspect was simple: if a piece of advice appeared before another piece, +it was more precedent. This made perfect sense for +before and around advice, but was the cause +of confusion (even among the AspectJ designers, more than once) for +after advice, as it seemed backward.

+ +

In addition, advice was ordered by kind, in that around advice +always surrounded before and after advice. +

+ +

AspectJ 1.0 has changed this; precedence for after +advice is inverted, and advice is no longer ordered by kind. +

+ +

This won't matter to you unless you write pieces of advice in the +same aspect that apply to the same join point.

+ +

If you do, here's what to think about: If you're looking at two +pieces of advice and want to know which has precedence, if either is +after advice, then the second one has precedence. +Otherwise, the first does.

+ +

This allows interesting advice interaction. In the following +advice, for example, the after throwing advice will catch +the exception thrown by the before advice

+ +
+aspect A {
+    before(): call(void main(..)) {
+        throw new RuntimeException();
+    }
+    after() throwing(RuntimeException e): call(void main(..)) {
+         System.err.println("caught you!");
+    }
+}
+
+ +

But reversing the order will give the before advice +more precedence, making its exception uncatchable by the after +throwing advice +

+ +
+aspect A {
+    after() throwing(RuntimeException e): call(void main(..)) {
+         System.err.println("missed you!");
+    }
+    before(): call(void main(..)) {
+        throw new RuntimeException();
+    }
+}
+
+ +

Advice in different aspects is ordered by the normal aspect +precedence rules of subtyping and the dominates modifier. +

+ +

Fixing after returning

+ +

If you use after returning advice and do not need to expose the +return value, you no longer need to write an empty set of parentheses +to indicate that fact. So,

+ +
+after(Formals) returning (): Pointcut { ... }
+==>
+after(Formals) returning: Pointcut { ... }
+
+ +

The same syntax is now available for after throwing advice, in +case you do not care what Throwable is thrown. +

+ +
+after(Formals) throwing: Pointcut { ... }
+
+ +

Renaming thisStaticJoinPoint

+ +

thisStaticJoinPoint has been renamed +thisJoinPointStaticPart, to reflect that it is now +exactly the static part of thisJoinPoint: It will return +the same object as thisJoinPoint.getStaticPart().

+ +

Converting access to thisJoinPoint

+ +

The JoinPoint object hierarchy has been folded into a +single class, org.aspectj.lang.JoinPoint. A common +pattern in logging, for example, was

+ +
+before() executions(* myMethod()) {
+    ExecutionJoinPoint jp = (ExecutionJoinPoint)thisJoinPoint;
+    CodeSignature jp = (CodeSignature)jp.getSignature();
+    System.err.println(jp.getParameters());
+    System.err.println(jp.getParameterNames());
+}    
+
+ +

While there is still a rich hierarchy for signatures, there is +only one JoinPoint type, so this can be rewritten as: +

+ +
+before() executions(* myMethod()) {
+    JoinPoint jp = thisJoinPoint;
+    CodeSignature jp = (CodeSignature)jp.getSignature();
+    System.err.println(jp.getArgs());
+    System.err.println(jp.getParameterNames());
+}    
+
+ +

Some of the method names of JoinPoint have been +reorganized, as well.

+ +

Introduction and static crosscutting

+ +

Removing +implements and +extends

+ +

The keywords +implements and +extends no +longer exist. Instead, AspectJ uses the declare +form for exactly the same functionality.

+ +
+Point +implements Serializable;
+=> 
+declare parents: Point implements Serializable;
+
+ +
+MyButton +extends ButtonAdaptor;
+=> 
+declare parents: MyButton extends ButtonAdaptor;
+
+ +

Using declare soft

+ +

Around advice advice no longer effects the static exception +checking of Java. This means that the following code previously +compiled:

+ +
+class C {
+    void noExceptionDeclared() {
+        exceptionDeclared();
+    }
+    void exceptionDeclared() throws IOException {}
+}
+aspect A {
+    around(): call(void C.exceptionDeclared()) {
+        try { proceed(); }
+        catch (IOException e) {}
+    }
+}
+
+ +

even though the class C is not compilable on its own (because +noExceptionDeclared actually throws an Exception). +

+ +

AspectJ now firmly places everything that affects the type system +of Java, including the declared-exception checking system, into the +space of introduction and declare. So, in order to state that the +call to exceptionDeclared() will not, actually, throw an exception, we +now "soften" that exception, that is, take it out of the space of +declared exceptions.

+ +
+declare soft: ExceptionType: Pointcut;
+
+ +

The pointcuts allowed here are limited; you cannot use pointcuts +that would require runtime information. But picking out method calls +is just fine. So in order to make the above example work, one new +declaration is needed: +

+ +
+declare soft: IOException:
+    call(void C.exceptionDeclared()) &&
+    withincode(void noExceptionDeclared());
+
+ +

Aspects

+ +

The syntax of "of each" modifiers has changed. For of +eachcflow and of eachcflowbelow, you can simply +replace "of each" with "per". So,

+ +
+aspect A of eachcflow(...) { ... }
+==>
+aspect A percflow(...) { ... }
+
+ +

If you have any aspects defined of eachJVM(), then +you should either remove that declaration entirely (because this is +the default behaviour), or replace the of eachJVM() +declaration with an issingleton declaration. +

+ +
+aspect of eachJVM() { ... }
+==>
+aspect A { ... }
+or
+aspect A issingleton { ... }
+
+ +

The of eachobject(Pointcut) modifier has +been split into two different forms, of +perthis(Pointcut) and of +pertarget(Pointcut). Which one you replace with +depends on the Pointcut you use. +

+ +

If you use a pointcut that picked out reception join points, then +use pertarget, and rewrite the pointcut to pick out call +join points. So +

+ +
+aspect Shadow
+        of eachobject(receptions(void Point.setX(int)) ||
+                      receptions(void Point.setY(int))) {
+    ...
+}
+==>
+aspect Shadow pertarget(call(void Point.setX(int)) ||
+                        call(void Point.setY(int))) {
+    ...
+}
+
+ +

Otherwise, in most cases, use perthis. When you +convert, remember the meaning of each of these modifiers. +perthis(Pointcut) indicates that an instance +of the aspect should be associated with every object that is +this at each of the join points picked out by +Pointcut, while pertarget(Pointcut) +associates with every object that is the target object at such join +points.

+ + + + + +
+ +

Porting pre-0.8beta3 code

+ + + + +

The following changes are only required when porting code written +prior to the 0.8beta3 release of AspectJ.

+ +

Changing cflow terminology

+ +

Changing pre-0.8beta3 code that uses AspectJ's control-flow-based +features only requires rewriting occurrences of +eachcflowroot, cflow, and +cflowtop. No editing of other aspect code is +necessary.

+ +

eachcflowroot

+ +

The aspect modifier "of +eachcflowroot(Pointcut)" should now be written more +as "percflow(Pointcut)".

+ +

cflow

+ +

In previous versions of AspectJ, the pointcut +cflow(Pointcut) picked out all join points in +the cflow below the join points of Pointcut. That is, it +did not include the join points of Pointcut, only the join +points in their control flow. +

+ +

As of version 0.8beta3, +cflowbelow(Pointcut) has that behavior. +cflow(Pointcut) includes the join points of +Pointcut.

+ +

In many cases, you may not care whether the points of +Pointcut are included or not, and so can safely leave +cflow(Pointcut) pointcut designators alone. +However, if you use the idiom +

+ +
+Pointcut && ! cflow(Pointcut)
+
+ +

to capture the non-recursive entries to a particular pointcut, you +will definitely want to rewrite that as +

+ +
+Pointcut && ! cflowbelow(Pointcut)
+
+ +

cflowtop

+ +

The primitive pointcut designator +cflowtop(Pointcut) has been removed from the +language, as it is expressible with cflow or +cflowbelow. All uses of +cflowtop(Pointcut) can be rewritten as: +

+ +
+cflowbelow(Pointcut && ! cflowbelow(Pointcut))
+
+ +

Though in most cases the following is sufficient +

+ +
+cflow(Pointcut && ! cflowbelow(Pointcut))
+
+ +

Overriding abstract pointcuts

+ +

In previous versions of AspectJ, a concrete aspect would +implicitly override all of its abstract pointcuts with an empty +pointcut. AspectJ 0.8beta3 enforces the restriction that a concrete +aspect may not have any abstract pointcuts. Thus the following +extension:

+ +
+abstract aspect A {
+    abstract pointcut pc();
+}
+
+aspect B {}
+
+ +

will no longer compile. +

+ +

Adding the new empty pointcut designator +

+ +
+pointcut Id();
+
+ +

in the declaration of the concrete aspect fixes this problem. +

+ +
+abstract aspect A {
+    abstract pointcut pc();
+}
+
+aspect B {
+    pointcut pc();
+}
+
+ +

Limiting recursive advice

+ +

Previously, the compiler silently refrained from applying a piece +of advice to join points within its own advice body. So, for example, +in

+ +
+class C {
+    static int i;
+}
+
+aspect A {
+    before(): gets(int C.i) {
+        System.err.println("C.i was " + C.i)
+    }
+}
+
+ +

The advice would trace all references of the static field +C.i except those in the body of the before.

+ +

The compiler has now removed this special case, and so running the +above example will now cause a StackOverflowException to +be thrown.

+ +

Most cases of this error can be fixed by correctly specifying the +desired pointcut: In the above example, the intention is clearly not +to trace all references of C.i, just those +outside the aspect. +

+ +
+class C {
+    static int i;
+}
+
+aspect A {
+    before(): get(int C.i) && ! within(A) {
+        System.err.println("C.i was " + C.i)
+    }
+}
+
+ +

In a very few cases, you may want the advice to be applicable to +other code in the aspect, but not in the particular piece of advice. +In such cases, you can pull the body of the advice into a method and +restrict away from that method (and away from calls to that method): +

+ +
+class C {
+    static int i;
+}
+
+aspect A {
+    public static int getCi() {
+        return C.i;                          // will be traced
+    }
+
+    before(): get(int C.i) &&
+              ! withincode(void A.traceCi())
+              ! call(void A.traceCi())      {
+        traceCi();
+    }
+    private void traceCi() {
+        System.err.println("C.i was " + C.i) // will not be traced
+    }
+}
+
+ + + + +
+

Porting pre-0.8beta1 code

+ + + +

The following changes are only required when porting code written +prior to the 0.8beta1 release of AspectJ.

+ +

Rewriting introductions

+ +

Syntax

+ +

The syntax of introduction has changed. Porting most programs +should require some simple editing. Anywhere you have an introduction +block

+ +
+introduction GTN {
+    ...
+}
+
+ +

simply move the GTN down into the introduction +declarations and remove the block.

+ +

For method introduction, place the GTN in front of the +method name, For field introduction, place the GTN in front +of the field name, and for constructor introduction, place the +GTN in front of the new identifier.

+ +
+introduction Foo {
+    public void doStuff() { this.doStuffLater(); }
+    public int calorieCount = 3;
+    public new(int x) { super(); calorieCount = x; }
+}
+
+==>
+
+public void Foo.doStuff() { this.doStuffLater(); }
+public int Foo.calorieCount= 3;
+public Foo.new(int x) { super(); calorieCount = x; }
+
+ +

For implements and extends introduction, move the GTN +in front of the new identifiers implements or +extends, and place that in a declare parents +form. +

+ +
+introduction Foo {
+    implements Comparable;
+    extends Goo;
+}
+
+==>
+
+declare parents: Foo implements Comparable;
+declare parents: Foo extends Goo;
+
+ +

In all cases, if the GTN is just a type name, it can be +moved down on its own. However, if the GTN uses any of +&&, ||, and !, it must +be parenthesized.

+ +
+introduction subtypes(Foo) && !Goo {
+    int x;
+}
+
+==>
+
+int (Foo+ && !Goo).x;
+
+ + +

Access

+ +

If you had an introduction that was referring to private or +protected members of the target class, this will no longer work. You +will either need to modify your code to avoid this accessibility +issue, or you will need to use the privileged modifier on +the aspect that contains the introduction.

+ +
+class Counter {
+    private int count = 2;
+}
+
+aspect ExposeCountersPrivates {
+    introduction Counter {
+        public int getCount() { return count; }
+    }
+}
+
+==>
+// in 0.8, only privileged aspects can expose a class's privates
+privileged aspect ExposeCountersPrivates {
+    public int Counter.getCount() { return count; }
+}
+
+ + +

If you have introduced private or package-protected members, you +will probably have to re-write some code. Most previous uses of +introducing privates can be improved by using private introduction +instead.

+ +
+class C {
+}
+
+aspect AddCounter {
+    introduction C {
+        private int count;
+        public int getCount() { return count; }
+    }
+}
+
+==>
+aspect AddCounter {
+    private int Counter.count;
+    public int Counter.getCount() { return count; }
+}
+
+ +

There is one case that we know of where the inability to perform +the introduction of private members makes 0.7 code difficult to +port to 0.8. If you were using the introduction of a private +void writeObject(..) or a private void +readObject(..) method to interact with Java's serialization +API, you will need to come up with an alternative design. Using some +combination of Externalizable, +writeReplace(..) and/or readResolve(..) +methods should allow you to port your code. If you find this isn't +the case, we'd like to hear about it. + + +

If you were introducing either a protected member or a +package-private member onto a class in order to override a protected +member that was inherited from a superclass, you will have to make +this introduction public.

+ + +

Removing static advice

+ +

Static advice has been removed from the language. Now, every +piece of advice is non-static, meaning that it will run in the context +of an aspect instance. +

+ +

If you have an aspect that only contains static advice, has no +"of" clause or is declared "of eachJVM()", and is not extended by +another aspect, simply remove the keyword "static" from all pieces of +advice, and make sure the aspect is not defined with the "abstract" +modifier.

+ +
+aspect Tracing {
+    static before(): executions(* *(..)) {
+        System.out.println("Got Here! " + thisJoinPoint);
+    }
+}
+
+==>
+
+aspect Tracing {
+    before(): execution(* *(..)) {
+        System.out.println("Got Here! " + thisJoinPoint);
+    }
+}
+
+ +

Otherwise, if you have an aspect contains both static and +non-static advice, is extended, or is "of eachObject(...)" or "of +eachcflowroot(...)", you should group your static advice together and +put it in a new aspect, possibly even an inner aspect.

+ +
+aspect ComplexTracing of eachobject(cflow(executions(void Main.main(..)))) {
+    static before(): executions(* *(..)) {
+        System.out.println("Got Here! " + thisJoinPoint);
+    }
+    static after(): executions(* *(..)) {
+        System.out.println("Returned! " + thisJoinPoint);
+    }
+
+    // some other dynamic advice, fields, etc
+}
+
+==>
+
+aspect ComplexTracing of eachobject(cflow(executions(void Main.main(..)))) {
+    static aspect AlwaysTracing {
+        before(): execution(* *(..)) {
+            System.out.println("Got Here! " + thisJoinPoint);
+        }
+        after(): execution(* *(..)) {
+            System.out.println("Returned! " + thisJoinPoint);
+        }
+    }
+
+    // some other dynamic advice, fields, etc
+}
+
+ +

Fixing aspect-aspect inheritance

+ +

Aspects can now only extend abstract aspects. This restriction +may cause some redesign of aspect hierarchies. You will probably find +that for the majority of your code the most serious change this +requires is to add an explicit abstract modifier to a +super-aspect that was already implicitly abstract.

+ +
+aspect BaseTracing {
+    abstract pointcut traced();
+    before(): traced() {
+        System.out.println("Got Here! " + thisJoinPoint);
+    }
+}
+
+==>
+
+// make this abstract aspect explicitly abstract
+abstract aspect BaseTracing {
+    ...
+}
+
+ + +

This change has also affected the getAspect static +method. Now, getAspect is only defined on non-abstract +aspects. Previously, you could call getAspect on an +abstract superaspect and (sometimes) get an instance of a subaspect +back.

+ +

This pattern was used in the Spacewar example in the AspectJ +distribution. We had the class hierarchy

+ +
+  SpaceObject (abstract)
+    |- Ship
+    |- Bullet
+    |- EnergyPellet
+
+ +

And the aspect hierarchy +

+ +
+  SpaceObjectDA (abstract)
+    |- ShipDA of eachobject(instanceof(Ship))
+    |- BulletDA of eachobject(instanceof(Ship))
+    |- EnergyPacketDA of eachobject(instanceof(Ship))
+
+ +

And we would call SpaceObjectDA.getAspect(SpaceObject) to access +the aspect associated with a ship, bullet, or energy pellet. This +pattern depended on the SpaceObjectDA aspect hierarchy +exactly mirroring the SpaceObject hierarchy, and being +maintained that way.

+ +

A better way to implement this kind of design aspect is to use +private introduction, a new feature of AspectJ. +

+ +

Using private introduction

+ +

A common pattern for AspectJ programs that need to associate some +state with every object of a particular type has been to use aspects +that are defined of eachobject(instanceof(...)). A prime +example of this was the BoundPoint aspect of the bean +example: which needed to associate each point with a +PropertyChangeSupport object.

+ +
+aspect BoundPoint of eachobject(instanceof(Point)) {
+
+    java.beans.PropertyChangeSupport support = null;
+
+    after() returning(Point p): receptions(p.new(..)){
+        support = new PropertyChangeSupport(myPoint);
+    }
+
+    around(Point p) returns void: receptions(void p.set*(*)) {
+        // code that uses support
+    }
+}
+
+ +

In the new version of AspectJ, a better way of accomplishing many +of these state association is to use privately introduced fields. +Instead of creating an aspect instance for every Point +object, store the PropertyChagneSupport object in the +Point objects themselves. +

+ +
+aspect BoundPoint {
+    private PropertyChangeSupport Point.support = new PropertyChangeSupport(this);
+
+    void around(Point p): setters(p) {
+        // code that uses p.support
+    }
+}
+
+ +

Just as in the past, the PropertyChangeSupport object is not +accessable to anyone but the aspect, but now less mechanism is needed. +

+ +

There are times when changing aspects that are defined of +eachobject(instanceof(...)) may not be reasonable. If the +aspect instance is stored or passed to other methods, then having a +real of eachobject(instanceof(...)), now written +perthis(this(...)), association may capture the +crosscutting concern best.

+ + + +
+

Porting pre-0.7beta11 code

+ + + +

The following changes are only required when porting code written +prior to the 0.7beta11 release of AspectJ.

+ +

Removing two-argument calls

+ +

In AspectJ 0.7beta11, the two-argument calls +primitive pointcut designator was deprecated. Removing these +designators will require different cases depending on what the +original pointcut did.

+ +

Calls to static methods

+ +

For pointcuts denoting calls to particular static methods, such as +

+ +
+calls(String, static String valueOf(int)) // deprecated
+
+ +

the transformation is easy. Simply make the desired signature +explicit. Instead of catching all calls to any static method that +happens to have the signature String valueOf(int), catch +calls to that exact method defined in the String class.

+ +
+call(static String String.valueOf(int))
+
+ +

Pointcuts denoting calls to classes of static methods can also be +rewritten with these rules. For example,

+ +
+calls(my.package.*, static * get*(..)) // deprecated
+
+ +

should now be written

+ +
+call(static * my.package.*.get*(..))
+
+ +

Calls to non-static methods

+ +

Many pointcuts denoting calls to non-static methods can be +fixed the same way that those pointcuts denoting calls to static +methods are fixed. So, +

+ +
+calls(Thread, int getPriority()) // deprecated
+
+ +

which denotes all calls to nullary int methods named getPriority +when the called object is an instance of the Thread type, +can almost always be rewritten

+ +
+call(int Thread.getPriority())
+
+ +

which denotes all calls to the nullary int Thread.getPriority() +method. +

+ +

Expanding the signature picks out slightly different join points +than the original two-argument form. This won't matter for most +programs, but in some cases the differences may be noticable. In +particular, the expanded-signature form only picks out those calls +where the called object is statically typed to Thread +when its int getPriority() method is called. If you want +to capture calls to the int Thread.getPriority() method, +regardless of how the called object is statically typed, you shoud use +the different translation:

+ +
+call(int getPriority()) && target(Thread)
+
+ +

This will capture all call join points of methods with signature +int Thread.getPriority().

+ +

It will also denote any join points if the Thread type does not +define (possibly abstractly) some int getPriority() +method, though.

+ + +

Removing advice from Class declarations

+ +

The simplest way to remove an advice declaration from a class is +to simply define the advice declaration in an inner aspect. So, +instead of

+ +
+class C {
+    static before(): executions(C.new()) { ... } // deprecated
+}
+
+ +

write

+ +
+class C {
+    static aspect ConstructionProtocol {
+        static before(): executions(C.new()) { ... }
+    }
+}
+
+ +

If your advice doesn't refer to any inner classes or interfaces of +C, you can move the inner aspect out of the class entirely.

+ +
+class C { ... }
+
+aspect ConstructionProtocol {
+    static before(): execution(C.new()) { ... }
+}
+
+ +

Your code will be clearer if you consider the purpose of each +piece of advice when you make this change. It may be that some of the +advice naturally belongs to another aspect, perhaps already existing. +Or it may be that some pieces of advice in a class are associated to +one concern and some to another; in which case more than aspect would +be appropriate.

+ + +
+

Porting pre-0.7beta10 code

+ + + +

The following changes are only required when porting code written +prior to the 0.7beta10 release of AspectJ.

+ + +

Changing access to thisJoinPoint

+ +

In AspectJ 0.7beta10, access to the reflective object +thisJoinPoint substantially changed. The two parts of +this change were the elimination of the runNext() static +method, and the use of an interface hierarchy represent the join point +object.

+ +

thisJoinPoint.runNext() to +proceed()

+ +

The elimination of the runNext() static method +requires almost no porting work. An automatic replacement of the +string +

+ +
thisJoinPoint.runNext
+ +

with the string +

+ +
proceed
+ +

will do the job. However, if any around advice used the +identifier "proceed" as a formal parameter or local +variable, it must be renamed, and if any aspect used it as a field, +then references to the field in around advice should be made explicit +(prefixing the reference with the aspect name or "this", +depending on whether the field is static or not).

+ +

Using thisJoinPoint

+ +

While access to reflective information through +thisJoinPoint is more powerful and regular through its +interface hierarchy, the previous uses must be rewritten. Changing +your code will likely require manual editing, but in doing so your +code should get simpler and cleaner.

+ + + +

Many existing uses of the fields on join points can be re-written +to use one of: +

+ + + +

For example: +

+ +
+System.out.println(thisJoinPoint.className + "." +
+                   thisJoinPoint.methodName)
+
+ +

can be replaced with +

+ +
System.out.println(thisJoinPoint)
+ +

or +

+ +
System.out.println(thisJoinPoint.getSignature().toShortString())
+ +

with comparable behavior. +

+ + + +

Accesses to the parameters field of join points should be changed +as follows. A field access like: +

+ + +
thisJoinPoint.parameters
+ +

must be changed to: +

+ + + + +

Accesses to the methodName and className fields of join points +that are not suitable for replacement with a toString method, +should be changed as follows. Field accesses like: +

+ + + +

must be changed to: +

+ + + + + +

Accessses to the parameterNames and parameterTypes fields of +join points, that are not suitable for conversion to one of the +toString() methods should be changed as follows. Field access +like: +

+ + + +

must be changed to: +

+ + + + + -- cgit v1.2.3