AspectScheme
This is the documentation for the implementation of aspectscheme language, introduced in Aspects in Higher-Order Languages.
The language implementation can be found at .
1 Installing AspectScheme
AspectScheme comes in two forms:
a user-installable language
a planet package
1.1 Installing the AspectScheme Language
Use the PLT Menu item File -> Install to install the provided aspectscheme.plt file. Then select AspectScheme as your default language; or write your code in a module based on the aspectscheme language.
To enable this language in DrScheme, under the Language menu, select Choose Language.... Under the section Experimental Languages, you will find the following language:
It is also available as the module language aspectscheme.
1.2 Programming using the planet package
Preface your code with
(require (planet "aspectscheme.ss" ("dutchyn" "aspectscheme.plt" 4 0)))
2 AspectScheme - the language
AspectScheme is derived from the scheme language, providing support for pointcuts and advice aspect-oriented programming.
AspectScheme considers procedure applications as join points, within a context of in-progress join points. These join points in context are recognized by pointcuts – predicates over the join point and context (represented as a list of join points). When a pointcut matches, advice transforms the join point into a new procedure.
3 Pointcuts
Pointcuts identify join points, principled locations in the execution of the program. Derived from continuations, the fundamental join points for a Scheme program are procedure calls, procedure executions, and by extension, advice executions.
3.1 Fundamental Pointcuts
_proc : procedure? |
recognizes a call to proc
_proc : procedure? |
recognizes an execution of proc
_proc : procedure? |
recognizes an execution of advice proc
3.2 Binding Pointcuts
These pointcuts expose the arguments available at the matched join point.
exposes the target (procedure or advice) of the matched join point as part of the context values
] exposes the entire list of arguments to the matched join point as part of the context values
as : (listof boolean?) |
exposes the selected (by #t) items in the context matched.
_val : any? |
appends additional values to the context
3.3 Contextual Pointcuts
In addition, join points may be filtered by control-flow context (i.e. continuation nesting).
_pc : pointcut? |
recognizes all join points which are below a join point matching pc, but with no intervening join points matching that pointcut. Values bound by the pointcut are inserted into the context argument of the advice.
_pc : pointcut? |
Recognizes all join points which are strictly below a join point matching pc. The values bound by the pointcut are taken from the nearest enclosing join point that matches pc. Using this construct, one may peel away join point context from the current join point upwards toward the top level. Nesting these allows one to access levels of nesting from the inside to the outside.
_pc : pointcut? |
recognizes join points which are strictly above a join point matching pc. The values bound by the pointcut are taken from the nearest enclosed join point that matches pc. Using this construct, one may worm back into join point context toward the currrently instantiated join point. This is useful for locating outermost executions from a known contextual join point identified by cflowtop or cflowbelow. Nestin these allows one to access levels of nesting from the outside to the inside.
_pc : pointcut? |
recognizes all join points which do not enclose another join point that matches
_pc : pointcut? |
(for compatibility) matches join points at the current level or any below; note that context values exposed by the binding join points are not replaceable in a proceed (unlike AspectJ), the replaceable arguments are supplied separately to the advice.
3.4 Pointcut Combinators
_pc : pointcut? |
merges many sub-pointcuts into one where all sub-pointcuts must match; context is merged from left-to-right to expose the context from all of the sub-pointcuts
_pc : pointcut? |
merges many sub-pointcuts into one where at least one of the sub-pointcuts must match; context is exposed in a short-circuit fashion: sub-pointcuts are tested until the first match, and its context is exposed. All sub-pointcuts must return the same number of context values (the some-args and with-args pointcuts may help satisfy this restriction); but, this is not statically checked.
_pc : pointcut? |
matches any join point that does not match the given sub-pointcut. Note that all context built up by the sub-pointcut is discarded.
3.5 Primitive Pointcuts
All the above pointcuts are actually derived from a few primitive pointcuts, using the higher-order property of pointcuts.
3.5.1 Join Point Stream
The primitive level pointcuts actually know about the join point stream. The join point stream is the list of join points currently extant between the start of program execution (the top) and the current expression to be evaluated (the bottom). As suggested by the connection between join points and continuations (hence the use of continuation-marks), the stream of join points corresponds with the continuations awaiting their values. Whenever a new join point is created, it needs to be matched against the available (dynamic and static) aspects. But that matching process can entail join points in the rest of the stream from the bottom to the top. In particular, cflowbelow forces a sub-match that climbs up the join point stream toward the top, looking for the first match in the awaiting join points. That is, cflowbelow tests whether the current join point of interest is below one that corresponds to the sub-match. Naturally, the cflowbelow sub-match is another match, looking at join points of interest and moving upwards.
AspectScheme reifies the join point stream for any joinpoint matcher (i.e. pointcut) as three arguments jp- jp jp+:
jp- are the join points that are cflowbelow the join point of interest
jp is the current join point of interest
jp+ are the join points which the current join point of interest is cflowbelow
Dually, this could all be phrased in terms of cflowabove, where the join point of interest traversal moves down the list.
For each pointcut, the join point stream starts as '() njp jp-stream) where njp is the newly-created join point, and jp-stream is the stream of join points leading to njp.
Each join point knows (has access to)
target: the procedure (or advice) corresponding to the join point – as a single-element list to conform to the usual result from a pointcut
args: the arguments to the target
A pointcut is a function from the jp stream to a list of context values – the targets and arguments (as appropriate) from the join points when they match.
_p? : procedure? |
_jp- : (listof joinpoint?) |
_jp : joinpoint |
_jp+ : (listof joinpoint?) |
simply focuses the join point predicate onto _jp, the join point of interest.
The primitive pointcuts call, exec, adv are a simple combination of focus-jp and a tag check of the join point of interest.
The primitive pointcuts target and args simply return take the appropriate fields of the join point of interest as a list of context values.
With two step functions,
below walk up the join point stream
above walk down the join point stream
, two pointcuts that recognize the end of the join point stream
top? there are no more join points above
bottom? there are no more join points below
and a simple iterator, cflow-walk, that takes a step function and a end recognizer, all of the control flow pointcuts are constructed.
4 Advice
(lambda (proceed) (lambda (ctxt_id ...) (lambda (arg_id ...) body)))
is the general form of advice. It takes
proceed a procedure that continues the computation
ctxt_id ... any context matched by the pointcut it is combined with
arg_id the arguments at the join point
and performs the desired semantic behaviour, including continuing the computation (potentially with new arguments) via
(proceed new_arg ...)
zero, one, or multiple times; or performing some completely different operation.
It is important to note that context values and current join point arguments are disjoint in our model. This makes clear what values are actually replacable by an around advice. Context values are not, but join point arguments are; hence the proceed only takes arguments. In contrast, AspectJ does not separate these, and pretends to proceed with new context values; but they’re not used: only the values bound in arguments are actually seen as changed. This can get tricky with cflow because it might match
the currently executing join point (whereupon supplying new arguments are actually seen as new value), or
a join point above the currently executing join point (whereupon new arguments are silently ignored).
5 Aspects
Pointcuts and advice are combined by an aspect declaration. These combinations can be static, dynamic, or top-level.
5.1 Static Aspects around
indicates whenever the pointcut pc matches within the static scope of the body ..., the advice adv is given control. These aspects apply lexically; outside of the lexical scope of the body ... they do not apply.
5.1.1 Common Static Aspect Control Structures
For convenience, several simpler versions are provided to satisfy common situations of performing semantic actions in addition to the usual ones. In these cases, proceed may not be called, although it must be present in the advice definition.
at join points matching pc, the advice adv is performed, then the join point continues with the original arguments.
at join points statically within body ... matching pc, the join point proceeds with the original arguments, and if an exception is thrown, the advice adv is performed; then the exception is re-thrown.
at join points statically within body ... matching pc, the join point proceeds with the original arguments, and if an exception is not thrown, the advice adv is performed; then result returned by the join point is returned.
at join points statically within body ... matching pc, the join point proceeds with the original arguments, and if an exception is not thrown, the advice adv is performed; then result returned by the join point is returned. If an exception is thrown within the proceeding join point, the advice adv is performed and then the exception is re-thrown.
5.2 Fluid Aspects fluid-around
just like around, but using dynamic scoping. Hence, the implementation essentially uses fluid-let to provide this facility. Unfortunately, fluid-let fails across module boundaries, so a replacement fluid-let-parameter is implemented using dynamic-wind and let-parameter.
5.2.1 Common Fluid- Aspect Control Structures
5.3 Top-Level Aspects top-level-around
These aspects apply for the remainder of the program. They are installed at the top level and continue until execution terminates.