NAME
Aspect Ideas - new ideas for aspects-oriented Perl
DESCRIPTION
Distribution Ideas
The following sections outline ideas for modular aspects, for new types of join points, and for new types of pointcuts. There are also some general ideas.
Modular Aspect Ideas
- Development Aspects
-
Development aspects are aspects that are useful in the development of software. Because aspect behavior can be enabled and disabled easily, you can cleanly separate the product from the development aids. When you are finished, you can remove the aspects altogether leaving only the product. That way, the development tools are not included in the finished product, but can easily be reenabled for further development.
An obvious example of a development aspect is tracing the program flow. See
Aspect::Trace
. - Profiling Aspects
-
(This means aspects for profiling, not the profiling of aspects, although that would be worthwhile as well.)
Count invocations of a subroutine; benchmark subroutines (cf. Attribute::Profiled).
- Design-by-contract
-
Implement design-by-contract pre- and postconditions using aspects, cf.
Class::Contract
. - Default Variable I/O
-
This sets up a wrapper for a sub that takes a scalar as an argument and returns a scalar. The wrapper calls the sub with
$_
if no argument was given and puts the return value in$_
if the sub was called in void context.An example:
my $aspect = Aspect::UnderscoreContext->new('main::plus1'); $aspect->enable; ... sub plus1 { $_[0] + 1 }; plus1; # now $_ has been incremented
- Package Redirection
-
say you implemented a specialized version of the HTTP::Request object and want LWP to use that. But you don't want to change the LWP code, as that would mean branching. And sending a patch to Gisle isn't a good idea either, because your new class is very specialized, so the patch might get rejected. So you set up advice that any calls to package HTTP::Request should be redirected to your new class.
- Load-balancing
-
have the same code run on different boxes or in different processes or threads and provide mod_backhand-like functionality via aspects.
- Advertising
-
A stupid use of include join points:
advice(includes(qr/./, \&aspect_ad))
might say "Thank you for using CGI.pm" (get the module name via context) or "Brought to you by Aspects".
Automatically done only once per module, as each module is use()d only once, but do that with op join points as well: print an ad the first time only via $seen{$op}++.
- Testing
-
Aspects useful for CPANTS
- Timeout
-
Aspect to provide timeout to subs via alarm().
- Tainting
-
Aspect to taint and untaint args and return values of subroutines.
- Authentication Aspect
-
Users and groups; give rwx access to variables and subroutines. Via aspects, you have external control over permissions. When doing something that requires permission, either ask for authorization (provided by a keychain?) or allow or deny, like Apache does.
- Once-only Aspects
-
Run the advice code once, then remove the aspect code.
- Aspects checking arguments
-
An aspect could check the bounds of subroutine arguments and, if necessary, throw an exception or otherwise signal an error.
Join Point Ideas
Some ideas for new kinds of join points, that is, well-defined points during execution:
- Include Join Point
-
Using coderef-in-@INC, you can intercept use() statements:
BEGIN { unshift @INC, \&include_join_point } sub include_join_point { # process relevant advice # now decline so normal include handlers # can do their work }
- Op Join Points
-
overload operations to raise op join points, e.g.:
overload '+' => \&add_join_point; sub add_join_point { # process relevant advice # return a+b as normal }
This won't work if the package already overloads operations.
- Import Join Points
-
by overriding or wrapping a package's import()
- Destroy Join Points
-
by overriding or wrapping a package's DESTROY()
- Phasic Join Points
-
by defining a BEGIN, INIT, CHECK, END handler for a package
- Package-specfic Join Points
-
Packages (modules, classes) may define their own types of join points. For example, the Error module might define a Throw Join Point, a Catches Join Point, a Try Join Point and a Finally Join Point.
Classes that construct other classes (such as Class::MethodMaker or Class::Contract) could define a Field Get Join Point, a Field Set Join Point and a Constructor Join Point. A convenience Field Access Join Point could simply be defined as "either a Field Get Join Point or a Field Set Join Point". And there might be a Method Call Join Point and a Method Return Join Points. These class-related join points would also come in useful in Perl 6, which has special language support for classes (as opposed to non-OO modules).
These special join points must be able to influence the context passed to the advice code. For example, a Field Set Join Point would want to pass on the new value in the context (though in this case it can be gleaned by looking at the method's arguments).
Viewed this way, Perl signal handlers could be viewed as advice on Signal Join Points.
Pointcut Ideas
- New pointcut designators
-
Using existing pointcut operators and logical operators you can create new pointcut designators.
- Pointcut match efficiency
-
pointcut p1 { $joinpt1 & $joinpt2 } pointcut p2 { $joinpt3 && $joinpt4 } pointcut p3 { $p1 | $p2 }
Cache eval results for join points and points cuts, so (as is the case with p3 above) each join point and pointcut is eval'd only once per advice.
cflow($x)
-
cflow() is a pointcut operator that matches any kind of join point where the call stack at run time matches the condition specified with the cflow() operator.
pkg($x)
-
matches join points of any kind (i.e., context isn't checked) where the currently executing package matches
$x
. is_a($x)
-
matches join points of any kind (i.e., context isn't checked) where the currently executing package is-a (per Perl's
@ISA
hierarchy)$x
. calledby($x)
-
is like
cflow()
but looks at the direct caller only. That is, there is a match if the current sub's immediate caller matches$x
.
Debugger Support
Debugger support for developing with aspects.
- Debugger commands
-
Continue to next join point
- Affected Join Points
-
List all join points affected by a pointcut.
- Meta-aspects
-
An aspect to insert breakpoints or print other information when a join point is encountered.
Completely Random Ideas
Runtime (RT), coincides with program runtime. Define-time (DT), also happens at program runtime.
At DT, i.e., when an advice is defined, the advice's pointcut is tested against each join point in the program. If a join point matches the pointcut, an advice handler is installed. The pointcut is represented as an abstract syntax tree (AST). The AST's match_define method is called for each join point.
The advice handlers become part of the normal program flow. Advice handlers execute the advice code under different circumstances for different types of pointcut operators. Each pointcut operator has, alongside the DT test (match_define), also a RT test (match_run).
For a calls() pointcut operator, the test always evaluates to true: Since it only matches call join points at DT then once it's installed, the code runs. There is no further condition.
For a cflow() pointcut operator, a DT match doesn't mean that the advice code will run all the time; it will only run if the RT control flow matches the cflow() argument.
Example:
advice( calls('get') && cflow(qr/^Graphics/), sub { ... } )
means the following: at DT, calls() matches call join points where the sub name is 'get', and cflow() matches any join point, i.e. its match_define is always true. At RT, the pointcut expression is evaluated again, using the same AST as was constructed at DT, but this time with run time tests (i.e., using match_run): calls() always matches since it's installed, but cflow() matches only if caller() at some point matches /^Graphics/; and because of '&&', both RT tests must be true. If the pointcut expression matches, the advice code runs.
Like httpd is a web server, AOP can be seen as a server, where there are several phases during which aspects can change things in the program's flow.
Aspect order: if several aspects' pointcut affect a given join point, then the advice code of the aspect that has been defined later is run first.
Implement design patterns (cf. GoF). Make patterns first-class citizens.
Separation of concerns: separate the basic algorithms from special-purpose concerns such as synchronization, real-time constraints, and location control.
Use Aspects where they make sense; don't use them where they don't make sense. Like with OOP and Perl.
Combine aspects with other Perl technology: quantum aspects, aspects and coroutines, aspects and attributes, aspects and threads, aspects during compilation (B::*; compile-time join points; insert potential advice handlers during opportune moments, e.g. at the beginning and end of loops, after variable assignments etc.)
"Dynamic extension of class structure for duration of a task. This removes the pollution of the class graph with information which is only needed for a specific task." -- http://www.ccs.neu.edu/research/demeter/biblio/context.html
Possible Publicity
- Perl monger mailing lists
- use.perl journal
- articles
-
In online and offline magazines (perl.com, tpj, consumer mags).
- Search engines and directories
- aspect.perl.org -> aspect.unixbeard.net ?
- lists.perl.org
-
entry for the perl-aspects mailing list.
- conference talks
- AOP conferences
-
Check AOSD 2002: http://trese.cs.utwente.nl/aosd2002.htm.
AUTHOR
Marcel Grunauer, <marcel@codewerk.com>
COPYRIGHT
Copyright 2001 Marcel Grunauer. All rights reserved.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
SEE ALSO
Aspect::Intro(3pm), Aspect(3pm).