line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $Id: Callback.pm 184 2009-06-11 06:44:27Z rcaputo $ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
POE::Callback - object wrapper for callbacks with lexical closures |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# TODO - Make this a complete working example. |
10
|
|
|
|
|
|
|
my $callback = POE::Callback->new( |
11
|
|
|
|
|
|
|
name => "Pkg::sub", |
12
|
|
|
|
|
|
|
code => \&coderef, |
13
|
|
|
|
|
|
|
); |
14
|
|
|
|
|
|
|
$callback->(@arguments); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 DESCRIPTION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
POE::Callback wraps coderefs in magic that makes certain lexical |
19
|
|
|
|
|
|
|
variables persistent between calls. |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
It's used internally by the classes that comprise POE::Stage. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=cut |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
package POE::Callback; |
26
|
|
|
|
|
|
|
|
27
|
1
|
|
|
1
|
|
30888
|
use warnings; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
37
|
|
28
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
79
|
|
29
|
|
|
|
|
|
|
|
30
|
1
|
|
|
1
|
|
774
|
use PadWalker qw(var_name peek_my peek_sub); |
|
1
|
|
|
|
|
2452
|
|
|
1
|
|
|
|
|
91
|
|
31
|
1
|
|
|
1
|
|
10
|
use Scalar::Util qw(blessed reftype weaken); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
109
|
|
32
|
1
|
|
|
1
|
|
1775
|
use Devel::LexAlias qw(lexalias); |
|
1
|
|
|
|
|
42543
|
|
|
1
|
|
|
|
|
145
|
|
33
|
1
|
|
|
1
|
|
13
|
use Carp qw(croak); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
123
|
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Track our wrappers to avoid wrapping them. Otherwise hilarity may |
36
|
|
|
|
|
|
|
# ensue. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my %callbacks; |
39
|
1
|
|
|
1
|
|
7
|
use constant CB_SELF => 0; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
129
|
|
40
|
1
|
|
|
1
|
|
6
|
use constant CB_NAME => 1; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1686
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head2 new CODEREF |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Creates a new callback from a raw CODEREF. Returns the callback, |
45
|
|
|
|
|
|
|
which is just the CODEREF blessed into POE::Callback. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=cut |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub new { |
50
|
4
|
|
|
4
|
1
|
498
|
my ($class, $arg) = @_; |
51
|
|
|
|
|
|
|
|
52
|
4
|
|
|
|
|
10
|
foreach my $required (qw(name code)) { |
53
|
8
|
50
|
|
|
|
24
|
croak "POE::Callback requires a '$required'" unless $arg->{$required}; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
4
|
|
|
|
|
8
|
my $code = $arg->{code}; |
57
|
4
|
|
|
|
|
6
|
my $name = $arg->{name}; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Don't wrap callbacks. |
60
|
4
|
50
|
|
|
|
15
|
return $code if exists $callbacks{$code}; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Gather the names of persistent variables. |
63
|
4
|
|
|
|
|
26
|
my $pad = peek_sub($code); |
64
|
15
|
100
|
|
|
|
96
|
my @persistent = grep { |
65
|
4
|
|
|
|
|
12
|
/^\$(self|req|rsp)$/ || /^([\$\@\%])(req|rsp|arg|self)_(\S+)/ |
66
|
|
|
|
|
|
|
} keys %$pad; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# No point in the wrapper if there are no persistent variables. |
69
|
|
|
|
|
|
|
|
70
|
4
|
50
|
|
|
|
13
|
unless (@persistent) { |
71
|
0
|
|
|
|
|
0
|
my $self = bless $code, $class; |
72
|
0
|
|
|
|
|
0
|
return $self->_track($name); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
4
|
|
|
|
|
6
|
my $b_self = ''; # build $self |
76
|
4
|
|
|
|
|
5
|
my $b_rsp = ''; # build $rsp |
77
|
4
|
|
|
|
|
5
|
my $b_req = ''; # build $req |
78
|
4
|
|
|
|
|
8
|
my $b_arg = ''; # build $arg |
79
|
4
|
|
|
|
|
3
|
my $b_req_id = ''; # build $req->get_id() |
80
|
4
|
|
|
|
|
5
|
my $b_rsp_id = ''; # build $rsp->get_id() |
81
|
|
|
|
|
|
|
|
82
|
4
|
|
|
|
|
4
|
my $a_self = ''; |
83
|
4
|
|
|
|
|
6
|
my $a_rsp = ''; |
84
|
4
|
|
|
|
|
3
|
my $a_req = ''; |
85
|
|
|
|
|
|
|
|
86
|
4
|
|
|
|
|
4
|
my @vars; |
87
|
|
|
|
|
|
|
|
88
|
4
|
|
|
|
|
7
|
foreach my $var_name (@persistent) { |
89
|
30
|
100
|
|
|
|
52
|
if ($var_name eq '_b_self') { |
90
|
1
|
|
|
|
|
1
|
$b_self = q{ my $self = POE::Stage::self();}; |
91
|
1
|
|
|
|
|
2
|
next; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
29
|
100
|
|
|
|
42
|
if ($var_name eq '_b_req') { |
95
|
2
|
100
|
|
|
|
5
|
push @persistent, '$self' unless $b_self; |
96
|
2
|
|
|
|
|
3
|
$b_req = q{ my $req = $self->_get_request();}; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
29
|
100
|
|
|
|
47
|
if ($var_name eq '_b_rsp') { |
100
|
2
|
50
|
|
|
|
4
|
push @persistent, '$self' unless $b_self; |
101
|
2
|
|
|
|
|
2
|
$b_rsp = q{ my $rsp = $self->_get_response(); }; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
29
|
100
|
|
|
|
44
|
if ($var_name eq '$self') { |
105
|
5
|
100
|
|
|
|
11
|
push @persistent, '_b_self' unless $b_self; |
106
|
5
|
|
|
|
|
4
|
$a_self = q{ lexalias($code, '$self', \$self);}; |
107
|
5
|
|
|
|
|
6
|
next; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
24
|
100
|
|
|
|
33
|
if ($var_name eq '_b_rsp_id') { |
111
|
3
|
100
|
|
|
|
7
|
push @persistent, '_b_rsp' unless $b_rsp; |
112
|
3
|
|
|
|
|
3
|
$b_rsp_id = q{ my $rsp_id = $rsp->get_id();}; |
113
|
3
|
|
|
|
|
3
|
next; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
21
|
100
|
|
|
|
35
|
if ($var_name eq '_b_req_id') { |
117
|
3
|
100
|
|
|
|
9
|
push @persistent, '_b_req' unless $b_req; |
118
|
3
|
|
|
|
|
4
|
$b_req_id = q{ my $req_id = $req->get_id();}; |
119
|
3
|
|
|
|
|
3
|
next; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
18
|
100
|
|
|
|
28
|
if ($var_name eq '$req') { |
123
|
1
|
50
|
|
|
|
5
|
push @persistent, '_b_req' unless $b_req; |
124
|
1
|
|
|
|
|
2
|
$a_req = q{ lexalias($code, '$req', \$req);}; |
125
|
1
|
|
|
|
|
2
|
next; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
17
|
100
|
|
|
|
27
|
if ($var_name eq '$rsp') { |
129
|
1
|
50
|
|
|
|
4
|
push @persistent, '_b_rsp' unless $b_rsp; |
130
|
1
|
|
|
|
|
1
|
$a_rsp = q{lexalias($code, '$rsp', \$rsp);}; |
131
|
1
|
|
|
|
|
2
|
next; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
16
|
100
|
|
|
|
75
|
next unless $var_name =~ /^([\$\@\%])(req|rsp|arg|self)_(\S+)/; |
135
|
|
|
|
|
|
|
|
136
|
12
|
|
|
|
|
34
|
my ($sigil, $prefix, $base_member_name) = ($1, $2, $3); |
137
|
12
|
|
|
|
|
37
|
my $member_name = $sigil . $base_member_name; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Arguments don't need vivification, so they come before @vivify. |
140
|
|
|
|
|
|
|
|
141
|
12
|
100
|
|
|
|
28
|
if ($prefix eq 'arg') { |
142
|
6
|
|
100
|
|
|
18
|
$b_arg ||= ( |
143
|
|
|
|
|
|
|
q/ my $arg; { package DB; my @x = caller(0); $arg = $DB::args[1]; }/ |
144
|
|
|
|
|
|
|
); |
145
|
|
|
|
|
|
|
|
146
|
6
|
|
|
|
|
12
|
my $def = ( |
147
|
|
|
|
|
|
|
qq/ \$var_reference = \$pad->{'$var_name'};/ |
148
|
|
|
|
|
|
|
); |
149
|
|
|
|
|
|
|
|
150
|
6
|
100
|
|
|
|
12
|
if ($sigil eq '$') { |
151
|
2
|
|
|
|
|
5
|
push @vars, ( |
152
|
|
|
|
|
|
|
$def, |
153
|
|
|
|
|
|
|
qq/ \$\$var_reference = \$arg->{'$base_member_name'};/ |
154
|
|
|
|
|
|
|
); |
155
|
2
|
|
|
|
|
586
|
next; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
4
|
100
|
|
|
|
9
|
if ($sigil eq '@') { |
159
|
2
|
|
|
|
|
4
|
push @vars, ( |
160
|
|
|
|
|
|
|
$def, |
161
|
|
|
|
|
|
|
qq/ \@\$var_reference = \@{\$arg->{'$base_member_name'}};/ |
162
|
|
|
|
|
|
|
); |
163
|
2
|
|
|
|
|
5
|
next; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
2
|
50
|
|
|
|
6
|
if ($sigil eq '%') { |
167
|
2
|
|
|
|
|
5
|
push @vars, ( |
168
|
|
|
|
|
|
|
$def, |
169
|
|
|
|
|
|
|
qq/ \%\$var_reference = \%{\$arg->{'$base_member_name'}};/ |
170
|
|
|
|
|
|
|
); |
171
|
2
|
|
|
|
|
4
|
next; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Common vivification code. |
176
|
|
|
|
|
|
|
|
177
|
6
|
|
|
|
|
12
|
my @vivify = ( q/ unless( defined $member_ref ) {/ ); |
178
|
6
|
100
|
|
|
|
16
|
if ($sigil eq '$') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
179
|
2
|
|
|
|
|
4
|
push @vivify, q( my $new_scalar; $member_ref = \$new_scalar;); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
elsif ($sigil eq '@') { |
182
|
2
|
|
|
|
|
4
|
push @vivify, q( $member_ref = [];); |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
elsif ($sigil eq '%') { |
185
|
2
|
|
|
|
|
3
|
push @vivify, q( $member_ref = {};); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Determine which object to use based on the prefix. |
189
|
|
|
|
|
|
|
|
190
|
6
|
|
|
|
|
9
|
my $obj; |
191
|
6
|
100
|
|
|
|
12
|
if ($prefix eq 'req') { |
192
|
3
|
50
|
|
|
|
8
|
push @persistent, '_b_req_id' unless $b_req; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Get the existing member reference. |
195
|
3
|
|
|
|
|
8
|
push @vars, ( |
196
|
|
|
|
|
|
|
q{ $member_ref = } . |
197
|
|
|
|
|
|
|
q{$self->_request_context_fetch(} . |
198
|
|
|
|
|
|
|
qq{\$req_id, '$member_name');} |
199
|
|
|
|
|
|
|
); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Autovivify if necessary. |
202
|
3
|
|
|
|
|
13
|
push @vars, ( |
203
|
|
|
|
|
|
|
@vivify, |
204
|
|
|
|
|
|
|
q{ $self->_request_context_store(} . |
205
|
|
|
|
|
|
|
qq{\$req_id, '$member_name', \$member_ref);}, |
206
|
|
|
|
|
|
|
q( }), |
207
|
|
|
|
|
|
|
# Alias the member. |
208
|
|
|
|
|
|
|
qq{ lexalias(\$code, '$var_name', \$member_ref);} |
209
|
|
|
|
|
|
|
); |
210
|
3
|
|
|
|
|
5
|
next; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
3
|
50
|
|
|
|
7
|
if ($prefix eq 'rsp') { |
214
|
3
|
50
|
|
|
|
8
|
push @persistent, '_b_rsp_id' unless $b_rsp; |
215
|
3
|
50
|
|
|
|
7
|
push @persistent, '$self' unless $b_self; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Get the existing member reference. |
218
|
3
|
|
|
|
|
7
|
push @vars, ( |
219
|
|
|
|
|
|
|
q{ $member_ref = } . |
220
|
|
|
|
|
|
|
q{$self->_request_context_fetch(}. |
221
|
|
|
|
|
|
|
qq{\$rsp_id, '$member_name');} |
222
|
|
|
|
|
|
|
); |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# Autovivify if necessary. |
225
|
3
|
|
|
|
|
12
|
push @vars, ( |
226
|
|
|
|
|
|
|
@vivify, |
227
|
|
|
|
|
|
|
q{ $self->_request_context_store(} . |
228
|
|
|
|
|
|
|
qq{ \$rsp_id, '$member_name', \$member_ref);}, |
229
|
|
|
|
|
|
|
qq( \}), |
230
|
|
|
|
|
|
|
# Alias the member. |
231
|
|
|
|
|
|
|
qq{ lexalias(\$code, '$var_name', \$member_ref);} |
232
|
|
|
|
|
|
|
); |
233
|
3
|
|
|
|
|
6
|
next; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
0
|
0
|
|
|
|
0
|
if ($prefix eq 'self') { |
237
|
0
|
0
|
|
|
|
0
|
push @persistent, '$self' unless $b_self; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Get the existing member reference. |
240
|
0
|
|
|
|
|
0
|
push @vars, ( |
241
|
|
|
|
|
|
|
qq{\$member_ref = \$self->_self_fetch('$member_name');} |
242
|
|
|
|
|
|
|
); |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# Autovivify if necessary. |
245
|
0
|
|
|
|
|
0
|
push @vars, ( |
246
|
|
|
|
|
|
|
@vivify, |
247
|
|
|
|
|
|
|
qq{ \$self->_self_store('$member_name', \$member_ref);}, |
248
|
|
|
|
|
|
|
qq( \}), |
249
|
|
|
|
|
|
|
# Alias the member. |
250
|
|
|
|
|
|
|
qq{ lexalias(\$code, '$var_name', \$member_ref);} |
251
|
|
|
|
|
|
|
); |
252
|
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
0
|
next; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
4
|
|
|
|
|
35
|
unshift @vars, ( |
258
|
|
|
|
|
|
|
$b_self, $b_arg, $b_req, $b_rsp, $b_req_id, $b_rsp_id, |
259
|
|
|
|
|
|
|
$a_self, $a_rsp, $a_req, |
260
|
|
|
|
|
|
|
); |
261
|
|
|
|
|
|
|
|
262
|
4
|
|
|
|
|
28
|
my $sub = join "\n", ( |
263
|
|
|
|
|
|
|
"sub {", |
264
|
|
|
|
|
|
|
" my \$pad = peek_sub(\$code);", |
265
|
|
|
|
|
|
|
" my (\$member_ref, \$var_reference);", |
266
|
|
|
|
|
|
|
@vars, |
267
|
|
|
|
|
|
|
" goto \$code;", |
268
|
|
|
|
|
|
|
"};" |
269
|
|
|
|
|
|
|
); |
270
|
|
|
|
|
|
|
#warn $sub; # for debugging generated code |
271
|
4
|
|
|
|
|
1133
|
my $coderef = eval $sub; |
272
|
4
|
50
|
|
|
|
13
|
if( $@ ) { |
273
|
0
|
|
|
|
|
0
|
while( $@ =~ /line (\d+)/g ) { |
274
|
0
|
|
|
|
|
0
|
my $line = $1; |
275
|
0
|
|
|
|
|
0
|
for( ($line-10) .. $line-4 ) { |
276
|
0
|
|
|
|
|
0
|
warn $_+4, ": $vars[$_]\n"; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
0
|
|
|
|
|
0
|
die $@; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
4
|
|
|
|
|
10
|
my $self = bless $coderef, $class; |
283
|
4
|
|
|
|
|
12
|
return $self->_track($name); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Track a callback so we don't accidentally wrap it. |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub _track { |
289
|
4
|
|
|
4
|
|
6
|
my ($self, $name) = @_; |
290
|
4
|
|
|
|
|
20
|
$callbacks{$self} = [ |
291
|
|
|
|
|
|
|
$self, # CB_SELF |
292
|
|
|
|
|
|
|
$name, # CB_NAME |
293
|
|
|
|
|
|
|
]; |
294
|
4
|
|
|
|
|
17
|
weaken($callbacks{$self}[CB_SELF]); |
295
|
4
|
|
|
|
|
33
|
return $self; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# When the callback object is destroyed, it's also removed from the |
299
|
|
|
|
|
|
|
# tracking hash. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub DESTROY { |
302
|
4
|
|
|
4
|
|
922
|
my $self = shift; |
303
|
4
|
50
|
|
|
|
18
|
warn "!!! Destroying untracked callback $self" unless ( |
304
|
|
|
|
|
|
|
exists $callbacks{$self} |
305
|
|
|
|
|
|
|
); |
306
|
4
|
|
|
|
|
314
|
delete $callbacks{$self}; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# End-of-run leak checking. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
END { |
312
|
1
|
|
|
1
|
|
3
|
my @leaks; |
313
|
1
|
|
|
|
|
7
|
foreach my $callback (sort keys %callbacks) { |
314
|
1
|
|
|
1
|
|
7
|
no strict 'refs'; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
206
|
|
315
|
0
|
|
|
|
|
0
|
my $cb_name = $callbacks{$callback}[CB_NAME]; |
316
|
0
|
0
|
|
|
|
0
|
next if *{$cb_name}{CODE} == $callbacks{$callback}[CB_SELF]; |
|
0
|
|
|
|
|
0
|
|
317
|
0
|
|
|
|
|
0
|
push @leaks, "!!! $callback = $cb_name\n"; |
318
|
|
|
|
|
|
|
} |
319
|
1
|
50
|
|
|
|
10
|
if (@leaks) { |
320
|
0
|
|
|
|
|
0
|
warn "\n!!! callback leak:"; |
321
|
0
|
|
|
|
|
0
|
warn @leaks; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
1; |