line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Aspect::AdviceContext; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
51
|
|
4
|
1
|
|
|
1
|
|
25
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
45
|
|
5
|
1
|
|
|
1
|
|
8
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1187
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
sub new { |
8
|
17
|
|
|
17
|
0
|
8152
|
my ($class, %spec) = @_; |
9
|
17
|
50
|
|
|
|
47
|
croak "cannot create with no sub_name" unless $spec{sub_name}; |
10
|
95
|
|
|
|
|
173
|
my $self = bless { |
11
|
17
|
|
|
|
|
48
|
(map { $_ => $spec{$_} } keys %spec), |
12
|
|
|
|
|
|
|
proceed => 1, |
13
|
|
|
|
|
|
|
}, $class; |
14
|
17
|
|
|
|
|
65
|
return $self; |
15
|
|
|
|
|
|
|
} |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub run_original { |
18
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
19
|
1
|
|
|
|
|
13
|
my $original = $self->original; |
20
|
1
|
|
|
|
|
5
|
my @params = $self->params; |
21
|
1
|
|
|
|
|
1
|
my $return_value; |
22
|
1
|
50
|
|
|
|
4
|
if (wantarray) |
23
|
0
|
|
|
|
|
0
|
{ $return_value = [$original->(@params)] } |
24
|
|
|
|
|
|
|
else |
25
|
1
|
|
|
|
|
4
|
{ $return_value = $original->(@params) } |
26
|
1
|
|
|
|
|
7
|
$self->return_value($return_value); |
27
|
1
|
|
|
|
|
2
|
return $self->return_value; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub proceed { |
31
|
9
|
|
|
9
|
0
|
16
|
my ($self, $value) = @_; |
32
|
9
|
50
|
|
|
|
50
|
return $self->get_value('proceed') if @_ == 1; |
33
|
0
|
|
|
|
|
0
|
$self->{proceed} = $value; |
34
|
0
|
|
|
|
|
0
|
return $self; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub append_param { |
38
|
3
|
|
|
3
|
0
|
11
|
my ($self, @param) = @_; |
39
|
3
|
|
|
|
|
4
|
push @{$self->params}, @param; |
|
3
|
|
|
|
|
7
|
|
40
|
3
|
|
|
|
|
7
|
return $self; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
1
|
|
|
1
|
0
|
5
|
sub append_params { shift->append_param(@_) } |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub params { |
46
|
18
|
|
|
18
|
0
|
54
|
my ($self, @value) = @_; |
47
|
18
|
100
|
|
|
|
73
|
return $self->get_value('params') if @_ == 1; |
48
|
1
|
|
|
|
|
3
|
$self->{params} = \@value; |
49
|
1
|
|
|
|
|
4
|
return $self; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
7
|
|
|
7
|
0
|
41
|
sub self { shift->{params}->[0] } |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub package_name { |
55
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
56
|
1
|
|
|
|
|
7
|
my $name = $self->sub_name; |
57
|
1
|
50
|
|
|
|
6
|
return '' unless $name =~ /::/; |
58
|
1
|
|
|
|
|
5
|
$name =~ s/::[^:]+$//; |
59
|
1
|
|
|
|
|
6
|
return $name; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub short_sub_name { |
63
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
64
|
1
|
|
|
|
|
7
|
my $name = $self->sub_name; |
65
|
1
|
50
|
|
|
|
8
|
return $name unless $name =~ /::/; |
66
|
1
|
|
|
|
|
6
|
$name =~ /::([^:]+)$/; |
67
|
1
|
|
|
|
|
6
|
return $1; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub return_value { |
71
|
23
|
|
|
23
|
0
|
464
|
my ($self, $value) = @_; |
72
|
23
|
100
|
|
|
|
56
|
if (@_ == 1) { |
73
|
13
|
|
|
|
|
27
|
my $return_value = $self->get_value('return_value'); |
74
|
13
|
50
|
33
|
|
|
120
|
return wantarray && ref $return_value eq 'ARRAY'? |
75
|
|
|
|
|
|
|
@$return_value: $return_value; |
76
|
|
|
|
|
|
|
} |
77
|
10
|
|
|
|
|
36
|
$self->{return_value} = $value; |
78
|
10
|
|
|
|
|
20
|
$self->{proceed} = 0; |
79
|
10
|
|
|
|
|
22
|
return $self; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub AUTOLOAD { |
83
|
10
|
|
|
10
|
|
1987
|
my $self = shift; |
84
|
10
|
|
|
|
|
16
|
my $key = our $AUTOLOAD; |
85
|
10
|
50
|
|
|
|
34
|
return if $key =~ /DESTROY$/; |
86
|
10
|
|
|
|
|
77
|
$key =~ s/^.*:://; |
87
|
10
|
|
|
|
|
31
|
return $self->get_value($key); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub get_value { |
91
|
49
|
|
|
49
|
0
|
60
|
my ($self, $key) = @_; |
92
|
49
|
50
|
|
|
|
144
|
croak "Key does not exist: [$key]" unless exists $self->{$key}; |
93
|
49
|
|
|
|
|
67
|
my $value = $self->{$key}; |
94
|
49
|
100
|
66
|
|
|
277
|
return wantarray && ref $value eq 'ARRAY'? @$value: $value; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
1; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 NAME |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Aspect::AdviceContext - a pointcut context for advice code |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 SYNOPSIS |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
$pointcut = call qr/^Person::[gs]et_/ & cflow company => qr/^Company::/; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# using in 'before' advice code |
108
|
|
|
|
|
|
|
before { |
109
|
|
|
|
|
|
|
my $context = shift; # context is only param to advice code |
110
|
|
|
|
|
|
|
print $context->type; # 'before': advice type: before/after |
111
|
|
|
|
|
|
|
print $context->pointcut; # $pointcut: the pointcut for this advice |
112
|
|
|
|
|
|
|
print $context->sub_name; # package + sub name of matched sub |
113
|
|
|
|
|
|
|
print $context->package_name; # 'Person': package name of matched sub |
114
|
|
|
|
|
|
|
print $context->short_sub_name; # sub name of matched sub |
115
|
|
|
|
|
|
|
print $context->self; # 1st parameter to matched sub |
116
|
|
|
|
|
|
|
print $context->params->[1]; # 2nd parameter to matched sub |
117
|
|
|
|
|
|
|
$context->append_param($rdbms); # append param to matched sub |
118
|
|
|
|
|
|
|
$context->append_params($a, $b); # append params to matched sub |
119
|
|
|
|
|
|
|
$context->return_value(4) # don't proceed to matched sub, return 4 |
120
|
|
|
|
|
|
|
$context->original->(x => 3); # call matched sub, don't proceed |
121
|
|
|
|
|
|
|
$context->proceed(1); # do proceed to matched sub after all |
122
|
|
|
|
|
|
|
print $context->company->name; # access cflow pointcut advice context |
123
|
|
|
|
|
|
|
} $pointcut; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head1 DESCRIPTION |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Advice code is called when the advice pointcut is matched. In this code, |
128
|
|
|
|
|
|
|
there is always a need to access information about the context of the |
129
|
|
|
|
|
|
|
advice. Information like: what is the actual sub name matched? What are |
130
|
|
|
|
|
|
|
the parameters in this call that we matched? Sometimes you want to change |
131
|
|
|
|
|
|
|
the context for the matched sub: append a parameter, or even stop the |
132
|
|
|
|
|
|
|
matched sub from being called. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
You do all these things through the C. It is the only |
135
|
|
|
|
|
|
|
parameter provided to the advice code. It provides all the information |
136
|
|
|
|
|
|
|
required about the match context, and allows you to change the behavior |
137
|
|
|
|
|
|
|
of the matched sub. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Note that modifying parameters through the context, in the code of an |
140
|
|
|
|
|
|
|
I advice, will have no effect, since the matched sub has already |
141
|
|
|
|
|
|
|
been called. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head1 CFLOW CONTEXT |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
If the pointcut of an advice is composed of at least one |
146
|
|
|
|
|
|
|
L, advice code may require not only the context |
147
|
|
|
|
|
|
|
of the advice, but also the context of the cflows. This is required if |
148
|
|
|
|
|
|
|
you want to find out, for example, what is the name of the sub that |
149
|
|
|
|
|
|
|
matched a cflow. E.g. for the synopsis example above, what method of |
150
|
|
|
|
|
|
|
C started the chain of calls that eventually reached the get/set |
151
|
|
|
|
|
|
|
on C? |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
You can access cflow context in the synopsis above, by calling: |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
$context->company; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
You get it from the main advice context, by calling a method named after |
158
|
|
|
|
|
|
|
the context key used in the cflow spec. In the synopsis pointcut |
159
|
|
|
|
|
|
|
definition, the cflow part was: |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
cflow company => qr/^Company::/ |
162
|
|
|
|
|
|
|
^^^^^^^ |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
An C will be created for the cflow, and you can access it |
165
|
|
|
|
|
|
|
using the key C. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 EXAMPLES |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Print parameters to matched sub: |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
before { my $c = shift; print join(',', $c->params) } $pointcut; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Append a parameter: |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
before { shift->append_param('extra-param') } $pointcut; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Don't proceed to matched sub, return 4 instead: |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
before { shift->return_value(4) } $pointcut; |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Call matched sub again, and again, until it returns something defined: |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
after { |
184
|
|
|
|
|
|
|
my $context = shift; |
185
|
|
|
|
|
|
|
my $return = $context->return_value; |
186
|
|
|
|
|
|
|
while (!defined $return) |
187
|
|
|
|
|
|
|
{ $return = $context->original($context->params) } |
188
|
|
|
|
|
|
|
$context->return_value($return); |
189
|
|
|
|
|
|
|
} $pointcut; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Print the name of the C object that started the chain of calls |
192
|
|
|
|
|
|
|
that eventually reached the get/set on C: |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
before { print shift->company->name } $pointcut; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head1 SEE ALSO |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
See the L pod for a guide to the Aspect module. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
You can find examples of using the C in any advice code. |
201
|
|
|
|
|
|
|
The aspect library for example (e.g. L). |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
L creates the main C, and |
204
|
|
|
|
|
|
|
C creates contexts for each matched call flow. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|