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