line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Aspect; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Aspect - Aspect-Oriented Programming (AOP) for Perl |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use Aspect; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# Run some code "Advice" before a particular function |
16
|
|
|
|
|
|
|
before { |
17
|
|
|
|
|
|
|
print "About to call create\n"; |
18
|
|
|
|
|
|
|
} call 'Person::create'; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Run Advice after several methods and hijack their return values |
23
|
|
|
|
|
|
|
after { |
24
|
|
|
|
|
|
|
print "Called getter/setter " . $_->sub_name . "\n"; |
25
|
|
|
|
|
|
|
$_->return_value(undef); |
26
|
|
|
|
|
|
|
} call qr/^Person::[gs]et_/; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Run Advice conditionally based on multiple factors |
31
|
|
|
|
|
|
|
before { |
32
|
|
|
|
|
|
|
print "Calling a get method in void context within Tester::run_tests"; |
33
|
|
|
|
|
|
|
} wantvoid |
34
|
|
|
|
|
|
|
& ( call qr/^Person::get_/ & ! call 'Person::get_not_trapped' ) |
35
|
|
|
|
|
|
|
& cflow 'Tester::run_tests'; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Context-aware runtime hijacking of a method if certain condition is true |
40
|
|
|
|
|
|
|
around { |
41
|
|
|
|
|
|
|
if ( $_->self->customer_name eq 'Adam Kennedy' ) { |
42
|
|
|
|
|
|
|
# Ensure I always have cash |
43
|
|
|
|
|
|
|
$_->return_value('One meeeelion dollars'); |
44
|
|
|
|
|
|
|
} else { |
45
|
|
|
|
|
|
|
# Take a dollar off everyone else |
46
|
|
|
|
|
|
|
$_->proceed; |
47
|
|
|
|
|
|
|
$_->return_value( $_->return_value - 1 ); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
} call 'Bank::Account::balance'; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Catch and handle unexpected exceptions in a function into a formal object |
54
|
|
|
|
|
|
|
after { |
55
|
|
|
|
|
|
|
$_->exception( |
56
|
|
|
|
|
|
|
Exception::Unexpected->new($_->exception) |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
} throwing() |
59
|
|
|
|
|
|
|
& ! throwing('Exception::Expected') |
60
|
|
|
|
|
|
|
& ! throwing('Exception::Unexpected'); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Run Advice only on the outmost of a recursive series of calls |
65
|
|
|
|
|
|
|
around { |
66
|
|
|
|
|
|
|
print "Starting recursive child search\n"; |
67
|
|
|
|
|
|
|
$_->proceed; |
68
|
|
|
|
|
|
|
print "Finished recursive child search\n"; |
69
|
|
|
|
|
|
|
} call 'Person::find_child' & highest; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Run Advice only during the current lexical scope |
74
|
|
|
|
|
|
|
SCOPE: { |
75
|
|
|
|
|
|
|
my $hook = before { |
76
|
|
|
|
|
|
|
print "About to call create\n"; |
77
|
|
|
|
|
|
|
} call 'Person::create'; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# Advice will run for this call |
80
|
|
|
|
|
|
|
Person->create('Bob'); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Advice won't run for this call |
84
|
|
|
|
|
|
|
Person->create('Tom'); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Use a pre-packaged collection "Aspect" of Advice rules to change a class |
89
|
|
|
|
|
|
|
aspect Singleton => 'Foo::new'; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Define debugger breakpoints with high precision and conditionality |
94
|
|
|
|
|
|
|
aspect Breakpoint => call qr/^Foo::.+::Bar::when_/ & wantscalar & highest; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head1 DESCRIPTION |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head2 What is Aspect-Oriented Programming? |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Aspect-Oriented Programming (AOP) allows you to modularise concerns that |
103
|
|
|
|
|
|
|
would otherwise cut across many parts of a program and be problematic to |
104
|
|
|
|
|
|
|
implement and maintain. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
One common example is logging, where many small fragments code are typically |
107
|
|
|
|
|
|
|
spread throughout your entire codebase. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Another example is the implementation of design patterns, which combine or |
110
|
|
|
|
|
|
|
manipulate various kinds of classes in particular ways produce a known type of |
111
|
|
|
|
|
|
|
higher order behavior. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Because Aspect-Oritented Programming moves this scattered code into a single |
114
|
|
|
|
|
|
|
place, another major benefit is conditional compilation. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Features implemented via Aspects can be compiled in only in certain situations, |
117
|
|
|
|
|
|
|
and because of this Aspects are useful when debugging or testing large complex |
118
|
|
|
|
|
|
|
programs. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Aspects can implement features necesary for correctness of programs such as |
121
|
|
|
|
|
|
|
reactivity or synchronisation, and it can be used to add checking assertions |
122
|
|
|
|
|
|
|
to your or other people's modules. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
If necesary (although not recommended) you can also do "Monkey Patching", |
125
|
|
|
|
|
|
|
hijacking the functionality of some other module to act different when used in |
126
|
|
|
|
|
|
|
your program than when other everywhere else. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Aspects can be used to implement space or time optimisations. One popular use |
129
|
|
|
|
|
|
|
case is to add caching to a module or function that does not natively implement |
130
|
|
|
|
|
|
|
caching. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
See L for more info. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 About This Implementation |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
The Perl B module tries to closely follow the terminology of the |
137
|
|
|
|
|
|
|
Java AspectJ project wherever possible (L). |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
However due to the dynamic nature of the Perl language, several C |
140
|
|
|
|
|
|
|
features are useless for us: exception softening, mixin support, out-of-class |
141
|
|
|
|
|
|
|
method declarations, annotations, and others. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
The Perl B module is focused on subroutine matching and wrapping. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
It allows you to select collections of subroutines and conditions using a |
146
|
|
|
|
|
|
|
flexible pointcut language, and modify their behavior in any way you want. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
In this regard it provides a similar set of functionality to L, |
149
|
|
|
|
|
|
|
but with much more precision and with much more control and maintainability |
150
|
|
|
|
|
|
|
as the complexity of what you are doing increases. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
In addition, where the Java implementation of Aspect-Oriented Programming is |
153
|
|
|
|
|
|
|
only able to integrate at compile time, the nature of Perl means that the |
154
|
|
|
|
|
|
|
B module can weave in aspect code at run-time, and pointcuts can take |
155
|
|
|
|
|
|
|
advantage of run-time information and Perl-specific features like closures. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
This allows the Perl implementation of Aspect-Oriented Programming to be |
158
|
|
|
|
|
|
|
stateful and adaptive in a way that Java nroamlly cannot be. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head2 Terminology |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
One of the more opaque aspects (no pun intended) of Aspect-Oriented programming |
163
|
|
|
|
|
|
|
is that it has an entire unique set of terms that can be confusing for people |
164
|
|
|
|
|
|
|
learning to use the B module. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
In this section, we will attempt to define all the major terms in a way that |
167
|
|
|
|
|
|
|
will hopefully make sense to Perl programmers. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head3 What is an Aspect? |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
An I is a modular unit of cross-cutting implementation, consisting of |
172
|
|
|
|
|
|
|
"Advice" on "Pointcuts" (we'll define those two shortly, don't worry if they |
173
|
|
|
|
|
|
|
don't make sense for now). |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
In Perl, this would typically mean a package or module containing declarations |
176
|
|
|
|
|
|
|
of where to inject code, the code to run at these points, and any variables or |
177
|
|
|
|
|
|
|
support functions needed by the injected functionality. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
The most critical point here is that the Aspect represents a collection of |
180
|
|
|
|
|
|
|
many different injection points which collectively implement a single function |
181
|
|
|
|
|
|
|
or feature and which should be enabled on an all or nothing basis. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
For example, you might implement the Aspect B as a module |
184
|
|
|
|
|
|
|
which will inject hooks into a dozen different strategic places in your |
185
|
|
|
|
|
|
|
program to watch for valid-but-suspicious values and report these values to |
186
|
|
|
|
|
|
|
an external network server. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Aspects can often written to be highly reusable, and be released via the CPAN. |
189
|
|
|
|
|
|
|
When these generic aspects are written in the special namespace |
190
|
|
|
|
|
|
|
L they can be called using the following special shorthand. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
use Aspect; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Load and enable the Aspect::Library::NYTProf aspect to constrain profiling |
195
|
|
|
|
|
|
|
# to only the object constructors for each class in your program. |
196
|
|
|
|
|
|
|
aspect NYTProf => call qr/^MyProgram\b.*::new$/; |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head3 What is a Pointcut? |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
A I is a well-defined location at a point in the execution of a |
201
|
|
|
|
|
|
|
program at which Perl can inject functionality, in effect joining two different |
202
|
|
|
|
|
|
|
bits of code together. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
In the Perl B implementation, this consists only of the execution of |
205
|
|
|
|
|
|
|
named subroutines on the symbol table such as C. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
In other languages, additional join points can exist such as the instantiation |
208
|
|
|
|
|
|
|
or destruction of an object or the static initialisation of a class. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
A I is a well-defined set of join points, and any conditions that |
211
|
|
|
|
|
|
|
must be true when at these join points. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
Example include "All public methods in class C" or "Any non-recursive |
214
|
|
|
|
|
|
|
call to the function C". |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
We will discuss each of the available pointcut types later in this document. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
In addition to the default pointcut types it is possible to write your own |
219
|
|
|
|
|
|
|
specialised pointcut types, although this is challenging due to the complex |
220
|
|
|
|
|
|
|
API they follow to allow aggressive multi-pass optimisation. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
See L for more information. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head3 What is Advice? |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
I is code designed to run automatically at all of the join points in |
227
|
|
|
|
|
|
|
a particular pointcut. Advice comes in several types, instructing that the |
228
|
|
|
|
|
|
|
code be run C, C or C (in place of) the different join |
229
|
|
|
|
|
|
|
points in the pointcut. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Advice code is introduced lexically to the target join points. That is, the |
232
|
|
|
|
|
|
|
new functionality is injected in place to the existing program rather the |
233
|
|
|
|
|
|
|
class being extended into some new version. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
For example, function C may not support caching |
236
|
|
|
|
|
|
|
because it is unsafe to do so in the general case. But you know that in the |
237
|
|
|
|
|
|
|
case of your program, the reasons it is unsafe in the general case don't apply. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
So for your program you might use the L aspect to |
240
|
|
|
|
|
|
|
"Weave" Advice code into the C class which adds caching to the function |
241
|
|
|
|
|
|
|
by integrating it with L. |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Each of the different advice types needs to be used slightly differently, and |
244
|
|
|
|
|
|
|
are best employed for different types of jobs. We will discuss the use of each |
245
|
|
|
|
|
|
|
of the different advice types later in this document. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
But in general, the more specific advice type you use, the more optimisation |
248
|
|
|
|
|
|
|
can be applied to your advice declaration, and the less impact the advice will |
249
|
|
|
|
|
|
|
have on the speed of your program. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
In addition to the default pointcut types, it is (theoretically) possible to |
252
|
|
|
|
|
|
|
write your own specialised Advice types, although this would be extremely |
253
|
|
|
|
|
|
|
difficult and probably involve some form of XS programming. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
For the brave, see L and the source for the different advice |
256
|
|
|
|
|
|
|
classes for more information. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head3 What is Weaving? |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
I is the installation of advice code to the subs that match a pointcut, |
261
|
|
|
|
|
|
|
or might potentially match depending on certain run-time conditions. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
In the Perl B module, weaving happens on the declaration of each |
264
|
|
|
|
|
|
|
advice block. Unweaving happens when a lexically-created advice variable goes |
265
|
|
|
|
|
|
|
out of scope. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Unfortunately, due to the nature of the mechanism B uses to hook into |
268
|
|
|
|
|
|
|
function calls, unweaving can never be guarenteed to be round-trip clean. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
While the pointcut matching logic and advice code will never be run for unwoven |
271
|
|
|
|
|
|
|
advice, it may be necesary to leave the underlying hooking artifact in place on |
272
|
|
|
|
|
|
|
the join point indefinitely (imposing a small performance penalty and preventing |
273
|
|
|
|
|
|
|
clean up of the relevant advice closure from memory). |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Programs that repeatedly weave and unweave during execution will thus gradually |
276
|
|
|
|
|
|
|
slow down and leak memory, and so is discouraged despite being permitted. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
If advice needs to be repeatedly enabled and disabled you should instead |
279
|
|
|
|
|
|
|
consider using the C pointcut and a variable in the aspect package or |
280
|
|
|
|
|
|
|
a closure to introduce a remote "on/off" switch for the aspect. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
into the advice code. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
package My::Aspect; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
my $switch = 1; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
before { |
289
|
|
|
|
|
|
|
print "Calling Foo::bar\n"; |
290
|
|
|
|
|
|
|
} call 'Foo::bar' & true { $switch }; |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub enable { |
293
|
|
|
|
|
|
|
$switch = 1; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub disable { |
297
|
|
|
|
|
|
|
$switch = 0; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
1; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Under the covers weaving is done using a mechanism that is very similar to |
303
|
|
|
|
|
|
|
the venerable L, although in some areas B will try to |
304
|
|
|
|
|
|
|
make use of faster mechanisms if it knows these are safe. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head2 Feature Summary |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=over |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=item * |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Create permanent pointcuts, advice, and aspects at compile time or run-time. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=item * |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Flexible pointcut language: select subs to match using string equality, |
317
|
|
|
|
|
|
|
regexp, or C ref. Match currently running sub, a sub in the call |
318
|
|
|
|
|
|
|
flow, calls in particular void, scalar, or array contexts, or only the highest |
319
|
|
|
|
|
|
|
call in a set of recursive calls. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=item * |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
Build pointcuts composed of a logical expression of other pointcuts, |
324
|
|
|
|
|
|
|
using conjunction, disjunction, and negation. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=item * |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
In advice code, you can modify parameter list for matched sub, modify return |
329
|
|
|
|
|
|
|
value, throw or supress exceptions, decide whether or not to proceed to matched |
330
|
|
|
|
|
|
|
sub, access a C ref for matched sub, and access the context of any call |
331
|
|
|
|
|
|
|
flow pointcuts that were matched, if they exist. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item * |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Add/remove advice and entire aspects lexically during run-time. The scope of |
336
|
|
|
|
|
|
|
advice and aspect objects, is the scope of their effect (This does, however, |
337
|
|
|
|
|
|
|
come with some caveats). |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item * |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
A basic library of reusable aspects. A base class makes it easy to create your |
342
|
|
|
|
|
|
|
own reusable aspects. The L aspect is an |
343
|
|
|
|
|
|
|
example of how to interface with AOP-like modules from CPAN. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=back |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=head2 Using Aspect.pm |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
The B package allows you to create pointcuts, advice, and aspects in a |
350
|
|
|
|
|
|
|
simple declarative fashion. This declarative form is a simple facade on top of |
351
|
|
|
|
|
|
|
the Perl AOP framework, which you can also use directly if you need the |
352
|
|
|
|
|
|
|
increased level of control or you feel the declarative form is not clear enough. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
For example, the following two examples are equivalent. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
use Aspect; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Declarative advice creation |
359
|
|
|
|
|
|
|
before { |
360
|
|
|
|
|
|
|
print "Calling " . $_->sub_name . "\n"; |
361
|
|
|
|
|
|
|
} call 'Function::one' |
362
|
|
|
|
|
|
|
| call 'Function::two'; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# Longhand advice creation |
365
|
|
|
|
|
|
|
Aspect::Advice::Before->new( |
366
|
|
|
|
|
|
|
Aspect::Pointcut::Or->new( |
367
|
|
|
|
|
|
|
Aspect::Pointcut::Call->new('Function::one'), |
368
|
|
|
|
|
|
|
Aspect::Pointcut::Call->new('Function::two'), |
369
|
|
|
|
|
|
|
), |
370
|
|
|
|
|
|
|
sub { |
371
|
|
|
|
|
|
|
print "Calling " . $_->sub_name . "\n"; |
372
|
|
|
|
|
|
|
}, |
373
|
|
|
|
|
|
|
); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
You will be mostly working with this package (B) and the |
376
|
|
|
|
|
|
|
L package, which provides the methods for getting information |
377
|
|
|
|
|
|
|
about the call to the join point within advice code. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
When you C |
380
|
|
|
|
|
|
|
functions. These are all factories that allow you to create pointcuts, |
381
|
|
|
|
|
|
|
advice, and aspects. |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=head1 FUNCTIONS |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
The following functions are exported by default (and are documented as such) |
386
|
|
|
|
|
|
|
but are also available directly in Aspect:: namespace as well if needed. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
They are documented in order from the simplest and and most common pointcut |
389
|
|
|
|
|
|
|
declarator to the highest level declarator for enabling complete aspect classes. |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=cut |
392
|
|
|
|
|
|
|
|
393
|
21
|
|
|
21
|
|
442549
|
use 5.008002; |
|
21
|
|
|
|
|
64
|
|
394
|
21
|
|
|
21
|
|
99
|
use strict; |
|
21
|
|
|
|
|
29
|
|
|
21
|
|
|
|
|
460
|
|
395
|
21
|
|
|
21
|
|
108
|
use warnings; |
|
21
|
|
|
|
|
33
|
|
|
21
|
|
|
|
|
644
|
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# Added by eilara as hack around caller() core dump |
398
|
|
|
|
|
|
|
# NOTE: Now we've switched to Sub::Uplevel can this be removed? |
399
|
|
|
|
|
|
|
# -- ADAMK |
400
|
21
|
|
|
21
|
|
10510
|
use Carp::Heavy (); |
|
21
|
|
|
|
|
2979
|
|
|
21
|
|
|
|
|
431
|
|
401
|
21
|
|
|
21
|
|
107
|
use Carp (); |
|
21
|
|
|
|
|
33
|
|
|
21
|
|
|
|
|
410
|
|
402
|
21
|
|
|
21
|
|
10817
|
use Params::Util 1.00 (); |
|
21
|
|
|
|
|
99355
|
|
|
21
|
|
|
|
|
668
|
|
403
|
21
|
|
|
21
|
|
10964
|
use Sub::Install 0.92 (); |
|
21
|
|
|
|
|
33250
|
|
|
21
|
|
|
|
|
552
|
|
404
|
21
|
|
|
21
|
|
7319
|
use Sub::Uplevel 0.2002 (); |
|
21
|
|
|
|
|
15962
|
|
|
21
|
|
|
|
|
500
|
|
405
|
21
|
|
|
21
|
|
9728
|
use Aspect::Pointcut (); |
|
21
|
|
|
|
|
61
|
|
|
21
|
|
|
|
|
478
|
|
406
|
21
|
|
|
21
|
|
130
|
use Aspect::Pointcut::Or (); |
|
21
|
|
|
|
|
29
|
|
|
21
|
|
|
|
|
288
|
|
407
|
21
|
|
|
21
|
|
75
|
use Aspect::Pointcut::And (); |
|
21
|
|
|
|
|
27
|
|
|
21
|
|
|
|
|
256
|
|
408
|
21
|
|
|
21
|
|
74
|
use Aspect::Pointcut::Not (); |
|
21
|
|
|
|
|
28
|
|
|
21
|
|
|
|
|
254
|
|
409
|
21
|
|
|
21
|
|
8097
|
use Aspect::Pointcut::True (); |
|
21
|
|
|
|
|
36
|
|
|
21
|
|
|
|
|
375
|
|
410
|
21
|
|
|
21
|
|
7696
|
use Aspect::Pointcut::Call (); |
|
21
|
|
|
|
|
34
|
|
|
21
|
|
|
|
|
431
|
|
411
|
21
|
|
|
21
|
|
8343
|
use Aspect::Pointcut::Cflow (); |
|
21
|
|
|
|
|
49
|
|
|
21
|
|
|
|
|
425
|
|
412
|
21
|
|
|
21
|
|
8337
|
use Aspect::Pointcut::Highest (); |
|
21
|
|
|
|
|
47
|
|
|
21
|
|
|
|
|
374
|
|
413
|
21
|
|
|
21
|
|
7797
|
use Aspect::Pointcut::Throwing (); |
|
21
|
|
|
|
|
44
|
|
|
21
|
|
|
|
|
391
|
|
414
|
21
|
|
|
21
|
|
98
|
use Aspect::Pointcut::Returning (); |
|
21
|
|
|
|
|
26
|
|
|
21
|
|
|
|
|
250
|
|
415
|
21
|
|
|
21
|
|
7677
|
use Aspect::Pointcut::Wantarray (); |
|
21
|
|
|
|
|
43
|
|
|
21
|
|
|
|
|
433
|
|
416
|
21
|
|
|
21
|
|
7305
|
use Aspect::Advice (); |
|
21
|
|
|
|
|
37
|
|
|
21
|
|
|
|
|
454
|
|
417
|
21
|
|
|
21
|
|
7799
|
use Aspect::Advice::After (); |
|
21
|
|
|
|
|
37
|
|
|
21
|
|
|
|
|
367
|
|
418
|
21
|
|
|
21
|
|
8503
|
use Aspect::Advice::Around (); |
|
21
|
|
|
|
|
42
|
|
|
21
|
|
|
|
|
355
|
|
419
|
21
|
|
|
21
|
|
7887
|
use Aspect::Advice::Before (); |
|
21
|
|
|
|
|
43
|
|
|
21
|
|
|
|
|
350
|
|
420
|
21
|
|
|
21
|
|
109
|
use Aspect::Point (); |
|
21
|
|
|
|
|
25
|
|
|
21
|
|
|
|
|
287
|
|
421
|
21
|
|
|
21
|
|
78
|
use Aspect::Point::Static (); |
|
21
|
|
|
|
|
27
|
|
|
21
|
|
|
|
|
265
|
|
422
|
21
|
|
|
21
|
|
75
|
use Aspect::Point::After (); |
|
21
|
|
|
|
|
22
|
|
|
21
|
|
|
|
|
246
|
|
423
|
21
|
|
|
21
|
|
74
|
use Aspect::Point::Around (); |
|
21
|
|
|
|
|
23
|
|
|
21
|
|
|
|
|
236
|
|
424
|
21
|
|
|
21
|
|
67
|
use Aspect::Point::Before (); |
|
21
|
|
|
|
|
24
|
|
|
21
|
|
|
|
|
17591
|
|
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
our $VERSION = '0.97_06'; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# Track the location of exported functions so that pointcuts |
429
|
|
|
|
|
|
|
# can avoid accidentally binding them. |
430
|
|
|
|
|
|
|
our %EXPORTED = (); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
###################################################################### |
437
|
|
|
|
|
|
|
# Public (Exported) Functions |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=pod |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 call |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
my $single = call 'Person::get_address'; |
444
|
|
|
|
|
|
|
my $multiple = call qr/^Person::get_/; |
445
|
|
|
|
|
|
|
my $complex = call sub { lc($_[0]) eq 'person::get_address' }; |
446
|
|
|
|
|
|
|
my $object = Aspect::Pointcut::Call->new('Person::get_address'); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
The most common pointcut is C. All three of the examples will match the |
449
|
|
|
|
|
|
|
calling of C as defined in the symbol table at the |
450
|
|
|
|
|
|
|
time an advice is declared. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
The C declarator takes a single parameter which is the pointcut spec, |
453
|
|
|
|
|
|
|
and can be provided in three different forms. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
B |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Select only the specific full resolved subroutine whose name is equal to the |
458
|
|
|
|
|
|
|
specification string. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
For example C will only match the plain C method |
461
|
|
|
|
|
|
|
and will not match the longer C method. |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
B |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
Select all subroutines whose name matches the regular expression. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
The following will match all the subs defined on the C class, but not |
468
|
|
|
|
|
|
|
on the C or any other child classes. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
$p = call qr/^Person::\w+$/; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
B |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Select all subroutines where the supplied code returns true when passed a |
475
|
|
|
|
|
|
|
full resolved subroutine name as the only parameter. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
The following will match all calls to subroutines whose names are a key in the |
478
|
|
|
|
|
|
|
hash C<%subs_to_match>: |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
$p = call sub { |
481
|
|
|
|
|
|
|
exists $subs_to_match{$_[0]}; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
For more information on the C pointcut see L. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=cut |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
sub call ($) { |
489
|
138
|
|
|
138
|
1
|
62131
|
Aspect::Pointcut::Call->new(@_); |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=pod |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=head2 cflow |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
before { |
497
|
|
|
|
|
|
|
print "Called My::foo somewhere within My::bar\n"; |
498
|
|
|
|
|
|
|
} call 'My::foo' |
499
|
|
|
|
|
|
|
& cflow 'My::bar'; |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
The C declarator is used to specify that the join point must be somewhere |
502
|
|
|
|
|
|
|
within the control flow of the C function. That is, at the time |
503
|
|
|
|
|
|
|
C is being called somewhere up the call stack is C. |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
The parameters to C are identical to the parameters to C. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Due to an idiosyncracy in the way C is implemented, they do not always |
508
|
|
|
|
|
|
|
parse properly well when joined with an operator. In general, you should use |
509
|
|
|
|
|
|
|
any C operator last in your pointcut specification, or use explicit |
510
|
|
|
|
|
|
|
braces for it. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# This works fine |
513
|
|
|
|
|
|
|
my $x = call 'My::foo' & cflow 'My::bar'; |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# This will error |
516
|
|
|
|
|
|
|
my $y = cflow 'My::bar' & call 'My::foo'; |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# Use explicit braces if you can't have the flow last |
519
|
|
|
|
|
|
|
my $z = cflow('My::bar') & call 'My::foo'; |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
For more information on the C pointcut, see L. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=cut |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub cflow ($;$) { |
526
|
7
|
|
|
7
|
1
|
49
|
Aspect::Pointcut::Cflow->new(@_); |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=pod |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=head2 wantlist |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
my $pointcut = call 'Foo::bar' & wantlist; |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
The C pointcut traps a condition based on Perl C context, |
536
|
|
|
|
|
|
|
when a function is called in list context. When used with C, this |
537
|
|
|
|
|
|
|
pointcut can be used to trap list-context calls to one or more functions, while |
538
|
|
|
|
|
|
|
letting void or scalar context calls continue as normal. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
For more information on the C pointcut see |
541
|
|
|
|
|
|
|
L. |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=cut |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub wantlist () { |
546
|
13
|
|
|
13
|
1
|
78
|
Aspect::Pointcut::Wantarray->new(1); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=pod |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=head2 wantscalar |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
my $pointcut = call 'Foo::bar' & wantscalar; |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
The C pointcut traps a condition based on Perl C context, |
556
|
|
|
|
|
|
|
when a function is called in scalar context. When used with C, this |
557
|
|
|
|
|
|
|
pointcut can be used to trap scalar-context calls to one or more functions, |
558
|
|
|
|
|
|
|
while letting void or list context calls continue as normal. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
For more information on the C pointcut see |
561
|
|
|
|
|
|
|
L. |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=cut |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
sub wantscalar () { |
566
|
10
|
|
|
10
|
1
|
55
|
Aspect::Pointcut::Wantarray->new(''); |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=pod |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head2 wantvoid |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
my $bug = call 'Foo::get_value' & wantvoid; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
The C pointcut traps a condition based on Perl C context, |
576
|
|
|
|
|
|
|
when a function is called in void context. When used with C, this pointcut |
577
|
|
|
|
|
|
|
can be used to trap void-context calls to one or more functions, while letting |
578
|
|
|
|
|
|
|
scalar or list context calls continue as normal. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
This is particularly useful for methods which make no sense to call in void |
581
|
|
|
|
|
|
|
context, such as getters or other methods calculating and returning a useful |
582
|
|
|
|
|
|
|
result. |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
For more information on the C pointcut see |
585
|
|
|
|
|
|
|
L. |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=cut |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
sub wantvoid () { |
590
|
9
|
|
|
9
|
1
|
56
|
Aspect::Pointcut::Wantarray->new(undef); |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
=pod |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
=head2 highest |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
my $entry = call 'Foo::recurse' & highest; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
The C pointcut is used to trap the first time a particular function |
600
|
|
|
|
|
|
|
is encountered, while ignoring any subsequent recursive calls into the same |
601
|
|
|
|
|
|
|
pointcut. |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
It is unusual in that unlike all other types of pointcuts it is stateful, and |
604
|
|
|
|
|
|
|
so some detailed explaination is needed to understand how it will behave. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
Pointcut declarators follow normal Perl precedence and shortcutting in the same |
607
|
|
|
|
|
|
|
way that a typical set of C might do for regular code. |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
When the C is evaluated for the first time it returns true and a |
610
|
|
|
|
|
|
|
counter is to track the depth of the call stack. This counter is bound to the |
611
|
|
|
|
|
|
|
join point itself, and will decrement back again once we exit the advice code. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
If we encounter another function that is potentially contained in the same |
614
|
|
|
|
|
|
|
pointcut, then C will always return false. |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
In this manner, you can trigger functionality to run only at the outermost |
617
|
|
|
|
|
|
|
call into a recursive series of functions, or you can negate the pointcut |
618
|
|
|
|
|
|
|
with C and look for recursive calls into a function when there |
619
|
|
|
|
|
|
|
shouldn't be any recursion. |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
In the current implementation, the semantics and behaviour of pointcuts |
622
|
|
|
|
|
|
|
containing multiple highest declarators is not defined (and the current |
623
|
|
|
|
|
|
|
implementation is also not amenable to supporting it). |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
For these reasons, the usage of multiple highest declarators such as in the |
626
|
|
|
|
|
|
|
following example is not support, and so the following will throw an exception. |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
before { |
629
|
|
|
|
|
|
|
print "This advice will not compile\n"; |
630
|
|
|
|
|
|
|
} wantscalar & ( |
631
|
|
|
|
|
|
|
(call 'My::foo' & highest) |
632
|
|
|
|
|
|
|
| |
633
|
|
|
|
|
|
|
(call 'My::bar' & highest) |
634
|
|
|
|
|
|
|
); |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
This limitation may change in future releases. Feedback welcome. |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
For more information on the C pointcut see |
639
|
|
|
|
|
|
|
L. |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=cut |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub highest () { |
644
|
3
|
|
|
3
|
1
|
21
|
Aspect::Pointcut::Highest->new; |
645
|
|
|
|
|
|
|
} |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=pod |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=head2 throwing |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
my $string = throwing qr/does not exist/; |
652
|
|
|
|
|
|
|
my $object = throwing 'Exception::Class'; |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
The C pointcut is used with the C (and C) to |
655
|
|
|
|
|
|
|
restrict the pointcut so advice code is only fired for a specific die message |
656
|
|
|
|
|
|
|
or a particular exception class (or subclass). |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
The C declarator takes a single parameter which is the pointcut spec, |
659
|
|
|
|
|
|
|
and can be provided in two different forms. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
B |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
If a regular expression is passed to C it will be matched against |
664
|
|
|
|
|
|
|
the exception if and only if the exception is a plain string. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Thus, the regexp form can be used to trap unstructured errors emitted by C |
667
|
|
|
|
|
|
|
or C while B trapping any formal exception objects of any kind. |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
B |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
If a string is passed to C it will be treated as a class name and |
672
|
|
|
|
|
|
|
will be matched against the exception via an C method call if and only |
673
|
|
|
|
|
|
|
if the exception is an object. |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Thus, the string form can be used to trap and handle specific types of |
676
|
|
|
|
|
|
|
exceptions while allowing other types of exceptions or raw string errors to |
677
|
|
|
|
|
|
|
pass through. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
For more information on the C pointcut see |
680
|
|
|
|
|
|
|
L. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=cut |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub throwing (;$) { |
685
|
0
|
|
|
0
|
1
|
0
|
Aspect::Pointcut::Throwing->new(@_); |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=pod |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=head2 returning |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
after { |
693
|
|
|
|
|
|
|
print "No exception\n"; |
694
|
|
|
|
|
|
|
} call 'Foo::bar' & returning; |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
The C pointcut is used with C advice types to indicate the |
697
|
|
|
|
|
|
|
join point should only occur when a function is returning B throwing |
698
|
|
|
|
|
|
|
an exception. |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=cut |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
sub returning () { |
703
|
1
|
|
|
1
|
1
|
12
|
Aspect::Pointcut::Returning->new; |
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=pod |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=head2 true |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
# Intercept an adjustable random percentage of calls to a function |
711
|
|
|
|
|
|
|
our $RATE = 0.01; |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
before { |
714
|
|
|
|
|
|
|
print "The few, the brave, the 1%\n"; |
715
|
|
|
|
|
|
|
} call 'My::foo' |
716
|
|
|
|
|
|
|
& true { |
717
|
|
|
|
|
|
|
rand() < $RATE |
718
|
|
|
|
|
|
|
}; |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
Because of the lengths that B goes to internally to optimise the |
721
|
|
|
|
|
|
|
selection and interception of calls, writing your own custom pointcuts can |
722
|
|
|
|
|
|
|
be very difficult. |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
When a custom or unusual pattern of interception is needed, often all that is |
725
|
|
|
|
|
|
|
desired is to extend a relatively normal pointcut with an extra caveat. |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
To allow for this scenario, B provides the C pointcut. |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
This pointcut allows you to specify any arbitrary code to match on. This code |
730
|
|
|
|
|
|
|
will be executed at run-time if the join point matches all previous conditions. |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
The join point matches if the function or closure returns true, and does not |
733
|
|
|
|
|
|
|
match if the code returns false or nothing at all. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
=cut |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
sub true (&) { |
738
|
1
|
|
|
1
|
1
|
13
|
Aspect::Pointcut::True->new(@_); |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=pod |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=head2 before |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
before { |
746
|
|
|
|
|
|
|
# Don't call the function, return instead |
747
|
|
|
|
|
|
|
$_->return_value(1); |
748
|
|
|
|
|
|
|
} call 'My::foo'; |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
The B advice declaration is used to defined advice code that will be |
751
|
|
|
|
|
|
|
run instead of the code originally at the join points, but continuing on to the |
752
|
|
|
|
|
|
|
real function if no action is taken to say otherwise. |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
When called in void context, as shown above, C will install the advice |
755
|
|
|
|
|
|
|
permanently into your program. |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
When called in scalar context, as shown below, C will return a guard |
758
|
|
|
|
|
|
|
object and enable the advice for as long as that guard object continues to |
759
|
|
|
|
|
|
|
remain in scope or otherwise avoid being destroyed. |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
SCOPE: { |
762
|
|
|
|
|
|
|
my $guard = before { |
763
|
|
|
|
|
|
|
print "Hello World!\n"; |
764
|
|
|
|
|
|
|
} call 'My::foo'; |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# This will print |
767
|
|
|
|
|
|
|
My::foo(); |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
# This will NOT print |
771
|
|
|
|
|
|
|
My::foo(); |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
Because the end result of the code at the join points is irrelevant to this |
774
|
|
|
|
|
|
|
type of advice and the Aspect system does not need to hang around and maintain |
775
|
|
|
|
|
|
|
control during the join point, the underlying implementation is done in a way |
776
|
|
|
|
|
|
|
that is by far the fastest and with the least impact (essentially none) on the |
777
|
|
|
|
|
|
|
execution of your program. |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
You are B encouraged to use C advice wherever possible for the |
780
|
|
|
|
|
|
|
current implementation, resorting to the other advice types when you truly need |
781
|
|
|
|
|
|
|
to be there are the end of the join point execution (or on both sides of it). |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
For more information, see L. |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=cut |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
sub before (&$) { |
788
|
25
|
|
|
25
|
1
|
277
|
Aspect::Advice::Before->new( |
789
|
|
|
|
|
|
|
lexical => defined wantarray, |
790
|
|
|
|
|
|
|
code => $_[0], |
791
|
|
|
|
|
|
|
pointcut => $_[1], |
792
|
|
|
|
|
|
|
); |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=pod |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=head2 after |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
# Confuse a program by bizarely swapping return values and exceptions |
800
|
|
|
|
|
|
|
after { |
801
|
|
|
|
|
|
|
if ( $_->exception ) { |
802
|
|
|
|
|
|
|
$_->return_value($_->exception); |
803
|
|
|
|
|
|
|
} else { |
804
|
|
|
|
|
|
|
$_->exception($_->return_value); |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
} call 'My::foo' & wantscalar; |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
The C declarator is used to create advice in which the advice code will |
809
|
|
|
|
|
|
|
be run after the join point has run, regardless of whether the function return |
810
|
|
|
|
|
|
|
correctly or throws an exception. |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
This advice should be used when you need to deal flexibly with return values, |
813
|
|
|
|
|
|
|
but might also want to handle exceptions. Use of this advice is relatively |
814
|
|
|
|
|
|
|
rare, as most of the time you want something more flexible like C, |
815
|
|
|
|
|
|
|
or something more specific like C or C. |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
For more information, see L. |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=cut |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
sub after (&$) { |
822
|
24
|
|
|
24
|
1
|
256
|
Aspect::Advice::After->new( |
823
|
|
|
|
|
|
|
lexical => defined wantarray, |
824
|
|
|
|
|
|
|
code => $_[0], |
825
|
|
|
|
|
|
|
pointcut => $_[1], |
826
|
|
|
|
|
|
|
); |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
sub after_returning (&$) { |
830
|
18
|
|
|
18
|
0
|
197
|
Aspect::Advice::After->new( |
831
|
|
|
|
|
|
|
lexical => defined wantarray, |
832
|
|
|
|
|
|
|
code => $_[0], |
833
|
|
|
|
|
|
|
pointcut => Aspect::Pointcut::And->new( |
834
|
|
|
|
|
|
|
Aspect::Pointcut::Returning->new, |
835
|
|
|
|
|
|
|
$_[1], |
836
|
|
|
|
|
|
|
), |
837
|
|
|
|
|
|
|
); |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
sub after_throwing (&$) { |
841
|
16
|
|
|
16
|
0
|
125
|
Aspect::Advice::After->new( |
842
|
|
|
|
|
|
|
lexical => defined wantarray, |
843
|
|
|
|
|
|
|
code => $_[0], |
844
|
|
|
|
|
|
|
pointcut => Aspect::Pointcut::And->new( |
845
|
|
|
|
|
|
|
Aspect::Pointcut::Throwing->new, |
846
|
|
|
|
|
|
|
$_[1], |
847
|
|
|
|
|
|
|
), |
848
|
|
|
|
|
|
|
); |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=pod |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
=head2 around |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
# Trace execution time for a function |
856
|
|
|
|
|
|
|
around { |
857
|
|
|
|
|
|
|
my @start = Time::HiRes::gettimeofday(); |
858
|
|
|
|
|
|
|
$_->proceed; |
859
|
|
|
|
|
|
|
my @stop = Time::HiRes::gettimeofday(); |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
my $elapsed = Time::HiRes::tv_interval( \@start, \@stop ); |
862
|
|
|
|
|
|
|
print "My::foo executed in $elapsed seconds\n"; |
863
|
|
|
|
|
|
|
} call 'My::foo'; |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
The C declarator is used to create the most general form of advice, |
866
|
|
|
|
|
|
|
and can be used to implement the most high level functionality. |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
It allows you to make changes to the calling parameters, to change the result |
869
|
|
|
|
|
|
|
of the function, to subvert or prevent the calling altogether, and to do so |
870
|
|
|
|
|
|
|
while storing extra lexical state of your own across the join point. |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
For example, the code shown above tracks the time at which a single function |
873
|
|
|
|
|
|
|
is called and returned, and then uses the two pieces of information to track |
874
|
|
|
|
|
|
|
the execution time of the call. |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
Similar functionality to the above is used to implement the CPAN modules |
877
|
|
|
|
|
|
|
L and the more complex L. |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
Within the C advice code, the C<$_-Eproceed> method is used to call |
880
|
|
|
|
|
|
|
the original function with whatever the current parameter context is, storing |
881
|
|
|
|
|
|
|
the result (whether return values or an exception) in the context as well. |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
Alternatively, you can use the C method to get access to a reference |
884
|
|
|
|
|
|
|
to the original function and call it directly without using context |
885
|
|
|
|
|
|
|
parameters and without storing the function results. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
around { |
888
|
|
|
|
|
|
|
$_->original->('alternative param'); |
889
|
|
|
|
|
|
|
$_->return_value('fake result'); |
890
|
|
|
|
|
|
|
} call 'My::foo'; |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
The above example calls the original function directly with an alternative |
893
|
|
|
|
|
|
|
parameter in void context (regardless of the original C context) |
894
|
|
|
|
|
|
|
ignoring any return values. It then sets an entirely made up return value of |
895
|
|
|
|
|
|
|
it's own. |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
Although it is the most powerful advice type, C is also the slowest |
898
|
|
|
|
|
|
|
advice type with the highest memory cost per join point. Where possible, you |
899
|
|
|
|
|
|
|
should try to use a more specific advice type. |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
For more information, see L. |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
=cut |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
sub around (&$) { |
906
|
25
|
|
|
25
|
1
|
290
|
Aspect::Advice::Around->new( |
907
|
|
|
|
|
|
|
lexical => defined wantarray, |
908
|
|
|
|
|
|
|
code => $_[0], |
909
|
|
|
|
|
|
|
pointcut => $_[1], |
910
|
|
|
|
|
|
|
); |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=pod |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=head2 aspect |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
aspect Singleton => 'Foo::new'; |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
The C declarator is used to enable complete reusable aspects. |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
The first parameter to C identifies the aspect library class. If the |
922
|
|
|
|
|
|
|
parameter is a fully resolved class name (i.e. it contains double colons like |
923
|
|
|
|
|
|
|
Foo::Bar) the value it will be used directly. If it is a simple C |
924
|
|
|
|
|
|
|
without colons then it will be interpreted as C. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
If the aspect class is not loaded, it will be loaded for you and validated as |
927
|
|
|
|
|
|
|
being a subclass of C. |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
And further paramters will be passed on to the constructor for that class. See |
930
|
|
|
|
|
|
|
the documentation for each class for more information on the appropriate |
931
|
|
|
|
|
|
|
parameters for that class. |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
As with each individual advice type complete aspects can be defined globally |
934
|
|
|
|
|
|
|
by using C in void context, or lexically via a guard object by calling |
935
|
|
|
|
|
|
|
C in scalar context. |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# Break on the topmost call to function for a limited time |
938
|
|
|
|
|
|
|
SCOPE: { |
939
|
|
|
|
|
|
|
my $break = aspect Breakpoint => call 'My::foo' & highest; |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
do_something(); |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
For more information on writing reusable aspects, see L. |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
=cut |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
sub aspect { |
949
|
7
|
|
|
7
|
1
|
914
|
my $class = _LIBRARY(shift); |
950
|
7
|
|
|
|
|
785
|
return $class->new( |
951
|
|
|
|
|
|
|
lexical => defined wantarray, |
952
|
|
|
|
|
|
|
args => [ @_ ], |
953
|
|
|
|
|
|
|
); |
954
|
|
|
|
|
|
|
} |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
###################################################################### |
962
|
|
|
|
|
|
|
# Import Logic |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub import { |
965
|
21
|
|
|
21
|
|
186
|
my $class = shift; |
966
|
21
|
|
|
|
|
31
|
my $legacy = 0; |
967
|
21
|
|
|
|
|
62
|
my $into = caller(); |
968
|
21
|
|
|
|
|
384
|
while ( @_ ) { |
969
|
0
|
|
|
|
|
0
|
my $value = shift; |
970
|
0
|
0
|
|
|
|
0
|
if ( $value eq ':legacy' ) { |
971
|
0
|
|
|
|
|
0
|
$legacy = 1; |
972
|
|
|
|
|
|
|
} else { |
973
|
0
|
|
|
|
|
0
|
Carp::croak("Unknown or unsupported import param '$value'"); |
974
|
|
|
|
|
|
|
} |
975
|
|
|
|
|
|
|
} |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# Install unchanged legacy functions |
978
|
21
|
|
|
|
|
42
|
foreach ( qw{ aspect before call cflow } ) { |
979
|
84
|
|
|
|
|
287
|
Sub::Install::install_sub( { |
980
|
|
|
|
|
|
|
code => $_, |
981
|
|
|
|
|
|
|
into => $into, |
982
|
|
|
|
|
|
|
} ); |
983
|
84
|
|
|
|
|
4169
|
$EXPORTED{"${into}::$_"} = 1; |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
# Install functions that change between API versions |
987
|
|
|
|
|
|
|
Sub::Install::install_sub( { |
988
|
21
|
50
|
|
|
|
122
|
code => $legacy ? 'after_returning' : 'after', |
989
|
|
|
|
|
|
|
as => 'after', |
990
|
|
|
|
|
|
|
into => $into, |
991
|
|
|
|
|
|
|
} ); |
992
|
21
|
|
|
|
|
712
|
$EXPORTED{"${into}::after"} = 1; |
993
|
|
|
|
|
|
|
|
994
|
21
|
50
|
|
|
|
70
|
unless ( $legacy ) { |
995
|
|
|
|
|
|
|
# Install new generation API functions |
996
|
21
|
|
|
|
|
38
|
foreach ( qw{ |
997
|
|
|
|
|
|
|
around |
998
|
|
|
|
|
|
|
throwing returning |
999
|
|
|
|
|
|
|
wantlist wantscalar wantvoid |
1000
|
|
|
|
|
|
|
true highest |
1001
|
|
|
|
|
|
|
} ) { |
1002
|
168
|
|
|
|
|
381
|
Sub::Install::install_sub( { |
1003
|
|
|
|
|
|
|
code => $_, |
1004
|
|
|
|
|
|
|
into => $into, |
1005
|
|
|
|
|
|
|
} ); |
1006
|
168
|
|
|
|
|
5329
|
$EXPORTED{"${into}::$_"} = 1; |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
|
1010
|
21
|
|
|
|
|
36294
|
return 1; |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
###################################################################### |
1018
|
|
|
|
|
|
|
# Private Functions |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# Run-time use call |
1021
|
|
|
|
|
|
|
# NOTE: Do we REALLY need to do this as a use? |
1022
|
|
|
|
|
|
|
# If the ->import method isn't important, change to native require. |
1023
|
|
|
|
|
|
|
sub _LIBRARY { |
1024
|
7
|
|
|
7
|
|
17
|
my $package = shift; |
1025
|
7
|
50
|
|
|
|
198
|
if ( Params::Util::_IDENTIFIER($package) ) { |
1026
|
7
|
|
|
|
|
109
|
$package = "Aspect::Library::$package"; |
1027
|
|
|
|
|
|
|
} |
1028
|
7
|
|
|
|
|
152
|
Params::Util::_DRIVER($package, 'Aspect::Library'); |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
1; |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=pod |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
=head1 OPERATORS |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
=head2 Pointcut & |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
Overloading of bitwise C<&> for pointcut declarations allows a natural looking |
1040
|
|
|
|
|
|
|
boolean "and" logic for pointcuts. When using the C<&> operator the combined |
1041
|
|
|
|
|
|
|
pointcut expression will match if all pointcut subexpressions match. |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
In the original Java AspectJ framework, the subexpressions are considered to |
1044
|
|
|
|
|
|
|
be a union without an inherent order at all. In Perl you may treat them as |
1045
|
|
|
|
|
|
|
ordered since they are ordered internally, but since all subexpressions run |
1046
|
|
|
|
|
|
|
anyway you should probably not do anything that relies on this order. The |
1047
|
|
|
|
|
|
|
optimiser may do interesting things with order in future, or we may move to an |
1048
|
|
|
|
|
|
|
unordered implementation. |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
For more information, see L. |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
=head2 Pointcut | |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
Overloading of bitwise C<|> for pointcut declarations allows a natural looking |
1055
|
|
|
|
|
|
|
boolean "or" logic for pointcuts. When using the C<|> operator the combined |
1056
|
|
|
|
|
|
|
pointcut expression will match if either pointcut subexpressions match. |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
The subexpressions are ostensibly considered without any inherent order, and |
1059
|
|
|
|
|
|
|
you should treat them that way when you can. However, they are internally |
1060
|
|
|
|
|
|
|
ordered and shortcutting will be applied as per normal Perl expressions. So for |
1061
|
|
|
|
|
|
|
speed reasons, you may with to put cheap pointcut declarators before expensive |
1062
|
|
|
|
|
|
|
ones where you can. |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
The optimiser may do interesting things with order in future, or we may move to |
1065
|
|
|
|
|
|
|
an unordered implementation. So as a general rule, avoid things that require |
1066
|
|
|
|
|
|
|
order while using order to optimise where you can. |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
For more information, see L. |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=head2 Pointcut ! |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
Overload of negation C for pointcut declarations allows a natural looking |
1073
|
|
|
|
|
|
|
boolean "not" logic for pointcuts. When using the C operator the resulting |
1074
|
|
|
|
|
|
|
pointcut expression will match if the single subexpression does B match. |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
For more information, see L. |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
=head1 ADVICE CONTEXT METHODS |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
The following methods are available in the advice code for one or more advice |
1081
|
|
|
|
|
|
|
types. Different sets of methods are available for the different advice types. |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
The actual objects involved are those within the L tree. |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
=head2 type |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
The C method is a convenience provided in the situation something has a |
1088
|
|
|
|
|
|
|
L method and wants to know the advice declarator it is made for. |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
Returns C<"before"> for L advice, C<"after"> for |
1091
|
|
|
|
|
|
|
L advice, or C<"around"> for |
1092
|
|
|
|
|
|
|
L advice. |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
=head1 LIBRARY |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
The main L distribution ships with the following set of libraries. These |
1097
|
|
|
|
|
|
|
are not necesarily recommended or the best on offer. The are shipped with |
1098
|
|
|
|
|
|
|
B for convenience, because they have no additional CPAN dependencies. |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
Their purpose is summarised below, but see their own documentation for more |
1101
|
|
|
|
|
|
|
information. |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
=head2 Aspect::Library::Singleton |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
L can be used to convert an existing class to |
1106
|
|
|
|
|
|
|
function as a singleton and return the same object for every constructor call. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=head2 Aspect::Library::Breakpoint |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
L allows you to inject debugging breakpoints into |
1111
|
|
|
|
|
|
|
a program using the full power and complexity of the C pointcuts. |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
=head2 Aspect::Library::Wormhole |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
L is a tool for passing objects down a call flow, |
1116
|
|
|
|
|
|
|
without adding extra arguments to the frames between the source and the target, |
1117
|
|
|
|
|
|
|
letting a function implicit context. |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=head2 Aspect::Library::Listenable |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
L assysts in the implementation of the "Listenable" |
1122
|
|
|
|
|
|
|
design pattern. It lets you define a function as emitting events that can be |
1123
|
|
|
|
|
|
|
registed for by subscribers, and then add/remove subscribers for these events |
1124
|
|
|
|
|
|
|
over time. |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
When the functions that are listenable are called, registered subscribers will |
1127
|
|
|
|
|
|
|
be notified. This lets you build a general event subscription system for your |
1128
|
|
|
|
|
|
|
program. This could be as part of a plugin API or just for your own convenience. |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head1 INTERNALS |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Due to the dynamic nature of Perl, there is no need for processing of source |
1133
|
|
|
|
|
|
|
or byte code, as required in the Java and .NET worlds. |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
The implementation is conceptually very simple: when you create advice, its |
1136
|
|
|
|
|
|
|
pointcut is matched to find every sub defined in the symbol table that might |
1137
|
|
|
|
|
|
|
match against the pointcut (potentially subject to further runtime conditions). |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
Those that match, will get a special wrapper installed. The wrapper only |
1140
|
|
|
|
|
|
|
executes if, during run-time, a compiled context test for the pointcut |
1141
|
|
|
|
|
|
|
returns true. |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
The wrapper code creates an advice context, and gives it to the advice code. |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
Most of the complexity comes from the extensive optimisation that is used to |
1146
|
|
|
|
|
|
|
reduce the impact of both weaving of the advice and the run-time costs of the |
1147
|
|
|
|
|
|
|
wrappers added to your code. |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
Some pointcuts like C are static and their full effect is known at |
1150
|
|
|
|
|
|
|
weave time, so the compiled run-time function can be optimised away entirely. |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
Some pointcuts like C are dynamic, so they are not used to select |
1153
|
|
|
|
|
|
|
the functions to hook, but impose a run-time cost to determine whether or not |
1154
|
|
|
|
|
|
|
they match. |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
To make this process faster, when the advice is installed, the pointcut |
1157
|
|
|
|
|
|
|
will not use itself directly for the compiled run-time function but will |
1158
|
|
|
|
|
|
|
additionally generate a "curried" (optimised) version of itself. |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
This curried version uses the fact that the run-time check will only be |
1161
|
|
|
|
|
|
|
called if it matches the C pointcut pattern, and so no C |
1162
|
|
|
|
|
|
|
pointcuts needed to be tested at run-time unless they are in deep and |
1163
|
|
|
|
|
|
|
complex nested coolean logic. It also handles collapsing any boolean logic |
1164
|
|
|
|
|
|
|
impacted by the safe removal of the C pointcuts. |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
Further, where possible the pointcuts will be expressed as Perl source |
1167
|
|
|
|
|
|
|
(including logic operators) and compiled into a single Perl expression. This |
1168
|
|
|
|
|
|
|
not only massively reduces the number of functions to be called, but allows |
1169
|
|
|
|
|
|
|
further optimisation of the pointcut by the opcode optimiser in perl itself. |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
If you use only C pointcuts (alone or in boolean combinations) |
1172
|
|
|
|
|
|
|
the currying results in a null test (the pointcut is optimised away |
1173
|
|
|
|
|
|
|
entirely) and so the need to make a run-time point test will be removed |
1174
|
|
|
|
|
|
|
altogether from the generated advice hooks, reducing call overheads |
1175
|
|
|
|
|
|
|
significantly. |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
If your pointcut does not have any static conditions (i.e. C) then |
1178
|
|
|
|
|
|
|
the wrapper code will need to be installed into every function on the symbol |
1179
|
|
|
|
|
|
|
table. This is highly discouraged and liable to result in hooks on unusual |
1180
|
|
|
|
|
|
|
functions and unwanted side effects, potentially breaking your program. |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
=head1 LIMITATIONS |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
=head2 Inheritance Support |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
Support for inheritance is lacking. Consider the following two classes: |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
package Automobile; |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
sub compute_mileage { |
1191
|
|
|
|
|
|
|
# ... |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
package Van; |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
use base 'Automobile'; |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
And the following two advice: |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
before { |
1201
|
|
|
|
|
|
|
print "Automobile!\n"; |
1202
|
|
|
|
|
|
|
} call 'Automobile::compute_mileage'; |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
before { |
1205
|
|
|
|
|
|
|
print "Van!\n"; |
1206
|
|
|
|
|
|
|
} call 'Van::compute_mileage'; |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
Some join points one would expect to be matched by the call pointcuts |
1209
|
|
|
|
|
|
|
above, do not: |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
$automobile = Automobile->new; |
1212
|
|
|
|
|
|
|
$van = Van->new; |
1213
|
|
|
|
|
|
|
$automobile->compute_mileage; # Automobile! |
1214
|
|
|
|
|
|
|
$van->compute_mileage; # Automobile!, should also print Van! |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
C will never be printed. This happens because B installs |
1217
|
|
|
|
|
|
|
advice code on symbol table entries. C does not |
1218
|
|
|
|
|
|
|
have one, so nothing happens. Until this is solved, you have to do the |
1219
|
|
|
|
|
|
|
thinking about inheritance yourself. |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
=head2 Performance |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
You may find it very easy to shoot yourself in the foot with this module. |
1224
|
|
|
|
|
|
|
Consider this advice: |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
# Do not do this! |
1227
|
|
|
|
|
|
|
before { |
1228
|
|
|
|
|
|
|
print $_->sub_name; |
1229
|
|
|
|
|
|
|
} cflow 'MyApp::Company::make_report'; |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
The advice code will be installed on B sub loaded. The advice code |
1232
|
|
|
|
|
|
|
will only run when in the specified call flow, which is the correct |
1233
|
|
|
|
|
|
|
behavior, but it will be I on every sub in the system. This |
1234
|
|
|
|
|
|
|
can be extremely slow because the run-time cost of checking C will |
1235
|
|
|
|
|
|
|
occur on every single function called in your program. |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
It happens because the C pointcut matches I subs during weave-time. |
1238
|
|
|
|
|
|
|
It matches the correct sub during run-time. The solution is to narrow the |
1239
|
|
|
|
|
|
|
pointcut: |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
# Much better |
1242
|
|
|
|
|
|
|
before { |
1243
|
|
|
|
|
|
|
print $_->sub_name; |
1244
|
|
|
|
|
|
|
} call qr/^MyApp::/ |
1245
|
|
|
|
|
|
|
& cflow 'MyApp::Company::make_report'; |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
=head1 TO DO |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
There are a many things that could be added, if people have an interest |
1250
|
|
|
|
|
|
|
in contributing to the project. |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=head2 Documentation |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
* cookbook |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
* tutorial |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
* example of refactoring a useful CPAN module using aspects |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=head2 Pointcuts |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
* New pointcuts: execution, cflowbelow, within, advice, calledby. Sure |
1263
|
|
|
|
|
|
|
you can implement them today with Perl treachery, but it is too much |
1264
|
|
|
|
|
|
|
work. |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
* We need a way to match subs with an attribute, attributes::get() |
1267
|
|
|
|
|
|
|
will currently not work. |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
* isa() support for method pointcuts as Gaal Yahas suggested: match |
1270
|
|
|
|
|
|
|
methods on class hierarchies without callbacks |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
* Perl join points: phasic- BEGIN/INIT/CHECK/END |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=head2 Weaving |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
* The current optimation has gone as far as it can, next we need to look into |
1277
|
|
|
|
|
|
|
XS acceleration and byte code manipulation with B:: modules. |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
* A debug flag to print out subs that were matched during weaving |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
* Warnings when over 1000 methods wrapped |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
* Allow finer control of advice execution order |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
* Centralised hooking in wrappers so that each successive advice won't need |
1286
|
|
|
|
|
|
|
to wrap around the previous one. |
1287
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
* Allow lexical aspects to be safely removed completely, rather than being left |
1289
|
|
|
|
|
|
|
in place and disabled as in the current implementation. |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
=head1 SUPPORT |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at |
1294
|
|
|
|
|
|
|
L. |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=head1 INSTALLATION |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
See L for information and options on installing Perl modules. |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=head1 AVAILABILITY |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
The latest version of this module is available from the Comprehensive Perl |
1303
|
|
|
|
|
|
|
Archive Network (CPAN). Visit to find a CPAN |
1304
|
|
|
|
|
|
|
site near you. Or see L. |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
=head1 AUTHORS |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
Adam Kennedy Eadamk@cpan.orgE |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
Marcel GrEnauer Emarcel@cpan.orgE |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
Ran Eilam Eeilara@cpan.orgE |
1313
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
=head1 SEE ALSO |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
You can find AOP examples in the C directory of the |
1317
|
|
|
|
|
|
|
distribution. |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
L |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
L |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
L |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
Copyright 2001 by Marcel GrEnauer |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
Some parts copyright 2009 - 2011 Adam Kennedy. |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
1332
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
=cut |