Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-users] writing/debugging pointcuts

fyi, I added a comment to the debugging pointcuts recipe of the aspectcookbook -- but felt it might be lost to the readers of
this list who don't read the cookbook (the perils of splitting).
(Nor do I in general think that duplication is the answer.)

Corrections most welcome.

Wes

[to debug or write pointcuts] First, I try to think in plain
language what I'm saying in the pointcut and see if that's what
the pointcut says.  If that does not show my error, I experiment
by building up the same pointcut from staticly-determinable to
runtime components.  Each stage of this has typical mistakes
and limitations, but there are few enough that you get better
quickly.  At first you have to experiment, but then you find you
can think it through, and finally you do it right the first time.

=== Writing and debugging a pointcut ===
Go at it top-down and then bottom-up.  Top-down, draft significant
aspects by first writing the comments to specify responsibilities.
Advice responsibility usually takes the form, "When X, do Y"
Pointcut responsibility for "When X" often takes the form,
"When [join points] [in locations] [are ...]".  These []'s often
translate to named pointcuts (like `libraryCalls() && within(Client)
&& args(Context)`) which form a semantic bridge to the plain-text
meaning in a comment: `// when the client passes only context into
the library`.

This gets you to a point where you can debug the parts of the
pointcut independently.

Bottom up (to build each part), consider each primitive pointcut
designator (PCD), then the composition, and then any implicit
constraints:

 * What kinds of join points should it match? (constructor-call?
 field-get?)?  This translates to using the kinded pointcuts
 (`call(..)`, `get(..)`, etc.).

 * Are these restricted to being lexically within something?  This
translates to using `within{code}(..)`.  If this is true, it should
always be used, to speed up weaving.

 * What runtime constraints and context should be true and available at
each join point?  This translates to `this()`, `target()`, `args()`,
`cflow{below}()` and `if(..)`.

 * Are there any advice or implementation limitations at issue?  This
involves knowing what Java bytecode tells AspectJ.

=== Experimenting ===
While the stages are the same for working it through mentally or
experimenting with ajc, ajc of course helps by giving the right feedback
(most of the time), and there's some things you can do to get better
feedback.

Declare warning or error: When experimenting with `ajc`, Istart (as
Ron suggested) by using staticly-determinable PCDs in a declare warning
to see if it warns about the places in the code you expect.  The most
relevant staticly-determinable PCDs are `within{code}()`, `call()`,
`execution()`, `{static}initialization()`, `get()` and `set()`, and
correspond to the first part of the pointcut responsibility: "When
[join points] [in locations] ...".

Compile time: If the compile takes a lot of time, ajc is doing a lot
of work, which can slow the feedback cycle when experimenting.
Advice on pointcuts that match lots of join points in lots of
classes take a long time to implement.  If you know the classes
containing the join points, then use `within()` or `withincode()`
to help the compiler exclude other types from consideration.
Otherwise, a pointcut like `call(String toString())` will be
matched against every toString() call in every class within the
control of the weaver.  Alternatively, you can restrict the sources
in the .lst file by commenting them out.

XLint messages: If you learn what things the compiler can warn about,
you can sometimes write pointcuts to get compile-time warnings
instead of runtime errors.  For example, it can tell you if a type
does not exist, but only if you didn't use a wildcard.

=== Mistakes and limitations for primitive pointcuts ===
Here are some common mistakes to consider when {experi}mentally
considering each primitive pointcut.  Most stem from not understanding
the join point at issue.

`target(Foo) && call(static * *(..))`: There is no `this` in a static
context, so `this()` or `target()` should not be used in a static
context or when targetting a static context (respectively).  This
happens most often when you want to say things like "all calls to Foo,"
and you only pick out calls to instance methods of Foo.

`target(Foo) && call(new(..)`: This will never match.  In
constructor-call join points, there is no target because the object
has not been created yet.

`call(* Foo.*(..))`: `Foo` refers to the compile-time type of the
invoking reference, not the implementing class.  In Java before 1.4,
the compile-time type was rendered as the defining type, not the
reference type; this was corrected in 1.4 (as shown when using ajc
with the -1.4 flag)  Most people should use `target(Foo) && call(...)`.

`execution(* Foo.bar(..))`: An execution join point for Foo is
always within Foo, so this won't pick out any overrides of bar(..).
Use `target(Foo) && execution(* bar(..))` for instance methods.

`within(Foo)`: anonymous types are not known at weave-time to be
within the lexically-enclosing type (a limitation of Java bytecode).

`args(Foo)`: `args()` is position-dependent, so `args(Foo)` only picks
 out join points where there is only one argument possible, of type Foo.
Use `args(Foo, ..)` as needed.

(We could develop a bunch of these; that's in part what the
`Pitfalls` section of the AspectJ Programming Guide aimed at.)

=== Mistakes in composition ===
`call(...) && get()`: A pointcut is evaluated at each join point, so
any time you use `&&` to two pointcuts that are mutually exclusive,
the pointcut will match nothing.  If you want to match both method-call
 and field-get join points, use `call(* ...) || get(...)`.

=== Mistakes in implicit advice constraints ===
`after () returning (Foo foo) : ...`: after advice can bind the
returned object or exception thrown.  That effectively acts like
`target()`, `this()`, or `args()` in restricting when the advice
runs based on the runtime type of the bound object.

=== Mistakes from implementation issues ===
`ajc` has to control the code for a join point in order to implement
the join point.  This translates to an implicit `within({code under
the control of the compiler})` for all join points, with additional
caveat for some join points.  Take exception handlers, for example:
there is no way to be sure from the bytecode where the original handler
ends, so `ajc` can't implement after advice on handler join points.
(Since these are on a per-join-point basis, they should be considered
for each corresponding primitive pointcut designator.)  Unlike the
mistakes with the primitive PCDs above, the compiler will emit an
error for these caveats.

=== AspectJ docs on point ===
See http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/documentation.html

 * Programming Guide, esp. Pitfalls, Semantics, and Implementation

 * FAQ entries

 * Sample code, esp. pointcut library



Back to the top