diff options
author | Alexander Kriegisch <Alexander@Kriegisch.name> | 2022-01-08 11:50:55 +0700 |
---|---|---|
committer | Alexander Kriegisch <Alexander@Kriegisch.name> | 2024-01-06 10:09:11 +0100 |
commit | d4a6906b3012fac6e4dbaca5854fc59ba0d67e47 (patch) | |
tree | c78540555837c12cfaef7f9cc95842668a3ee7f9 /docs/pdguide/pointcuts.adoc | |
parent | 9735e858af48ff0bce152ea489800a86a151b08d (diff) | |
download | aspectj-d4a6906b3012fac6e4dbaca5854fc59ba0d67e47.tar.gz aspectj-d4a6906b3012fac6e4dbaca5854fc59ba0d67e47.zip |
Rename '*GuideDB' directories to their actual HTML site target names
Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Diffstat (limited to 'docs/pdguide/pointcuts.adoc')
-rw-r--r-- | docs/pdguide/pointcuts.adoc | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/docs/pdguide/pointcuts.adoc b/docs/pdguide/pointcuts.adoc new file mode 100644 index 000000000..ecd964c57 --- /dev/null +++ b/docs/pdguide/pointcuts.adoc @@ -0,0 +1,130 @@ +[[pointcuts]] +== Debugging Pointcuts + +[[pointcuts-introduction]] +=== Introduction + +This section describes how to write and debug pointcuts using the usual +approach of iteration and decomposition. New users are often stumped +when their advice does not match. That means the pointcut doesn't match; +they rewrite the pointcut and it still doesn't match, with no new +information. This can be frustrating if each iteration involves +building, deploying, and testing a complex application. Learning to +break it down, particularly into parts that can be checked at +compile-time, can save a lot of time. + +[[pointcuts-debugging]] +=== Debugging pointcuts + +Go at it top-down and then bottom-up. + +==== Top-down + +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, e.g. `// 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 + +Bottom-up (to build each part), consider each primitive pointcut +designator (PCD), then the composition, and then any implicit +constraints: + +[arabic] +. 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 the few constraints on AspectJ imposed by Java bytecode +as listed in the AspectJ Programming Guide section on +xref:../progguide/implementation.adoc#implementation[Implementation Notes]. + +It's much faster to iterate a pointcut at compile-time using declare +warning (even better, some errors are identified at parse-time in the +latest versions of AJDT). Start with the parts of the pointcut that are +staticly-determinable (i.e., they do not involve the runtime PCD's +listed above). If compiles themselves take too long because of all the +AspectJ weaving, then try to only include the debugging aspect with the +prototype pointcut, and limit the scope using `within(..)`. + +=== Common pointcut mistakes + +There are some typical types of mistakes developers make when designing pointcuts. +Here are a few examples: + +==== Mistakes in primitive pointcuts + +* `this(Foo) && execution(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` from ``Bar``" +and you only pick out calls to instance methods of `Foo` or you try to +pick out calls from static methods of `Bar`. + +* `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). + +==== Mistakes in composition + +* `call(* foo(Bar, Foo)) && args(Foo)`: This will never match. The +parameters in `args(..)` are position-dependent, so `args(Foo)` only +picks out join points where there is only one argument possible, of type +Foo. Use the indeterminate-arguments operator `..` as needed, e.g., +`args(Foo, ..)`. + +* `call(* foo()) && execution(* foo())`: This will never match. Each +pointcut must be true at each join point matched. For a union of +different kinds of join points (here, call or execution), use `||`. +E.g., 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, even though it is not +explicitly part of the pointcut. + +==== Mistakes in implementation requirements + +* _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. + +* `call(@SuperAnnotation Subclass.meth()`: Annotations are not inherited +by default, so e.g., if the pointcut specifies an annotation, then +subclass implementations of that method will not be matched. |