diff options
Diffstat (limited to 'docs/progGuideDB/semantics.adoc')
-rw-r--r-- | docs/progGuideDB/semantics.adoc | 2618 |
1 files changed, 2618 insertions, 0 deletions
diff --git a/docs/progGuideDB/semantics.adoc b/docs/progGuideDB/semantics.adoc new file mode 100644 index 000000000..5a3e7fb00 --- /dev/null +++ b/docs/progGuideDB/semantics.adoc @@ -0,0 +1,2618 @@ +[[semantics]] +== Language Semantics + +[[semantics-intro]] +=== Introduction + +AspectJ extends Java by overlaying a concept of join points onto the +existing Java semantics and adding a few new program elements to Java: + +A join point is a well-defined point in the execution of a program. +These include method and constructor calls, field accesses and others +described below. + +A pointcut picks out join points, and exposes some of the values in the +execution context of those join points. There are several primitive +pointcut designators, and others can be named and defined by the +`pointcut` declaration. + +A piece of advice is code that executes at each join point in a +pointcut. Advice has access to the values exposed by the pointcut. +Advice is defined by `before`, `after`, and `around` declarations. + +Inter-type declarations form AspectJ's static crosscutting features, +that is, is code that may change the type structure of a program, by +adding to or extending interfaces and classes with new fields, +constructors, or methods. Some inter-type declarations are defined +through an extension of usual method, field, and constructor +declarations, and other declarations are made with a new `declare` +keyword. + +An aspect is a crosscutting type that encapsulates pointcuts, advice, +and static crosscutting features. By type, we mean Java's notion: a +modular unit of code, with a well-defined interface, about which it is +possible to do reasoning at compile time. Aspects are defined by the +`aspect` declaration. + +[[semantics-joinPoints]] +=== Join Points + +While aspects define types that crosscut, the AspectJ system does not +allow completely arbitrary crosscutting. Rather, aspects define types +that cut across principled points in a program's execution. These +principled points are called join points. + +A join point is a well-defined point in the execution of a program. The +join points defined by AspectJ are: + +Method call:: + When a method is called, not including super calls of non-static + methods. +Method execution:: + When the body of code for an actual method executes. +Constructor call:: + When an object is built and that object's initial constructor is + called (i.e., not for "super" or "this" constructor calls). The object + being constructed is returned at a constructor call join point, so its + return type is considered to be the type of the object, and the object + itself may be accessed with + + + after returning + + + advice. +Constructor execution:: + When the body of code for an actual constructor executes, after its + this or super constructor call. The object being constructed is the + currently executing object, and so may be accessed with the + + + this + + + pointcut. The constructor execution join point for a constructor that + calls a super constructor also includes any non-static initializers of + enclosing class. No value is returned from a constructor execution + join point, so its return type is considered to be void. +Static initializer execution:: + When the static initializer for a class executes. No value is returned + from a static initializer execution join point, so its return type is + considered to be void. +Object pre-initialization:: + Before the object initialization code for a particular class runs. + This encompasses the time between the start of its first called + constructor and the start of its parent's constructor. Thus, the + execution of these join points encompass the join points of the + evaluation of the arguments of + + + this() + + + and + + + super() + + + constructor calls. No value is returned from an object + pre-initialization join point, so its return type is considered to be + void. +Object initialization:: + When the object initialization code for a particular class runs. This + encompasses the time between the return of its parent's constructor + and the return of its first called constructor. It includes all the + dynamic initializers and constructors used to create the object. The + object being constructed is the currently executing object, and so may + be accessed with the + + + this + + + pointcut. No value is returned from a constructor execution join + point, so its return type is considered to be void. +Field reference:: + When a non-constant field is referenced. [Note that references to + constant fields (static final fields bound to a constant string object + or primitive value) are not join points, since Java requires them to + be inlined.] +Field set:: + When a field is assigned to. Field set join points are considered to + have one argument, the value the field is being set to. No value is + returned from a field set join point, so its return type is considered + to be void. [Note that the initializations of constant fields (static + final fields where the initializer is a constant string object or + primitive value) are not join points, since Java requires their + references to be inlined.] +Handler execution:: + When an exception handler executes. Handler execution join points are + considered to have one argument, the exception being handled. No value + is returned from a field set join point, so its return type is + considered to be void. +Advice execution:: + When the body of code for a piece of advice executes. + +Each join point potentially has three pieces of state associated with +it: the currently executing object, the target object, and an object +array of arguments. These are exposed by the three state-exposing +pointcuts, `this`, `target`, and `args`, respectively. + +Informally, the currently executing object is the object that a `this` +expression would pick out at the join point. The target object is where +control or attention is transferred to by the join point. The arguments +are those values passed for that transfer of control or attention. + +[cols=",,,",options="header",] +|=== +|*Join Point* |*Current Object* |*Target Object* |*Arguments* +|Method Call |executing object* |target object** |method arguments + +|Method Execution |executing object* |executing object* |method +arguments + +|Constructor Call |executing object* |None |constructor arguments + +|Constructor Execution |executing object |executing object |constructor +arguments + +|Static initializer execution |None |None |None + +|Object pre-initialization |None |None |constructor arguments + +|Object initialization |executing object |executing object |constructor +arguments + +|Field reference |executing object* |target object** |None + +|Field assignment |executing object* |target object** |assigned value + +|Handler execution |executing object* |executing object* |caught +exception + +|Advice execution |executing aspect |executing aspect |advice arguments +|=== + +* There is no executing object in static contexts such as static method +bodies or static initializers. + +** There is no target object for join points associated with static +methods or fields. + +[[semantics-pointcuts]] +=== Pointcuts + +A pointcut is a program element that picks out join points and exposes +data from the execution context of those join points. Pointcuts are used +primarily by advice. They can be composed with boolean operators to +build up other pointcuts. The primitive pointcuts and combinators +provided by the language are: + +`call(MethodPattern)`:: + Picks out each method call join point whose signature matches + + + MethodPattern + + + . +`execution(MethodPattern)`:: + Picks out each method execution join point whose signature matches + + + MethodPattern + + + . +`get(FieldPattern)`:: + Picks out each field reference join point whose signature matches + + + FieldPattern + + + . [Note that references to constant fields (static final fields bound + to a constant string object or primitive value) are not join points, + since Java requires them to be inlined.] +`set(FieldPattern)`:: + Picks out each field set join point whose signature matches + + + FieldPattern + + + . [Note that the initializations of constant fields (static final + fields where the initializer is a constant string object or primitive + value) are not join points, since Java requires their references to be + inlined.] +`call(ConstructorPattern)`:: + Picks out each constructor call join point whose signature matches + + + ConstructorPattern + + + . +`execution(ConstructorPattern)`:: + Picks out each constructor execution join point whose signature + matches + + + ConstructorPattern + + + . +`initialization(ConstructorPattern)`:: + Picks out each object initialization join point whose signature + matches + + + ConstructorPattern + + + . +`preinitialization(ConstructorPattern)`:: + Picks out each object pre-initialization join point whose signature + matches + + + ConstructorPattern + + + . +`staticinitialization(TypePattern)`:: + Picks out each static initializer execution join point whose signature + matches + + + TypePattern + + + . +`handler(TypePattern)`:: + Picks out each exception handler join point whose signature matches + + + TypePattern + + + . +`adviceexecution()`:: + Picks out all advice execution join points. +`within(TypePattern)`:: + Picks out each join point where the executing code is defined in a + type matched by + + + TypePattern + + + . +`withincode(MethodPattern)`:: + Picks out each join point where the executing code is defined in a + method whose signature matches + + + MethodPattern + + + . +`withincode(ConstructorPattern)`:: + Picks out each join point where the executing code is defined in a + constructor whose signature matches + + + ConstructorPattern + + + . +`cflow(Pointcut)`:: + Picks out each join point in the control flow of any join point + + + P + + + picked out by + + + Pointcut + + + , including + + + P + + + itself. +`cflowbelow(Pointcut)`:: + Picks out each join point in the control flow of any join point + + + P + + + picked out by + + + Pointcut + + + , but not + + + P + + + itself. +`this(Type or Id)`:: + Picks out each join point where the currently executing object (the + object bound to + + + this + + + ) is an instance of + + + Type + + + , or of the type of the identifier + + + Id + + + (which must be bound in the enclosing advice or pointcut definition). + Will not match any join points from static contexts. +`target(Type or Id)`:: + Picks out each join point where the target object (the object on which + a call or field operation is applied to) is an instance of + + + Type + + + , or of the type of the identifier + + + Id + + + (which must be bound in the enclosing advice or pointcut definition). + Will not match any calls, gets, or sets of static members. +`args(Type or Id, ...)`:: + Picks out each join point where the arguments are instances of the + appropriate type (or type of the identifier if using that form). A + + + null + + + argument is matched iff the static type of the argument (declared + parameter type or field type) is the same as, or a subtype of, the + specified args type. +`PointcutId(TypePattern or Id, ...)`:: + Picks out each join point that is picked out by the user-defined + pointcut designator named by + + + PointcutId + + + . +`if(BooleanExpression)`:: + Picks out each join point where the boolean expression evaluates to + + + true + + + . The boolean expression used can only access static members, + parameters exposed by the enclosing pointcut or advice, and + + + thisJoinPoint + + + forms. In particular, it cannot call non-static methods on the aspect + or use return values or exceptions exposed by after advice. +`! Pointcut`:: + Picks out each join point that is not picked out by + + + Pointcut + + + . +`Pointcut0 && Pointcut1`:: + Picks out each join points that is picked out by both + + + Pointcut0 + + + and + + + Pointcut1 + + + . +`Pointcut0 || Pointcut1`:: + Picks out each join point that is picked out by either pointcuts. + + + Pointcut0 + + + or + + + Pointcut1 + + + . +`( Pointcut )`:: + Picks out each join points picked out by + + + Pointcut + + + . + +==== Pointcut definition + +Pointcuts are defined and named by the programmer with the `pointcut` +declaration. + +.... +pointcut publicIntCall(int i): + call(public * *(int)) && args(i); +.... + +A named pointcut may be defined in either a class or aspect, and is +treated as a member of the class or aspect where it is found. As a +member, it may have an access modifier such as `public` or `private`. + +.... +class C { + pointcut publicCall(int i): + call(public * *(int)) && args(i); +} + +class D { + pointcut myPublicCall(int i): + C.publicCall(i) && within(SomeType); +} +.... + +Pointcuts that are not final may be declared abstract, and defined +without a body. Abstract pointcuts may only be declared within abstract +aspects. + +.... +abstract aspect A { + abstract pointcut publicCall(int i); +} +.... + +In such a case, an extending aspect may override the abstract pointcut. + +.... +aspect B extends A { + pointcut publicCall(int i): call(public Foo.m(int)) && args(i); +} +.... + +For completeness, a pointcut with a declaration may be declared `final`. + +Though named pointcut declarations appear somewhat like method +declarations, and can be overridden in subaspects, they cannot be +overloaded. It is an error for two pointcuts to be named with the same +name in the same class or aspect declaration. + +The scope of a named pointcut is the enclosing class declaration. This +is different than the scope of other members; the scope of other members +is the enclosing class _body_. This means that the following code is +legal: + +.... +aspect B percflow(publicCall()) { + pointcut publicCall(): call(public Foo.m(int)); +} +.... + +==== Context exposure + +Pointcuts have an interface; they expose some parts of the execution +context of the join points they pick out. For example, the PublicIntCall +above exposes the first argument from the receptions of all public unary +integer methods. This context is exposed by providing typed formal +parameters to named pointcuts and advice, like the formal parameters of +a Java method. These formal parameters are bound by name matching. + +On the right-hand side of advice or pointcut declarations, in certain +pointcut designators, a Java identifier is allowed in place of a type or +collection of types. The pointcut designators that allow this are +`this`, `target`, and `args`. In all such cases, using an identifier +rather than a type does two things. First, it selects join points as +based on the type of the formal parameter. So the pointcut + +.... +pointcut intArg(int i): args(i); +.... + +picks out join points where an `int` (or a `byte`, `short`, or `char`; +anything assignable to an `int`) is being passed as an argument. Second, +though, it makes the value of that argument available to the enclosing +advice or pointcut. + +Values can be exposed from named pointcuts as well, so + +.... +pointcut publicCall(int x): call(public *.*(int)) && intArg(x); +pointcut intArg(int i): args(i); +.... + +is a legal way to pick out all calls to public methods accepting an int +argument, and exposing that argument. + +There is one special case for this kind of exposure. Exposing an +argument of type Object will also match primitive typed arguments, and +expose a "boxed" version of the primitive. So, + +.... +pointcut publicCall(): call(public *.*(..)) && args(Object); +.... + +will pick out all unary methods that take, as their only argument, +subtypes of Object (i.e., not primitive types like `int`), but + +.... +pointcut publicCall(Object o): call(public *.*(..)) && args(o); +.... + +will pick out all unary methods that take any argument: And if the +argument was an `int`, then the value passed to advice will be of type +`java.lang.Integer`. + +The "boxing" of the primitive value is based on the _original_ primitive +type. So in the following program + +.... +public class InstanceOf { + + public static void main(String[] args) { + doInt(5); + } + + static void doInt(int i) { } +} + +aspect IntToLong { + pointcut el(long l) : + execution(* doInt(..)) && args(l); + + before(Object o) : el(o) { + System.out.println(o.getClass()); + } +} +.... + +The pointcut will match and expose the integer argument, but it will +expose it as an `Integer`, not a `Long`. + +==== Primitive pointcuts + +===== Method-related pointcuts + +AspectJ provides two primitive pointcut designators designed to capture +method call and execution join points. + +* call( ++ +MethodPattern ++ +) +* execution( ++ +MethodPattern ++ +) + +===== Field-related pointcuts + +AspectJ provides two primitive pointcut designators designed to capture +field reference and set join points: + +* get( ++ +FieldPattern ++ +) +* set( ++ +FieldPattern ++ +) + +All set join points are treated as having one argument, the value the +field is being set to, so at a set join point, that value can be +accessed with an `args` pointcut. So an aspect guarding a static integer +variable x declared in type T might be written as + +.... +aspect GuardedX { + static final int MAX_CHANGE = 100; + before(int newval): set(static int T.x) && args(newval) { + if (Math.abs(newval - T.x) > MAX_CHANGE) + throw new RuntimeException(); + } +} +.... + +===== Object creation-related pointcuts + +AspectJ provides primitive pointcut designators designed to capture the +initializer execution join points of objects. + +* call( ++ +ConstructorPattern ++ +) +* execution( ++ +ConstructorPattern ++ +) +* initialization( ++ +ConstructorPattern ++ +) +* preinitialization( ++ +ConstructorPattern ++ +) + +===== Class initialization-related pointcuts + +AspectJ provides one primitive pointcut designator to pick out static +initializer execution join points. + +* staticinitialization( ++ +TypePattern ++ +) + +===== Exception handler execution-related pointcuts + +AspectJ provides one primitive pointcut designator to capture execution +of exception handlers: + +* handler( ++ +TypePattern ++ +) + +All handler join points are treated as having one argument, the value of +the exception being handled. That value can be accessed with an `args` +pointcut. So an aspect used to put `FooException` objects into some +normal form before they are handled could be written as + +.... +aspect NormalizeFooException { + before(FooException e): handler(FooException) && args(e) { + e.normalize(); + } +} +.... + +===== Advice execution-related pointcuts + +AspectJ provides one primitive pointcut designator to capture execution +of advice + +* adviceexecution() + +This can be used, for example, to filter out any join point in the +control flow of advice from a particular aspect. + +.... +aspect TraceStuff { + pointcut myAdvice(): adviceexecution() && within(TraceStuff); + + before(): call(* *(..)) && !cflow(myAdvice) { + // do something + } +} +.... + +===== State-based pointcuts + +Many concerns cut across the dynamic times when an object of a +particular type is executing, being operated on, or being passed around. +AspectJ provides primitive pointcuts that capture join points at these +times. These pointcuts use the dynamic types of their objects to pick +out join points. They may also be used to expose the objects used for +discrimination. + +* this( ++ +Type ++ +or ++ +Id ++ +) +* target( ++ +Type ++ +or ++ +Id ++ +) + +The `this` pointcut picks out each join point where the currently +executing object (the object bound to `this`) is an instance of a +particular type. The `target` pointcut picks out each join point where +the target object (the object on which a method is called or a field is +accessed) is an instance of a particular type. Note that `target` should +be understood to be the object the current join point is transfering +control to. This means that the target object is the same as the current +object at a method execution join point, for example, but may be +different at a method call join point. + +* args( ++ +Type ++ +or ++ +Id ++ +or "..", ...) + +The args pointcut picks out each join point where the arguments are +instances of some types. Each element in the comma-separated list is one +of four things. If it is a type name, then the argument in that position +must be an instance of that type. If it is an identifier, then that +identifier must be bound in the enclosing advice or pointcut +declaration, and so the argument in that position must be an instance of +the type of the identifier (or of any type if the identifier is typed to +Object). If it is the "*" wildcard, then any argument will match, and if +it is the special wildcard "..", then any number of arguments will +match, just like in signature patterns. So the pointcut + +.... +args(int, .., String) +.... + +will pick out all join points where the first argument is an `int` and +the last is a `String`. + +===== Control flow-based pointcuts + +Some concerns cut across the control flow of the program. The `cflow` +and `cflowbelow` primitive pointcut designators capture join points +based on control flow. + +* cflow( ++ +Pointcut ++ +) +* cflowbelow( ++ +Pointcut ++ +) + +The `cflow` pointcut picks out all join points that occur between entry +and exit of each join point <P> picked out by <Pointcut>, including <P> +itself. Hence, it picks out the join points _in_ the control flow of the +join points picked out by <Pointcut>. + +The `cflowbelow` pointcut picks out all join points that occur between +entry and exit of each join point <P> picked out by <Pointcut>, but not +including <P> itself. Hence, it picks out the join points _below_ the +control flow of the join points picked out by <Pointcut>. + +====== Context exposure from control flows + +The `cflow` and `cflowbelow` pointcuts may expose context state through +enclosed `this`, `target`, and `args` pointcuts. + +Anytime such state is accessed, it is accessed through the _most recent_ +control flow that matched. So the "current arg" that would be printed by +the following program is zero, even though it is in many control flows. + +.... +class Test { + public static void main(String[] args) { + fact(5); + } + static int fact(int x) { + if (x == 0) { + System.err.println("bottoming out"); + return 1; + } + else return x * fact(x - 1); + } +} + +aspect A { + pointcut entry(int i): call(int fact(int)) && args(i); + pointcut writing(): call(void println(String)) && ! within(A); + + before(int i): writing() && cflow(entry(i)) { + System.err.println("Current arg is " + i); + } +} +.... + +It is an error to expose such state through _negated_ control flow +pointcuts, such as within `! + cflowbelow(P)`. + +===== Program text-based pointcuts + +While many concerns cut across the runtime structure of the program, +some must deal with the lexical structure. AspectJ allows aspects to +pick out join points based on where their associated code is defined. + +* within( ++ +TypePattern ++ +) +* withincode( ++ +MethodPattern ++ +) +* withincode( ++ +ConstructorPattern ++ +) + +The `within` pointcut picks out each join point where the code executing +is defined in the declaration of one of the types in <TypePattern>. This +includes the class initialization, object initialization, and method and +constructor execution join points for the type, as well as any join +points associated with the statements and expressions of the type. It +also includes any join points that are associated with code in a type's +nested types, and that type's default constructor, if there is one. + +The `withincode` pointcuts picks out each join point where the code +executing is defined in the declaration of a particular method or +constructor. This includes the method or constructor execution join +point as well as any join points associated with the statements and +expressions of the method or constructor. It also includes any join +points that are associated with code in a method or constructor's local +or anonymous types. + +===== Expression-based pointcuts + +* if( ++ +BooleanExpression ++ +) + +The if pointcut picks out join points based on a dynamic property. its +syntax takes an expression, which must evaluate to a boolean true or +false. Within this expression, the `thisJoinPoint` object is available. +So one (extremely inefficient) way of picking out all call join points +would be to use the pointcut + +.... +if(thisJoinPoint.getKind().equals("call")) +.... + +Note that the order of evaluation for pointcut expression components at +a join point is undefined. Writing `if` pointcuts that have side-effects +is considered bad style and may also lead to potentially confusing or +even changing behavior with regard to when or if the test code will run. + +==== Signatures + +One very important property of a join point is its signature, which is +used by many of AspectJ's pointcut designators to select particular join +points. + +===== Methods + +Join points associated with methods typically have method signatures, +consisting of a method name, parameter types, return type, the types of +the declared (checked) exceptions, and some type that the method could +be called on (below called the "qualifying type"). + +At a method call join point, the signature is a method signature whose +qualifying type is the static type used to _access_ the method. This +means that the signature for the join point created from the call +`((Integer)i).toString()` is different than that for the call +`((Object)i).toString()`, even if `i` is the same variable. + +At a method execution join point, the signature is a method signature +whose qualifying type is the declaring type of the method. + +===== Fields + +Join points associated with fields typically have field signatures, +consisting of a field name and a field type. A field reference join +point has such a signature, and no parameters. A field set join point +has such a signature, but has a has a single parameter whose type is the +same as the field type. + +===== Constructors + +Join points associated with constructors typically have constructor +signatures, consisting of a parameter types, the types of the declared +(checked) exceptions, and the declaring type. + +At a constructor call join point, the signature is the constructor +signature of the called constructor. At a constructor execution join +point, the signature is the constructor signature of the currently +executing constructor. + +At object initialization and pre-initialization join points, the +signature is the constructor signature for the constructor that started +this initialization: the first constructor entered during this type's +initialization of this object. + +===== Others + +At a handler execution join point, the signature is composed of the +exception type that the handler handles. + +At an advice execution join point, the signature is composed of the +aspect type, the parameter types of the advice, the return type (void +for all but around advice) and the types of the declared (checked) +exceptions. + +==== Matching + +The `withincode`, `call`, `execution`, `get`, and `set` primitive +pointcut designators all use signature patterns to determine the join +points they describe. A signature pattern is an abstract description of +one or more join-point signatures. Signature patterns are intended to +match very closely the same kind of things one would write when +declaring individual members and constructors. + +Method declarations in Java include method names, method parameters, +return types, modifiers like static or private, and throws clauses, +while constructor declarations omit the return type and replace the +method name with the class name. The start of a particular method +declaration, in class `Test`, for example, might be + +.... +class C { + public final void foo() throws ArrayOutOfBoundsException { ... } +} +.... + +In AspectJ, method signature patterns have all these, but most elements +can be replaced by wildcards. So + +.... +call(public final void C.foo() throws ArrayOutOfBoundsException) +.... + +picks out call join points to that method, and the pointcut + +.... +call(public final void *.*() throws ArrayOutOfBoundsException) +.... + +picks out all call join points to methods, regardless of their name name +or which class they are defined on, so long as they take no arguments, +return no value, are both `public` and `final`, and are declared to +throw `ArrayOutOfBounds` exceptions. + +The defining type name, if not present, defaults to *, so another way of +writing that pointcut would be + +.... +call(public final void *() throws ArrayOutOfBoundsException) +.... + +The wildcard `..` indicates zero or more parameters, so + +.... +execution(void m(..)) +.... + +picks out execution join points for void methods named `m`, of any +number of arguments, while + +.... +execution(void m(.., int)) +.... + +picks out execution join points for void methods named `m` whose last +parameter is of type `int`. + +The modifiers also form part of the signature pattern. If an AspectJ +signature pattern should match methods without a particular modifier, +such as all non-public methods, the appropriate modifier should be +negated with the `!` operator. So, + +.... +withincode(!public void foo()) +.... + +picks out all join points associated with code in null non-public void +methods named `foo`, while + +.... +withincode(void foo()) +.... + +picks out all join points associated with code in null void methods +named `foo`, regardless of access modifier. + +Method names may contain the * wildcard, indicating any number of +characters in the method name. So + +.... +call(int *()) +.... + +picks out all call join points to `int` methods regardless of name, but + +.... +call(int get*()) +.... + +picks out all call join points to `int` methods where the method name +starts with the characters "get". + +AspectJ uses the `new` keyword for constructor signature patterns rather +than using a particular class name. So the execution join points of +private null constructor of a class C defined to throw an +ArithmeticException can be picked out with + +.... +execution(private C.new() throws ArithmeticException) +.... + +===== Matching based on the declaring type + +The signature-matching pointcuts all specify a declaring type, but the +meaning varies slightly for each join point signature, in line with Java +semantics. + +When matching for pointcuts `withincode`, `get`, and `set`, the +declaring type is the class that contains the declaration. + +When matching method-call join points, the declaring type is the static +type used to access the method. A common mistake is to specify a +declaring type for the `call` pointcut that is a subtype of the +originally-declaring type. For example, given the class + +.... +class Service implements Runnable { + public void run() { ... } +} +.... + +the following pointcut + +.... +call(void Service.run()) +.... + +would fail to pick out the join point for the code + +.... +((Runnable) new Service()).run(); +.... + +Specifying the originally-declaring type is correct, but would pick out +any such call (here, calls to the `run()` method of any Runnable). In +this situation, consider instead picking out the target type: + +.... +call(void run()) && target(Service) +.... + +When matching method-execution join points, if the execution pointcut +method signature specifies a declaring type, the pointcut will only +match methods declared in that type, or methods that override methods +declared in or inherited by that type. So the pointcut + +.... +execution(public void Middle.*()) +.... + +picks out all method executions for public methods returning void and +having no arguments that are either declared in, or inherited by, +Middle, even if those methods are overridden in a subclass of Middle. So +the pointcut would pick out the method-execution join point for Sub.m() +in this code: + +.... +class Super { + protected void m() { ... } +} +class Middle extends Super { +} +class Sub extends Middle { + public void m() { ... } +} +.... + +===== Matching based on the throws clause + +Type patterns may be used to pick out methods and constructors based on +their throws clauses. This allows the following two kinds of extremely +wildcarded pointcuts: + +.... +pointcut throwsMathlike(): + // each call to a method with a throws clause containing at least + // one exception exception with "Math" in its name. + call(* *(..) throws *..*Math*); + +pointcut doesNotThrowMathlike(): + // each call to a method with a throws clause containing no + // exceptions with "Math" in its name. + call(* *(..) throws !*..*Math*); +.... + +A <ThrowsClausePattern> is a comma-separated list of +<ThrowsClausePatternItem>s, where + +<ThrowsClausePatternItem> ::: + [ ! ] + + + TypeNamePattern + +A <ThrowsClausePattern> matches the throws clause of any code member +signature. To match, each `ThrowsClausePatternItem` must match the +throws clause of the member in question. If any item doesn't match, then +the whole pattern doesn't match. + +If a ThrowsClausePatternItem begins with "!", then it matches a +particular throws clause if and only if _none_ of the types named in the +throws clause is matched by the `TypeNamePattern`. + +If a <ThrowsClausePatternItem> does not begin with "!", then it matches +a throws clause if and only if _any_ of the types named in the throws +clause is matched by the _TypeNamePattern_. + +The rule for "!" matching has one potentially surprising property, in +that these two pointcuts + +* call(* *(..) throws !IOException) +* call(* *(..) throws (!IOException)) + +will match differently on calls to + +____ +void m() throws RuntimeException, IOException \{} +____ + +[1] will NOT match the method m(), because method m's throws clause +declares that it throws IOException. [2] WILL match the method m(), +because method m's throws clause declares the it throws some exception +which does not match IOException, i.e. RuntimeException. + +==== Type patterns + +Type patterns are a way to pick out collections of types and use them in +places where you would otherwise use only one type. The rules for using +type patterns are simple. + +===== Exact type pattern + +First, all type names are also type patterns. So `Object`, +`java.util.HashMap`, `Map.Entry`, `int` are all type patterns. + +If a type pattern is an exact type - if it doesn't include a wildcard - +then the matching works just like normal type lookup in Java: + +* Patterns that have the same names as primitive types (like ++ +int ++ +) match those primitive types. +* Patterns that are qualified by package names (like ++ +java.util.HashMap ++ +) match types in other packages. +* Patterns that are not qualified (like ++ +HashMap ++ +) match types that are resolved by Java's normal scope rules. So, for +example, ++ +HashMap ++ +might match a package-level type in the same package or a type that have +been imported with java's ++ +import ++ +form. But it would not match ++ +java.util.HashMap ++ +unless the aspect were in ++ +java.util ++ +or the type had been imported. + +So exact type patterns match based on usual Java scope rules. + +===== Type name patterns + +There is a special type name, *, which is also a type pattern. * picks +out all types, including primitive types. So + +.... +call(void foo(*)) +.... + +picks out all call join points to void methods named foo, taking one +argument of any type. + +Type names that contain the two wildcards "*" and "`..`" are also type +patterns. The * wildcard matches zero or more characters characters +except for ".", so it can be used when types have a certain naming +convention. So + +.... +handler(java.util.*Map) +.... + +picks out the types java.util.Map and java.util.java.util.HashMap, among +others, and + +.... +handler(java.util.*) +.... + +picks out all types that start with "`java.util.`" and don't have any +more "."s, that is, the types in the `java.util` package, but not inner +types (such as java.util.Map.Entry). + +The "`..`" wildcard matches any sequence of characters that start and +end with a ".", so it can be used to pick out all types in any +subpackage, or all inner types. So + +.... +within(com.xerox..*) +.... + +picks out all join points where the code is in any declaration of a type +whose name begins with "`com.xerox.`". + +Type patterns with wildcards do not depend on Java's usual scope rules - +they match against all types available to the weaver, not just those +that are imported into an Aspect's declaring file. + +===== Subtype patterns + +It is possible to pick out all subtypes of a type (or a collection of +types) with the "+" wildcard. The "+" wildcard follows immediately a +type name pattern. So, while + +.... +call(Foo.new()) +.... + +picks out all constructor call join points where an instance of exactly +type Foo is constructed, + +.... +call(Foo+.new()) +.... + +picks out all constructor call join points where an instance of any +subtype of Foo (including Foo itself) is constructed, and the unlikely + +.... +call(*Handler+.new()) +.... + +picks out all constructor call join points where an instance of any +subtype of any type whose name ends in "Handler" is constructed. + +===== Array type patterns + +A type name pattern or subtype pattern can be followed by one or more +sets of square brackets to make array type patterns. So `Object[]` is an +array type pattern, and so is `com.xerox..*[][]`, and so is `Object+[]`. + +===== Type patterns + +Type patterns are built up out of type name patterns, subtype patterns, +and array type patterns, and constructed with boolean operators `&&`, +`||`, and `!`. So + +.... +staticinitialization(Foo || Bar) +.... + +picks out the static initializer execution join points of either Foo or +Bar, and + +.... +call((Foo+ && ! Foo).new(..)) +.... + +picks out the constructor call join points when a subtype of Foo, but +not Foo itself, is constructed. + +==== Pattern Summary + +Here is a summary of the pattern syntax used in AspectJ: + +.... +MethodPattern = + [ModifiersPattern] TypePattern + [TypePattern . ] IdPattern (TypePattern | ".." , ... ) + [ throws ThrowsPattern ] +ConstructorPattern = + [ModifiersPattern ] + [TypePattern . ] new (TypePattern | ".." , ...) + [ throws ThrowsPattern ] +FieldPattern = + [ModifiersPattern] TypePattern [TypePattern . ] IdPattern +ThrowsPattern = + [ ! ] TypePattern , ... +TypePattern = + IdPattern [ + ] [ [] ... ] + | ! TypePattern + | TypePattern && TypePattern + | TypePattern || TypePattern + | ( TypePattern ) +IdPattern = + Sequence of characters, possibly with special * and .. wildcards +ModifiersPattern = + [ ! ] JavaModifier ... +.... + +[[semantics-advice]] +=== Advice + +Each piece of advice is of the form + +____ +[ strictfp ] + +AdviceSpec + +[ throws + +TypeList + +] : + +Pointcut + +\{ + +Body + +} +____ + +where <AdviceSpec> is one of + +* before( ++ +Formals ++ +) +* after( ++ +Formals ++ +) returning [ ( ++ +Formal ++ +) ] +* after( ++ +Formals ++ +) throwing [ ( ++ +Formal ++ +) ] +* after( ++ +Formals ++ +) +* Type ++ +around( ++ +Formals ++ +) + +and where <Formal> refers to a variable binding like those used for +method parameters, of the form `Type` `Variable-Name`, and <Formals> +refers to a comma-delimited list of <Formal>. + +Advice defines crosscutting behavior. It is defined in terms of +pointcuts. The code of a piece of advice runs at every join point picked +out by its pointcut. Exactly how the code runs depends on the kind of +advice. + +AspectJ supports three kinds of advice. The kind of advice determines +how it interacts with the join points it is defined over. Thus AspectJ +divides advice into that which runs before its join points, that which +runs after its join points, and that which runs in place of (or +"around") its join points. + +While before advice is relatively unproblematic, there can be three +interpretations of after advice: After the execution of a join point +completes normally, after it throws an exception, or after it does +either one. AspectJ allows after advice for any of these situations. + +.... +aspect A { + pointcut publicCall(): call(public Object *(..)); + after() returning (Object o): publicCall() { + System.out.println("Returned normally with " + o); + } + after() throwing (Exception e): publicCall() { + System.out.println("Threw an exception: " + e); + } + after(): publicCall(){ + System.out.println("Returned or threw an Exception"); + } +} +.... + +After returning advice may not care about its returned object, in which +case it may be written + +.... +after() returning: call(public Object *(..)) { + System.out.println("Returned normally"); +} +.... + +If after returning does expose its returned object, then the type of the +parameter is considered to be an `instanceof`-like constraint on the +advice: it will run only when the return value is of the appropriate +type. + +A value is of the appropriate type if it would be assignable to a +variable of that type, in the Java sense. That is, a `byte` value is +assignable to a `short` parameter but not vice-versa, an `int` is +assignable to a `float` parameter, `boolean` values are only assignable +to `boolean` parameters, and reference types work by instanceof. + +There are two special cases: If the exposed value is typed to `Object`, +then the advice is not constrained by that type: the actual return value +is converted to an object type for the body of the advice: `int` values +are represented as `java.lang.Integer` objects, etc, and no value (from +void methods, for example) is represented as `null`. + +Secondly, the `null` value is assignable to a parameter `T` if the join +point _could_ return something of type `T`. + +Around advice runs in place of the join point it operates over, rather +than before or after it. Because around is allowed to return a value, it +must be declared with a return type, like a method. + +Thus, a simple use of around advice is to make a particular method +constant: + +.... +aspect A { + int around(): call(int C.foo()) { + return 3; + } +} +.... + +Within the body of around advice, though, the computation of the +original join point can be executed with the special syntax + +.... +proceed( ... ) +.... + +The proceed form takes as arguments the context exposed by the around's +pointcut, and returns whatever the around is declared to return. So the +following around advice will double the second argument to `foo` +whenever it is called, and then halve its result: + +.... +aspect A { + int around(int i): call(int C.foo(Object, int)) && args(i) { + int newi = proceed(i*2) + return newi/2; + } +} +.... + +If the return value of around advice is typed to `Object`, then the +result of proceed is converted to an object representation, even if it +is originally a primitive value. And when the advice returns an Object +value, that value is converted back to whatever representation it was +originally. So another way to write the doubling and halving advice is: + +.... +aspect A { + Object around(int i): call(int C.foo(Object, int)) && args(i) { + Integer newi = (Integer) proceed(i*2) + return new Integer(newi.intValue() / 2); + } +} +.... + +Any occurence of `proceed(..)` within the body of around advice is +treated as the special proceed form (even if the aspect defines a method +named `proceed`), unless a target other than the aspect instance is +specified as the recipient of the call. For example, in the following +program the first call to proceed will be treated as a method call to +the `ICanProceed` instance, whereas the second call to proceed is +treated as the special proceed form. + +.... +aspect A { + Object around(ICanProceed canProceed) : execution(* *(..)) && this(canProceed) { + canProceed.proceed(); // a method call + return proceed(canProceed); // the special proceed form + } + + private Object proceed(ICanProceed canProceed) { + // this method cannot be called from inside the body of around advice in + // the aspect + } +} +.... + +In all kinds of advice, the parameters of the advice behave exactly like +method parameters. In particular, assigning to any parameter affects +only the value of the parameter, not the value that it came from. This +means that + +.... +aspect A { + after() returning (int i): call(int C.foo()) { + i = i * 2; + } +} +.... + +will _not_ double the returned value of the advice. Rather, it will +double the local parameter. Changing the values of parameters or return +values of join points can be done by using around advice. + +With `proceed(..)` it is possible to change the values used by +less-precedent advice and the underlying join point by supplying +different values for the variables. For example, this aspect replaces +the string bound to `s` in the named pointcut `privateData`: + +.... +aspect A { + Object around(String s): MyPointcuts.privateData(s) { + return proceed("private data"); + } +} +.... + +If you replace an argument to `proceed(..)`, you can cause a +`ClassCastException` at runtime when the argument refers to a supertype +of the actual type and you do not supply a reference of the actual type. +In the following aspect, the around advice replaces the declared target +`List` with an `ArrayList`. This is valid code at compile-time since the +types match. + +.... +import java.util.*; + +aspect A { + Object around(List list): call(* List+.*()) && target(list) { + return proceed(new ArrayList()); + } +} +.... + +But imagine a simple program where the actual target is `LinkedList`. In +this case, the advice would cause a `ClassCastException` at runtime, and +`peek()` is not declared in `ArrayList`. + +.... +public class Test { + public static void main(String[] args) { + new LinkedList().peek(); + } +} +.... + +The `ClassCastException` can occur even in situations where it appears +to be unnecessary, e.g., if the program is changed to call `size()`, +declared in `List`: + +.... +public class Test { + public static void main(String[] args) { + new LinkedList().size(); + } +} +.... + +There will still be a `ClassCastException` because it is impossible to +prove that there won't be a runtime binary-compatible change in the +hierarchy of `LinkedList` or some other advice on the join point that +requires a `LinkedList`. + +==== Advice modifiers + +The `strictfp` modifier is the only modifier allowed on advice, and it +has the effect of making all floating-point expressions within the +advice be FP-strict. + +==== Advice and checked exceptions + +An advice declaration must include a `throws` clause listing the checked +exceptions the body may throw. This list of checked exceptions must be +compatible with each target join point of the advice, or an error is +signalled by the compiler. + +For example, in the following declarations: + +.... +import java.io.FileNotFoundException; + +class C { + int i; + + int getI() { return i; } +} + +aspect A { + before(): get(int C.i) { + throw new FileNotFoundException(); + } + before() throws FileNotFoundException: get(int C.i) { + throw new FileNotFoundException(); + } +} +.... + +both pieces of advice are illegal. The first because the body throws an +undeclared checked exception, and the second because field get join +points cannot throw `FileNotFoundException`s. + +The exceptions that each kind of join point in AspectJ may throw are: + +method call and execution:: + the checked exceptions declared by the target method's + + + throws + + + clause. +constructor call and execution:: + the checked exceptions declared by the target constructor's + + + throws + + + clause. +field get and set:: + no checked exceptions can be thrown from these join points. +exception handler execution:: + the exceptions that can be thrown by the target exception handler. +static initializer execution:: + no checked exceptions can be thrown from these join points. +pre-initialization and initialization:: + any exception that is in the throws clause of + + + all + + + constructors of the initialized class. +advice execution:: + any exception that is in the throws clause of the advice. + +==== Advice precedence + +Multiple pieces of advice may apply to the same join point. In such +cases, the resolution order of the advice is based on advice precedence. + +===== Determining precedence + +There are a number of rules that determine whether a particular piece of +advice has precedence over another when they advise the same join point. + +If the two pieces of advice are defined in different aspects, then there +are three cases: + +* If aspect A is matched earlier than aspect B in some ++ +declare precedence ++ +form, then all advice in concrete aspect A has precedence over all +advice in concrete aspect B when they are on the same join point. +* Otherwise, if aspect A is a subaspect of aspect B, then all advice +defined in A has precedence over all advice defined in B. So, unless +otherwise specified with ++ +declare precedence ++ +, advice in a subaspect has precedence over advice in a superaspect. +* Otherwise, if two pieces of advice are defined in two different +aspects, it is undefined which one has precedence. + +If the two pieces of advice are defined in the same aspect, then there +are two cases: + +* If either are ++ +after ++ +advice, then the one that appears later in the aspect has precedence +over the one that appears earlier. +* Otherwise, then the one that appears earlier in the aspect has +precedence over the one that appears later. + +These rules can lead to circularity, such as + +.... +aspect A { + before(): execution(void main(String[] args)) {} + after(): execution(void main(String[] args)) {} + before(): execution(void main(String[] args)) {} +} +.... + +such circularities will result in errors signalled by the compiler. + +===== Effects of precedence + +At a particular join point, advice is ordered by precedence. + +A piece of `around` advice controls whether advice of lower precedence +will run by calling `proceed`. The call to `proceed` will run the advice +with next precedence, or the computation under the join point if there +is no further advice. + +A piece of `before` advice can prevent advice of lower precedence from +running by throwing an exception. If it returns normally, however, then +the advice of the next precedence, or the computation under the join +pint if there is no further advice, will run. + +Running `after returning` advice will run the advice of next precedence, +or the computation under the join point if there is no further advice. +Then, if that computation returned normally, the body of the advice will +run. + +Running `after throwing` advice will run the advice of next precedence, +or the computation under the join point if there is no further advice. +Then, if that computation threw an exception of an appropriate type, the +body of the advice will run. + +Running `after` advice will run the advice of next precedence, or the +computation under the join point if there is no further advice. Then the +body of the advice will run. + +==== Reflective access to the join point + +Three special variables are visible within bodies of advice and within +`if()` pointcut expressions: `thisJoinPoint`, `thisJoinPointStaticPart`, +and `thisEnclosingJoinPointStaticPart`. Each is bound to an object that +encapsulates some of the context of the advice's current or enclosing +join point. These variables exist because some pointcuts may pick out +very large collections of join points. For example, the pointcut + +.... +pointcut publicCall(): call(public * *(..)); +.... + +picks out calls to many methods. Yet the body of advice over this +pointcut may wish to have access to the method name or parameters of a +particular join point. + +`thisJoinPoint` is bound to a complete join point object. + +`thisJoinPointStaticPart` is bound to a part of the join point object +that includes less information, but for which no memory allocation is +required on each execution of the advice. It is equivalent to +`thisJoinPoint.getStaticPart()`. + +`thisEnclosingJoinPointStaticPart` is bound to the static part of the +join point enclosing the current join point. Only the static part of +this enclosing join point is available through this mechanism. + +Standard Java reflection uses objects from the `java.lang.reflect` +hierarchy to build up its reflective objects. Similarly, AspectJ join +point objects have types in a type hierarchy. The type of objects bound +to `thisJoinPoint` is `org.aspectj.lang.JoinPoint`, while +`thisStaticJoinPoint` is bound to objects of interface type +`org.aspectj.lang.JoinPoint.StaticPart`. + +[[semantics-declare]] +=== Static crosscutting + +Advice declarations change the behavior of classes they crosscut, but do +not change their static type structure. For crosscutting concerns that +do operate over the static structure of type hierarchies, AspectJ +provides inter-type member declarations and other `declare` forms. + +==== Inter-type member declarations + +AspectJ allows the declaration of members by aspects that are associated +with other types. + +An inter-type method declaration looks like + +* [ ++ +Modifiers ++ +] ++ +Type ++ +OnType ++ +. ++ +Id ++ +( ++ +Formals ++ +) [ ++ +ThrowsClause ++ +] \{ ++ +Body ++ +} +* abstract [ ++ +Modifiers ++ +] ++ +Type ++ +OnType ++ +. ++ +Id ++ +( ++ +Formals ++ +) [ ++ +ThrowsClause ++ +] ; + +The effect of such a declaration is to make <OnType> support the new +method. Even if <OnType> is an interface. Even if the method is neither +public nor abstract. So the following is legal AspectJ code: + +.... +interface Iface {} + +aspect A { + private void Iface.m() { + System.err.println("I'm a private method on an interface"); + } + void worksOnI(Iface iface) { + // calling a private method on an interface + iface.m(); + } +} +.... + +An inter-type constructor declaration looks like + +* [ ++ +Modifiers ++ +] ++ +OnType ++ +. new ( ++ +Formals ++ +) [ ++ +ThrowsClause ++ +] \{ ++ +Body ++ +} + +The effect of such a declaration is to make <OnType> support the new +constructor. It is an error for <OnType> to be an interface. + +Inter-type declared constructors cannot be used to assign a value to a +final variable declared in <OnType>. This limitation significantly +increases the ability to both understand and compile the <OnType> class +and the declaring aspect separately. + +Note that in the Java language, classes that define no constructors have +an implicit no-argument constructor that just calls `super()`. This +means that attempting to declare a no-argument inter-type constructor on +such a class may result in a conflict, even though it _looks_ like no +constructor is defined. + +An inter-type field declaration looks like one of + +* [ ++ +Modifiers ++ +] ++ +Type ++ +OnType ++ +. ++ +Id ++ += ++ +Expression ++ +; +* [ ++ +Modifiers ++ +] ++ +Type ++ +OnType ++ +. ++ +Id ++ +; + +The effect of such a declaration is to make <OnType> support the new +field. Even if <OnType> is an interface. Even if the field is neither +public, nor static, nor final. + +The initializer, if any, of an inter-type field declaration runs before +the class-local initializers defined in its target class. + +Any occurrence of the identifier `this` in the body of an inter-type +constructor or method declaration, or in the initializer of an +inter-type field declaration, refers to the <OnType> object rather than +to the aspect type; it is an error to access `this` in such a position +from a `static` inter-type member declaration. + +==== Access modifiers + +Inter-type member declarations may be public or private, or have default +(package-protected) visibility. AspectJ does not provide protected +inter-type members. + +The access modifier applies in relation to the aspect, not in relation +to the target type. So a private inter-type member is visible only from +code that is defined within the declaring aspect. A default-visibility +inter-type member is visible only from code that is defined within the +declaring aspect's package. + +Note that a declaring a private inter-type method (which AspectJ +supports) is very different from inserting a private method declaration +into another class. The former allows access only from the declaring +aspect, while the latter would allow access only from the target type. +Java serialization, for example, uses the presense of a private method +`void writeObject(ObjectOutputStream)` for the implementation of +`java.io.Serializable`. A private inter-type declaration of that method +would not fulfill this requirement, since it would be private to the +aspect, not private to the target type. + +The access modifier of abstract inter-type methods has one constraint: +It is illegal to declare an abstract non-public inter-type method on a +public interface. This is illegal because it would say that a public +interface has a constraint that only non-public implementors must +fulfill. This would not be compatible with Java's type system. + +==== Conflicts + +Inter-type declarations raise the possibility of conflicts among locally +declared members and inter-type members. For example, assuming +`otherPackage` is not the package containing the aspect `A`, the code + +.... +aspect A { + private Registry otherPackage.onType.r; + public void otherPackage.onType.register(Registry r) { + r.register(this); + this.r = r; + } +} +.... + +declares that `onType` in `otherPackage` has a field `r`. This field, +however, is only accessible from the code inside of aspect `A`. The +aspect also declares that `onType` has a method "`register`", but makes +this method accessible from everywhere. + +If `onType` already defines a private or package-protected field "`r`", +there is no conflict: The aspect cannot see such a field, and no code in +`otherPackage` can see the inter-type "`r`". + +If `onType` defines a public field "`r`", there is a conflict: The +expression + +.... +this.r = r +.... + +is an error, since it is ambiguous whether the private inter-type "`r`" +or the public locally-defined "`r`" should be used. + +If `onType` defines a method "`register(Registry)`" there is a conflict, +since it would be ambiguous to any code that could see such a defined +method which "`register(Registry)`" method was applicable. + +Conflicts are resolved as much as possible as per Java's conflict +resolution rules: + +* A subclass can inherit multiple ++ +fields ++ +from its superclasses, all with the same name and type. However, it is +an error to have an ambiguous ++ +reference ++ +to a field. +* A subclass can only inherit multiple ++ +methods ++ +with the same name and argument types from its superclasses if only zero +or one of them is concrete (i.e., all but one is abstract, or all are +abstract). + +Given a potential conflict between inter-type member declarations in +different aspects, if one aspect has precedence over the other its +declaration will take effect without any conflict notice from compiler. +This is true both when the precedence is declared explicitly with +`declare precedence` as well as when when sub-aspects implicitly have +precedence over their super-aspect. + +==== Extension and Implementation + +An aspect may change the inheritance hierarchy of a system by changing +the superclass of a type or adding a superinterface onto a type, with +the `declare parents` form. + +* declare parents: ++ +TypePattern ++ +extends ++ +Type ++ +; +* declare parents: ++ +TypePattern ++ +implements ++ +TypeList ++ +; + +For example, if an aspect wished to make a particular class runnable, it +might define appropriate inter-type `void + run()` method, but it should also declare that the class +fulfills the `Runnable` interface. In order to implement the methods in +the `Runnable` interface, the inter-type `run()` method must be public: + +.... +aspect A { + declare parents: SomeClass implements Runnable; + public void SomeClass.run() { ... } +} +.... + +==== Interfaces with members + +Through the use of inter-type members, interfaces may now carry +(non-public-static-final) fields and (non-public-abstract) methods that +classes can inherit. Conflicts may occur from ambiguously inheriting +members from a superclass and multiple superinterfaces. + +Because interfaces may carry non-static initializers, each interface +behaves as if it has a zero-argument constructor containing its +initializers. The order of super-interface instantiation is observable. +We fix this order with the following properties: A supertype is +initialized before a subtype, initialized code runs only once, and the +initializers for a type's superclass are run before the initializers for +its superinterfaces. Consider the following hierarchy where \{`Object`, +`C`, `D`, `E`} are classes, \{`M`, `N`, `O`, `P`, `Q`} are interfaces. + +.... + Object M O + \ / \ / + C N Q + \ / / + D P + \ / + E +.... + +when a new `E` is instantiated, the initializers run in this order: + +.... +Object M C O N D Q P E +.... + +==== Warnings and Errors + +An aspect may specify that a particular join point should never be +reached. + +* declare error: ++ +Pointcut ++ +: ++ +String ++ +; +* declare warning: ++ +Pointcut ++ +: ++ +String ++ +; + +If the compiler determines that a join point in <Pointcut> could +possibly be reached, then it will signal either an error or warning, as +declared, using the <String> for its message. + +==== Softened exceptions + +An aspect may specify that a particular kind of exception, if thrown at +a join point, should bypass Java's usual static exception checking +system and instead be thrown as a `org.aspectj.lang.SoftException`, +which is subtype of `RuntimeException` and thus does not need to be +declared. + +* declare soft: ++ +Type ++ +: ++ +Pointcut ++ +; + +For example, the aspect + +.... +aspect A { + declare soft: Exception: execution(void main(String[] args)); +} +.... + +Would, at the execution join point, catch any `Exception` and rethrow a +`org.aspectj.lang.SoftException` containing original exception. + +This is similar to what the following advice would do + +.... +aspect A { + void around() execution(void main(String[] args)) { + try { proceed(); } + catch (Exception e) { + throw new org.aspectj.lang.SoftException(e); + } + } +} +.... + +except, in addition to wrapping the exception, it also affects Java's +static exception checking mechanism. + +Like advice, the declare soft form has no effect in an abstract aspect +that is not extended by a concreate aspect. So the following code will +not compile unless it is compiled with an extending concrete aspect: + +.... +abstract aspect A { + abstract pointcut softeningPC(); + + before() : softeningPC() { + Class.forName("FooClass"); // error: uncaught ClassNotFoundException + } + + declare soft : ClassNotFoundException : call(* Class.*(..)); +} +.... + +[[advice-precedence-cross]] +==== Advice Precedence + +An aspect may declare a precedence relationship between concrete aspects +with the `declare precedence` form: + +* declare precedence : ++ +TypePatternList ++ +; + +This signifies that if any join point has advice from two concrete +aspects matched by some pattern in <TypePatternList>, then the +precedence of the advice will be the order of in the list. + +In <TypePatternList>, the wildcard "*" can appear at most once, and it +means "any type not matched by any other pattern in the list". + +For example, the constraints that (1) aspects that have Security as part +of their name should have precedence over all other aspects, and (2) the +Logging aspect (and any aspect that extends it) should have precedence +over all non-security aspects, can be expressed by: + +.... +declare precedence: *..*Security*, Logging+, *; +.... + +For another example, the CountEntry aspect might want to count the entry +to methods in the current package accepting a Type object as its first +argument. However, it should count all entries, even those that the +aspect DisallowNulls causes to throw exceptions. This can be +accomplished by stating that CountEntry has precedence over +DisallowNulls. This declaration could be in either aspect, or in +another, ordering aspect: + +.... +aspect Ordering { + declare precedence: CountEntry, DisallowNulls; +} +aspect DisallowNulls { + pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..); + before(Type obj): allTypeMethods(obj) { + if (obj == null) throw new RuntimeException(); + } +} +aspect CountEntry { + pointcut allTypeMethods(Type obj): call(* *(..)) && args(obj, ..); + static int count = 0; + before(): allTypeMethods(Type) { + count++; + } +} +.... + +===== Various cycles + +It is an error for any aspect to be matched by more than one TypePattern +in a single decare precedence, so: + +.... +declare precedence: A, B, A ; // error +.... + +However, multiple declare precedence forms may legally have this kind of +circularity. For example, each of these declare precedence is perfectly +legal: + +.... +declare precedence: B, A; +declare precedence: A, B; +.... + +And a system in which both constraints are active may also be legal, so +long as advice from A and B don't share a join point. So this is an +idiom that can be used to enforce that A and B are strongly independent. + +===== Applies to concrete aspects + +Consider the following library aspects: + +.... +abstract aspect Logging { + abstract pointcut logged(); + + before(): logged() { + System.err.println("thisJoinPoint: " + thisJoinPoint); + } +} + +abstract aspect MyProfiling { + abstract pointcut profiled(); + + Object around(): profiled() { + long beforeTime = System.currentTimeMillis(); + try { + return proceed(); + } finally { + long afterTime = System.currentTimeMillis(); + addToProfile(thisJoinPointStaticPart, + afterTime - beforeTime); + } + } + abstract void addToProfile( + org.aspectj.JoinPoint.StaticPart jp, + long elapsed); +} +.... + +In order to use either aspect, they must be extended with concrete +aspects, say, MyLogging and MyProfiling. Because advice only applies +from concrete aspects, the declare precedence form only matters when +declaring precedence with concrete aspects. So + +.... +declare precedence: Logging, Profiling; +.... + +has no effect, but both + +.... +declare precedence: MyLogging, MyProfiling; +declare precedence: Logging+, Profiling+; +.... + +are meaningful. + +==== Statically determinable pointcuts + +Pointcuts that appear inside of `declare` forms have certain +restrictions. Like other pointcuts, these pick out join points, but they +do so in a way that is statically determinable. + +Consequently, such pointcuts may not include, directly or indirectly +(through user-defined pointcut declarations) pointcuts that discriminate +based on dynamic (runtime) context. Therefore, such pointcuts may not be +defined in terms of + +* cflow +* cflowbelow +* this +* target +* args +* if + +all of which can discriminate on runtime information. + +[[semantics-aspects]] +=== Aspects + +An aspect is a crosscutting type defined by the `aspect` declaration. + +==== Aspect Declaration + +The `aspect` declaration is similar to the `class` declaration in that +it defines a type and an implementation for that type. It differs in a +number of ways: + +===== Aspect implementation can cut across other types + +In addition to normal Java class declarations such as methods and +fields, aspect declarations can include AspectJ declarations such as +advice, pointcuts, and inter-type declarations. Thus, aspects contain +implementation declarations that can can cut across other types +(including those defined by other aspect declarations). + +===== Aspects are not directly instantiated + +Aspects are not directly instantiated with a new expression, with +cloning, or with serialization. Aspects may have one constructor +definition, but if so it must be of a constructor taking no arguments +and throwing no checked exceptions. + +===== Nested aspects must be `static` + +Aspects may be defined either at the package level, or as a static +nested aspect -- that is, a static member of a class, interface, or +aspect. If it is not at the package level, the aspect _must_ be defined +with the static keyword. Local and anonymous aspects are not allowed. + +==== Aspect Extension + +To support abstraction and composition of crosscutting concerns, aspects +can be extended in much the same way that classes can. Aspect extension +adds some new rules, though. + +===== Aspects may extend classes and implement interfaces + +An aspect, abstract or concrete, may extend a class and may implement a +set of interfaces. Extending a class does not provide the ability to +instantiate the aspect with a new expression: The aspect may still only +define a null constructor. + +===== Classes may not extend aspects + +It is an error for a class to extend or implement an aspect. + +===== Aspects extending aspects + +Aspects may extend other aspects, in which case not only are fields and +methods inherited but so are pointcuts. However, aspects may only extend +abstract aspects. It is an error for a concrete aspect to extend another +concrete aspect. + +==== Aspect instantiation + +Unlike class expressions, aspects are not instantiated with `new` +expressions. Rather, aspect instances are automatically created to cut +across programs. A program can get a reference to an aspect instance +using the static method `aspectOf(..)`. + +Because advice only runs in the context of an aspect instance, aspect +instantiation indirectly controls when advice runs. + +The criteria used to determine how an aspect is instantiated is +inherited from its parent aspect. If the aspect has no parent aspect, +then by default the aspect is a singleton aspect. How an aspect is +instantiated controls the form of the `aspectOf(..)` method defined on +the concrete aspect class. + +===== Singleton Aspects + +* aspect ++ +Id ++ +\{ ... } +* aspect ++ +Id ++ +issingleton() \{ ... } + +By default (or by using the modifier `issingleton()`) an aspect has +exactly one instance that cuts across the entire program. That instance +is available at any time during program execution from the static method +`aspectOf()` automatically defined on all concrete aspects -- so, in the +above examples, `A.aspectOf()` will return A's instance. This aspect +instance is created as the aspect's classfile is loaded. + +Because the an instance of the aspect exists at all join points in the +running of a program (once its class is loaded), its advice will have a +chance to run at all such join points. + +(In actuality, one instance of the aspect A is made for each version of +the aspect A, so there will be one instantiation for each time A is +loaded by a different classloader.) + +===== Per-object aspects + +* aspect ++ +Id ++ +perthis( ++ +Pointcut ++ +) \{ ... } +* aspect ++ +Id ++ +pertarget( ++ +Pointcut ++ +) \{ ... } + +If an aspect A is defined `perthis(Pointcut)`, then one object of type A +is created for every object that is the executing object (i.e., "this") +at any of the join points picked out by <Pointcut>. The advice defined +in A will run only at a join point where the currently executing object +has been associated with an instance of A. + +Similarly, if an aspect A is defined `pertarget(Pointcut)`, then one +object of type A is created for every object that is the target object +of the join points picked out by <Pointcut>. The advice defined in A +will run only at a join point where the target object has been +associated with an instance of A. + +In either case, the static method call `A.aspectOf(Object)` can be used +to get the aspect instance (of type A) registered with the object. Each +aspect instance is created as early as possible, but not before reaching +a join point picked out by <Pointcut> where there is no associated +aspect of type A. + +Both `perthis` and `pertarget` aspects may be affected by code the +AspectJ compiler controls, as discussed in the xref:#implementation[???] +appendix. + +===== Per-control-flow aspects + +* aspect ++ +Id ++ +percflow( ++ +Pointcut ++ +) \{ ... } +* aspect ++ +Id ++ +percflowbelow( ++ +Pointcut ++ +) \{ ... } + +If an aspect A is defined `percflow(Pointcut)` or +`percflowbelow(Pointcut)`, then one object of type A is created for each +flow of control of the join points picked out by <Pointcut>, either as +the flow of control is entered, or below the flow of control, +respectively. The advice defined in A may run at any join point in or +under that control flow. During each such flow of control, the static +method `A.aspectOf()` will return an object of type A. An instance of +the aspect is created upon entry into each such control flow. + +===== Aspect instantiation and advice + +All advice runs in the context of an aspect instance, but it is possible +to write a piece of advice with a pointcut that picks out a join point +that must occur before asopect instantiation. For example: + +.... +public class Client +{ + public static void main(String[] args) { + Client c = new Client(); + } +} + +aspect Watchcall { + pointcut myConstructor(): execution(new(..)); + + before(): myConstructor() { + System.err.println("Entering Constructor"); + } +} +.... + +The before advice should run before the execution of all constructors in +the system. It must run in the context of an instance of the Watchcall +aspect. The only way to get such an instance is to have Watchcall's +default constructor execute. But before that executes, we need to run +the before advice... + +There is no general way to detect these kinds of circularities at +compile time. If advice runs before its aspect is instantiated, AspectJ +will throw a +xref:../api/org/aspectj/lang/NoAspectBoundException.html[`org.aspectj.lang.NoAspectBoundException`]. + +==== Aspect privilege + +* privileged aspect ++ +Id ++ +\{ ... } + +Code written in aspects is subject to the same access control rules as +Java code when referring to members of classes or aspects. So, for +example, code written in an aspect may not refer to members with default +(package-protected) visibility unless the aspect is defined in the same +package. + +While these restrictions are suitable for many aspects, there may be +some aspects in which advice or inter-type members needs to access +private or protected resources of other types. To allow this, aspects +may be declared `privileged`. Code in priviliged aspects has access to +all members, even private ones. + +.... +class C { + private int i = 0; + void incI(int x) { i = i+x; } +} +privileged aspect A { + static final int MAX = 1000; + before(int x, C c): call(void C.incI(int)) && target(c) && args(x) { + if (c.i+x > MAX) throw new RuntimeException(); + } +} +.... + +In this case, if A had not been declared privileged, the field reference +c.i would have resulted in an error signaled by the compiler. + +If a privileged aspect can access multiple versions of a particular +member, then those that it could see if it were not privileged take +precedence. For example, in the code + +.... +class C { + private int i = 0; + void foo() { } +} + +privileged aspect A { + private int C.i = 999; + before(C c): call(void C.foo()) target(c) { + System.out.println(c.i); + } +} +.... + +A's private inter-type field C.i, initially bound to 999, will be +referenced in the body of the advice in preference to C's privately +declared field, since the A would have access to its own inter-type +fields even if it were not privileged. + +Note that a privileged aspect can access private inter-type declarations +made by other aspects, since they are simply considered private members +of that other aspect. |