| 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 |