| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package MOP::Internal::Util; |
|
2
|
|
|
|
|
|
|
# ABSTRACT: For MOP Internal Use Only |
|
3
|
|
|
|
|
|
|
|
|
4
|
35
|
|
|
35
|
|
241
|
use strict; |
|
|
35
|
|
|
|
|
81
|
|
|
|
35
|
|
|
|
|
1078
|
|
|
5
|
35
|
|
|
35
|
|
205
|
use warnings; |
|
|
35
|
|
|
|
|
76
|
|
|
|
35
|
|
|
|
|
1039
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
35
|
|
|
35
|
|
213
|
use B (); # nasty stuff, all nasty stuff |
|
|
35
|
|
|
|
|
163
|
|
|
|
35
|
|
|
|
|
594
|
|
|
8
|
35
|
|
|
35
|
|
185
|
use Carp (); # errors and stuff |
|
|
35
|
|
|
|
|
69
|
|
|
|
35
|
|
|
|
|
599
|
|
|
9
|
35
|
|
|
35
|
|
10817
|
use Sub::Name (); # handling some sub stuff |
|
|
35
|
|
|
|
|
17358
|
|
|
|
35
|
|
|
|
|
1134
|
|
|
10
|
35
|
|
|
35
|
|
10586
|
use Sub::Metadata (); # handling other sub stuff |
|
|
35
|
|
|
|
|
38480
|
|
|
|
35
|
|
|
|
|
897
|
|
|
11
|
35
|
|
|
35
|
|
10381
|
use Symbol (); # creating the occasional symbol |
|
|
35
|
|
|
|
|
26977
|
|
|
|
35
|
|
|
|
|
871
|
|
|
12
|
35
|
|
|
35
|
|
254
|
use Scalar::Util (); # I think I use blessed somewhere in here ... |
|
|
35
|
|
|
|
|
363
|
|
|
|
35
|
|
|
|
|
638
|
|
|
13
|
35
|
|
|
35
|
|
11196
|
use Devel::OverloadInfo (); # Sometimes I need to know about overloading |
|
|
35
|
|
|
|
|
433559
|
|
|
|
35
|
|
|
|
|
1063
|
|
|
14
|
35
|
|
|
35
|
|
10981
|
use Devel::Hook (); # for scheduling UNITCHECK blocks ... |
|
|
35
|
|
|
|
|
33751
|
|
|
|
35
|
|
|
|
|
19849
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '0.12'; |
|
17
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:STEVAN'; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
20
|
|
|
|
|
|
|
## Basic Glob access |
|
21
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub IS_VALID_MODULE_NAME { |
|
24
|
112
|
|
|
112
|
0
|
242
|
my ($name) = @_; |
|
25
|
112
|
|
|
|
|
828
|
$name =~ /[A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*/ |
|
26
|
|
|
|
|
|
|
} |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub IS_STASH_REF { |
|
29
|
4
|
|
|
4
|
0
|
9
|
my ($stash) = @_; |
|
30
|
4
|
50
|
|
|
|
11
|
Carp::confess('[ARGS] You must specify a stash') |
|
31
|
|
|
|
|
|
|
unless defined $stash; |
|
32
|
4
|
100
|
|
|
|
33
|
if ( my $name = B::svref_2object( $stash )->NAME ) { |
|
33
|
2
|
|
|
|
|
6
|
return IS_VALID_MODULE_NAME( $name ); |
|
34
|
|
|
|
|
|
|
} |
|
35
|
2
|
|
|
|
|
8
|
return; |
|
36
|
|
|
|
|
|
|
} |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub GET_NAME { |
|
39
|
1117
|
|
|
1117
|
0
|
1967
|
my ($stash) = @_; |
|
40
|
1117
|
50
|
|
|
|
2495
|
Carp::confess('[ARGS] You must specify a stash') |
|
41
|
|
|
|
|
|
|
unless defined $stash; |
|
42
|
1117
|
|
|
|
|
5732
|
B::svref_2object( $stash )->NAME |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub GET_STASH_NAME { |
|
46
|
81
|
|
|
81
|
0
|
146
|
my ($stash) = @_; |
|
47
|
81
|
50
|
|
|
|
189
|
Carp::confess('[ARGS] You must specify a stash') |
|
48
|
|
|
|
|
|
|
unless defined $stash; |
|
49
|
81
|
|
|
|
|
1249
|
B::svref_2object( $stash )->STASH->NAME |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub GET_GLOB_NAME { |
|
53
|
546
|
|
|
546
|
0
|
895
|
my ($stash) = @_; |
|
54
|
546
|
50
|
|
|
|
1083
|
Carp::confess('[ARGS] You must specify a stash') |
|
55
|
|
|
|
|
|
|
unless defined $stash; |
|
56
|
546
|
|
|
|
|
2678
|
B::svref_2object( $stash )->GV->NAME |
|
57
|
|
|
|
|
|
|
} |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub GET_GLOB_STASH_NAME { |
|
60
|
605
|
|
|
605
|
0
|
1011
|
my ($stash) = @_; |
|
61
|
605
|
50
|
|
|
|
1233
|
Carp::confess('[ARGS] You must specify a stash') |
|
62
|
|
|
|
|
|
|
unless defined $stash; |
|
63
|
605
|
|
|
|
|
4346
|
B::svref_2object( $stash )->GV->STASH->NAME |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub GET_GLOB_SLOT { |
|
67
|
1591
|
|
|
1591
|
0
|
3551
|
my ($stash, $name, $slot) = @_; |
|
68
|
|
|
|
|
|
|
|
|
69
|
1591
|
50
|
|
|
|
3322
|
Carp::confess('[ARGS] You must specify a stash') |
|
70
|
|
|
|
|
|
|
unless defined $stash; |
|
71
|
1591
|
50
|
|
|
|
2771
|
Carp::confess('[ARGS] You must specify a name') |
|
72
|
|
|
|
|
|
|
unless defined $name; |
|
73
|
1591
|
50
|
|
|
|
2700
|
Carp::confess('[ARGS] You must specify a slot') |
|
74
|
|
|
|
|
|
|
unless defined $slot; |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# do my best to not autovivify, and |
|
77
|
|
|
|
|
|
|
# return undef if not |
|
78
|
1591
|
100
|
|
|
|
3320
|
return unless exists $stash->{ $name }; |
|
79
|
|
|
|
|
|
|
# occasionally we need to auto-inflate |
|
80
|
|
|
|
|
|
|
# the optimized version of a required |
|
81
|
|
|
|
|
|
|
# method, its annoying, but the XS side |
|
82
|
|
|
|
|
|
|
# should not have to care about this so |
|
83
|
|
|
|
|
|
|
# it can be removed eventually. |
|
84
|
1441
|
50
|
100
|
|
|
9041
|
if (( $slot eq 'CODE' && $stash->{ $name } eq "-1" ) || ref $stash->{ $name } ne 'GLOB') { |
|
|
|
|
66
|
|
|
|
|
|
85
|
1441
|
|
|
|
|
7991
|
B::svref_2object( $stash )->NAME->can( $name ); |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# return the reference stored in the glob |
|
90
|
|
|
|
|
|
|
# which might be undef, but that can be |
|
91
|
|
|
|
|
|
|
# handled by the caller |
|
92
|
1441
|
|
|
|
|
3539
|
return *{ $stash->{ $name } }{ $slot }; |
|
|
1441
|
|
|
|
|
7492
|
|
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub SET_GLOB_SLOT { |
|
96
|
4
|
|
|
4
|
0
|
14
|
my ($stash, $name, $value_ref) = @_; |
|
97
|
|
|
|
|
|
|
|
|
98
|
4
|
50
|
|
|
|
18
|
Carp::confess('[ARGS] You must specify a stash') |
|
99
|
|
|
|
|
|
|
unless defined $stash; |
|
100
|
4
|
50
|
|
|
|
22
|
Carp::confess('[ARGS] You must specify a name') |
|
101
|
|
|
|
|
|
|
unless defined $name; |
|
102
|
4
|
50
|
|
|
|
17
|
Carp::confess('[ARGS] You must specify a value REF') |
|
103
|
|
|
|
|
|
|
unless defined $value_ref; |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
{ |
|
106
|
35
|
|
|
35
|
|
315
|
no strict 'refs'; |
|
|
35
|
|
|
|
|
108
|
|
|
|
35
|
|
|
|
|
1375
|
|
|
|
4
|
|
|
|
|
11
|
|
|
107
|
35
|
|
|
35
|
|
250
|
no warnings 'once'; |
|
|
35
|
|
|
|
|
87
|
|
|
|
35
|
|
|
|
|
22651
|
|
|
108
|
|
|
|
|
|
|
# get the name of the stash, we could have |
|
109
|
|
|
|
|
|
|
# passed this in, but it is easy to get in |
|
110
|
|
|
|
|
|
|
# XS, and so we can punt that down the road |
|
111
|
|
|
|
|
|
|
# for the time being |
|
112
|
4
|
|
|
|
|
32
|
my $pkg = B::svref_2object( $stash )->NAME; |
|
113
|
4
|
|
|
|
|
14
|
*{ $pkg . '::' . $name } = $value_ref; |
|
|
4
|
|
|
|
|
40
|
|
|
114
|
|
|
|
|
|
|
} |
|
115
|
4
|
|
|
|
|
14
|
return; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
119
|
|
|
|
|
|
|
## UNITCHECK hook |
|
120
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub ADD_UNITCHECK_HOOK { |
|
123
|
5
|
|
|
5
|
0
|
12
|
my ($cv) = @_; |
|
124
|
5
|
50
|
|
|
|
18
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
125
|
|
|
|
|
|
|
unless $cv; |
|
126
|
5
|
50
|
33
|
|
|
38
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
127
|
|
|
|
|
|
|
unless $cv && ref $cv eq 'CODE'; |
|
128
|
5
|
|
|
|
|
30
|
Devel::Hook->push_UNITCHECK_hook( $cv ); |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
132
|
|
|
|
|
|
|
## CV/Glob introspection |
|
133
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub CAN_COERCE_TO_CODE_REF { |
|
136
|
109
|
|
|
109
|
0
|
182
|
my ($object) = @_; |
|
137
|
109
|
100
|
66
|
|
|
898
|
return 0 unless $object && Scalar::Util::blessed( $object ); |
|
138
|
|
|
|
|
|
|
# might be just a blessed CODE ref ... |
|
139
|
26
|
50
|
|
|
|
105
|
return 1 if Scalar::Util::reftype( $object ) eq 'CODE'; |
|
140
|
|
|
|
|
|
|
# or might be overloaded object ... |
|
141
|
0
|
0
|
|
|
|
0
|
return 0 unless Devel::OverloadInfo::is_overloaded( $object ); |
|
142
|
0
|
|
|
|
|
0
|
return exists Devel::OverloadInfo::overload_info( $object )->{'&{}'}; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub IS_CV_NULL { |
|
146
|
926
|
|
|
926
|
0
|
1561
|
my ($cv) = @_; |
|
147
|
926
|
50
|
|
|
|
1795
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
148
|
|
|
|
|
|
|
unless $cv; |
|
149
|
926
|
50
|
33
|
|
|
3688
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
|
|
|
33
|
|
|
|
|
|
150
|
|
|
|
|
|
|
unless $cv && ref $cv eq 'CODE' |
|
151
|
|
|
|
|
|
|
|| CAN_COERCE_TO_CODE_REF( $cv ); |
|
152
|
926
|
|
|
|
|
3559
|
return Sub::Metadata::sub_body_type( $cv ) eq 'UNDEF'; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub DOES_GLOB_HAVE_NULL_CV { |
|
156
|
160
|
|
|
160
|
0
|
558
|
my ($glob) = @_; |
|
157
|
160
|
50
|
|
|
|
686
|
Carp::confess('[ARGS] You must specify a GLOB') |
|
158
|
|
|
|
|
|
|
unless $glob; |
|
159
|
|
|
|
|
|
|
# NOTE: |
|
160
|
|
|
|
|
|
|
# If the glob eq -1 that means it may well be a null sub |
|
161
|
|
|
|
|
|
|
# this seems to be some kind of artifact of an optimization |
|
162
|
|
|
|
|
|
|
# perhaps, I really don't know, it is odd. It should not |
|
163
|
|
|
|
|
|
|
# need to be dealt with in XS, it seems to be a Perl language |
|
164
|
|
|
|
|
|
|
# level thing. |
|
165
|
|
|
|
|
|
|
# - SL |
|
166
|
160
|
100
|
|
|
|
617
|
return 1 if $glob eq '-1'; |
|
167
|
|
|
|
|
|
|
# next lets see if we have a CODE slot ... |
|
168
|
159
|
100
|
|
|
|
311
|
if ( my $code = *{ $glob }{CODE} ) { |
|
|
159
|
|
|
|
|
691
|
|
|
169
|
136
|
|
|
|
|
1166
|
return Sub::Metadata::sub_body_type( $code ) eq 'UNDEF'; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
# if we had no CODE slot, it can't be a NULL CV ... |
|
172
|
23
|
|
|
|
|
94
|
return 0; |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub CREATE_NULL_CV { |
|
176
|
3
|
|
|
3
|
0
|
10
|
my ($in_pkg, $name) = @_; |
|
177
|
3
|
50
|
|
|
|
10
|
Carp::confess('[ARGS] You must specify a package name') |
|
178
|
|
|
|
|
|
|
unless defined $in_pkg; |
|
179
|
3
|
50
|
|
|
|
11
|
Carp::confess('[ARGS] You must specify a name') |
|
180
|
|
|
|
|
|
|
unless defined $name; |
|
181
|
|
|
|
|
|
|
# this just tries to eval the NULL CV into |
|
182
|
|
|
|
|
|
|
# place, it is ugly, but works for now |
|
183
|
3
|
100
|
|
|
|
177
|
eval "sub ${in_pkg}::${name}; 1;" or do { Carp::confess($@) }; |
|
|
1
|
|
|
|
|
260
|
|
|
184
|
2
|
|
|
|
|
9
|
return; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub SET_COMP_STASH_FOR_CV { |
|
188
|
3
|
|
|
3
|
0
|
7
|
my ($cv, $in_pkg) = @_; |
|
189
|
3
|
50
|
|
|
|
9
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
190
|
|
|
|
|
|
|
unless $cv; |
|
191
|
3
|
50
|
|
|
|
9
|
Carp::confess('[ARGS] You must specify a package name') |
|
192
|
|
|
|
|
|
|
unless defined $in_pkg; |
|
193
|
3
|
50
|
66
|
|
|
20
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
|
|
|
66
|
|
|
|
|
|
194
|
|
|
|
|
|
|
unless $cv && ref $cv eq 'CODE' |
|
195
|
|
|
|
|
|
|
|| CAN_COERCE_TO_CODE_REF( $cv ); |
|
196
|
3
|
|
|
|
|
18
|
Sub::Metadata::mutate_sub_package( $cv, $in_pkg ); |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub INSTALL_CV { |
|
200
|
421
|
|
|
421
|
0
|
1309
|
my ($in_pkg, $name, $cv, %opts) = @_; |
|
201
|
|
|
|
|
|
|
|
|
202
|
421
|
50
|
|
|
|
892
|
Carp::confess('[ARGS] You must specify a package name') |
|
203
|
|
|
|
|
|
|
unless defined $in_pkg; |
|
204
|
421
|
50
|
|
|
|
828
|
Carp::confess('[ARGS] You must specify a name') |
|
205
|
|
|
|
|
|
|
unless defined $name; |
|
206
|
421
|
50
|
33
|
|
|
1609
|
Carp::confess('[ARGS] You must specify a CODE reference') |
|
|
|
|
33
|
|
|
|
|
|
207
|
|
|
|
|
|
|
unless $cv && ref $cv eq 'CODE' |
|
208
|
|
|
|
|
|
|
|| CAN_COERCE_TO_CODE_REF( $cv ); |
|
209
|
|
|
|
|
|
|
Carp::confess("[ARGS] You must specify a boolean value for `set_subname` option") |
|
210
|
421
|
50
|
|
|
|
1014
|
if not exists $opts{set_subname}; |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
{ |
|
213
|
35
|
|
|
35
|
|
281
|
no strict 'refs'; |
|
|
35
|
|
|
|
|
87
|
|
|
|
35
|
|
|
|
|
1316
|
|
|
|
421
|
|
|
|
|
644
|
|
|
214
|
35
|
|
|
35
|
|
233
|
no warnings 'once', 'redefine'; |
|
|
35
|
|
|
|
|
95
|
|
|
|
35
|
|
|
|
|
7120
|
|
|
215
|
|
|
|
|
|
|
|
|
216
|
421
|
|
|
|
|
971
|
my $fullname = $in_pkg.'::'.$name; |
|
217
|
421
|
100
|
|
|
|
857
|
*{$fullname} = $opts{set_subname} ? Sub::Name::subname($fullname, $cv) : $cv; |
|
|
421
|
|
|
|
|
2306
|
|
|
218
|
|
|
|
|
|
|
} |
|
219
|
421
|
|
|
|
|
1289
|
return; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub REMOVE_CV_FROM_GLOB { |
|
223
|
3
|
|
|
3
|
0
|
7
|
my ($stash, $name) = @_; |
|
224
|
|
|
|
|
|
|
|
|
225
|
3
|
50
|
33
|
|
|
20
|
Carp::confess('[ARGS] You must specify a stash') |
|
226
|
|
|
|
|
|
|
unless $stash && ref $stash eq 'HASH'; |
|
227
|
3
|
50
|
|
|
|
9
|
Carp::confess('[ARGS] You must specify a name') |
|
228
|
|
|
|
|
|
|
unless defined $name; |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# find the glob we are looking for |
|
231
|
|
|
|
|
|
|
# which might not exist, in which |
|
232
|
|
|
|
|
|
|
# case we do nothing .... |
|
233
|
3
|
50
|
|
|
|
12
|
if ( my $glob = $stash->{ $name } ) { |
|
234
|
|
|
|
|
|
|
# once we find it, extract all the |
|
235
|
|
|
|
|
|
|
# slots we need, note the missing |
|
236
|
|
|
|
|
|
|
# CODE slot since we don't need |
|
237
|
|
|
|
|
|
|
# that in our new glob ... |
|
238
|
3
|
|
|
|
|
5
|
my %to_save; |
|
239
|
3
|
|
|
|
|
8
|
foreach my $slot (qw[ SCALAR ARRAY HASH FORMAT IO ]) { |
|
240
|
15
|
100
|
|
|
|
24
|
if ( my $val = *{ $glob }{ $slot } ) { |
|
|
15
|
|
|
|
|
45
|
|
|
241
|
3
|
|
|
|
|
9
|
$to_save{ $slot } = $val; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
# replace the old glob with a new one ... |
|
245
|
3
|
|
|
|
|
16
|
$stash->{ $name } = Symbol::gensym(); |
|
246
|
|
|
|
|
|
|
# now go about constructing our new |
|
247
|
|
|
|
|
|
|
# glob by restoring the other slots |
|
248
|
|
|
|
|
|
|
{ |
|
249
|
35
|
|
|
35
|
|
278
|
no strict 'refs'; |
|
|
35
|
|
|
|
|
102
|
|
|
|
35
|
|
|
|
|
1262
|
|
|
|
3
|
|
|
|
|
59
|
|
|
250
|
35
|
|
|
35
|
|
252
|
no warnings 'once'; |
|
|
35
|
|
|
|
|
87
|
|
|
|
35
|
|
|
|
|
33144
|
|
|
251
|
|
|
|
|
|
|
# get the name of the stash, we could have |
|
252
|
|
|
|
|
|
|
# passed this in, but it is easy to get in |
|
253
|
|
|
|
|
|
|
# XS, and so we can punt that down the road |
|
254
|
|
|
|
|
|
|
# for the time being |
|
255
|
3
|
|
|
|
|
15
|
my $pkg = B::svref_2object( $stash )->NAME; |
|
256
|
3
|
|
|
|
|
13
|
foreach my $type ( keys %to_save ) { |
|
257
|
3
|
|
|
|
|
8
|
*{ $pkg . '::' . $name } = $to_save{ $type }; |
|
|
3
|
|
|
|
|
13
|
|
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
# ... the end |
|
262
|
3
|
|
|
|
|
11
|
return; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
266
|
|
|
|
|
|
|
## Role application and composition |
|
267
|
|
|
|
|
|
|
## ------------------------------------------------------------------ |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub APPLY_ROLES { |
|
270
|
16
|
|
|
16
|
0
|
437
|
my ($meta, $roles) = @_; |
|
271
|
|
|
|
|
|
|
|
|
272
|
16
|
50
|
|
|
|
106
|
Carp::confess('[ARGS] You must specify a metaclass to apply roles to') |
|
273
|
|
|
|
|
|
|
unless Scalar::Util::blessed( $meta ); |
|
274
|
16
|
50
|
33
|
|
|
171
|
Carp::confess('[ARGS] You must specify a least one roles to apply as an ARRAY ref') |
|
|
|
|
33
|
|
|
|
|
|
275
|
|
|
|
|
|
|
unless $roles && ref $roles eq 'ARRAY' && scalar( @$roles ) != 0; |
|
276
|
|
|
|
|
|
|
|
|
277
|
16
|
|
|
|
|
177
|
foreach my $r ( $meta->roles ) { |
|
278
|
|
|
|
|
|
|
Carp::confess("[ERROR] Could not find role ($_) in the set of roles in $meta (" . $meta->name . ")") |
|
279
|
18
|
50
|
|
|
|
45
|
unless scalar grep { $r eq $_ } @$roles; |
|
|
22
|
|
|
|
|
118
|
|
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
16
|
|
|
|
|
56
|
my @meta_roles = map { MOP::Role->new( name => $_ ) } @$roles; |
|
|
18
|
|
|
|
|
150
|
|
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
my ( |
|
285
|
16
|
|
|
|
|
344
|
$slots, |
|
286
|
|
|
|
|
|
|
$slot_conflicts |
|
287
|
|
|
|
|
|
|
) = COMPOSE_ALL_ROLE_SLOTS( @meta_roles ); |
|
288
|
|
|
|
|
|
|
|
|
289
|
16
|
50
|
|
|
|
83
|
Carp::confess("[CONFLICT] There should be no conflicting slots when composing (" . (join ', ' => @$roles) . ") into (" . $meta->name . ")") |
|
290
|
|
|
|
|
|
|
if scalar keys %$slot_conflicts; |
|
291
|
|
|
|
|
|
|
|
|
292
|
16
|
|
|
|
|
55
|
foreach my $name ( keys %$slots ) { |
|
293
|
|
|
|
|
|
|
# if we have a slot already by that name ... |
|
294
|
0
|
0
|
|
|
|
0
|
Carp::confess("[CONFLICT] Role Conflict, cannot compose slot ($name) into (" . $meta->name . ") because ($name) already exists") |
|
295
|
|
|
|
|
|
|
if $meta->has_slot( $name ); |
|
296
|
|
|
|
|
|
|
# otherwise alias it ... |
|
297
|
0
|
|
|
|
|
0
|
$meta->alias_slot( $name, $slots->{ $name } ); |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
my ( |
|
301
|
16
|
|
|
|
|
61
|
$methods, |
|
302
|
|
|
|
|
|
|
$method_conflicts, |
|
303
|
|
|
|
|
|
|
$required_methods |
|
304
|
|
|
|
|
|
|
) = COMPOSE_ALL_ROLE_METHODS( @meta_roles ); |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Carp::confess("[CONFLICT] There should be no conflicting methods when composing (" . (join ', ' => @$roles) . ") into (" . $meta->name . ") but instead we found (" . (join ', ' => keys %$method_conflicts) . ")") |
|
307
|
|
|
|
|
|
|
if (scalar keys %$method_conflicts) # do we have any conflicts ... |
|
308
|
|
|
|
|
|
|
# and the conflicts are not satisfied by the composing item ... |
|
309
|
16
|
0
|
50
|
|
|
95
|
&& (scalar grep { !$meta->has_method( $_ ) } keys %$method_conflicts); |
|
|
0
|
|
|
|
|
0
|
|
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# check the required method set and |
|
312
|
|
|
|
|
|
|
# see if what we are composing into |
|
313
|
|
|
|
|
|
|
# happens to fulfill them |
|
314
|
16
|
|
|
|
|
57
|
foreach my $name ( keys %$required_methods ) { |
|
315
|
3
|
50
|
|
|
|
8
|
delete $required_methods->{ $name } |
|
316
|
|
|
|
|
|
|
if $meta->name->can( $name ); |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
|
|
319
|
16
|
50
|
|
|
|
70
|
Carp::confess("[CONFLICT] There should be no required methods when composing (" . (join ', ' => @$roles) . ") into (" . $meta->name . ") but instead we found (" . (join ', ' => keys %$required_methods) . ")") |
|
320
|
|
|
|
|
|
|
if scalar keys %$required_methods; # do we have required methods ... |
|
321
|
|
|
|
|
|
|
|
|
322
|
16
|
|
|
|
|
120
|
foreach my $name ( keys %$methods ) { |
|
323
|
|
|
|
|
|
|
# if we have a method already by that name ... |
|
324
|
419
|
50
|
|
|
|
1030
|
next if $meta->has_method( $name ); |
|
325
|
|
|
|
|
|
|
# otherwise, alias it ... |
|
326
|
419
|
|
|
|
|
1101
|
$meta->alias_method( $name, $methods->{ $name } ); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# if we still have keys in $required, it is |
|
330
|
|
|
|
|
|
|
# because we are a role (class would have |
|
331
|
|
|
|
|
|
|
# died above), so we can just stuff in the |
|
332
|
|
|
|
|
|
|
# required methods ... |
|
333
|
16
|
|
|
|
|
105
|
$meta->add_required_method( $_ ) for keys %$required_methods; |
|
334
|
|
|
|
|
|
|
|
|
335
|
16
|
|
|
|
|
190
|
return; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub COMPOSE_ALL_ROLE_SLOTS { |
|
339
|
16
|
|
|
16
|
0
|
48
|
my @roles = @_; |
|
340
|
|
|
|
|
|
|
|
|
341
|
16
|
50
|
|
|
|
86
|
Carp::confess('[ARGS] You must specify a least one role to compose slots in') |
|
342
|
|
|
|
|
|
|
if scalar( @roles ) == 0; |
|
343
|
|
|
|
|
|
|
|
|
344
|
16
|
|
|
|
|
40
|
my (%slots, %conflicts); |
|
345
|
|
|
|
|
|
|
|
|
346
|
16
|
|
|
|
|
45
|
foreach my $role ( @roles ) { |
|
347
|
18
|
|
|
|
|
79
|
foreach my $slot ( $role->slots ) { |
|
348
|
0
|
|
|
|
|
0
|
my $name = $slot->name; |
|
349
|
|
|
|
|
|
|
# if we have one already, but |
|
350
|
|
|
|
|
|
|
# it is not the same refaddr ... |
|
351
|
0
|
0
|
0
|
|
|
0
|
if ( exists $slots{ $name } && $slots{ $name } != $slot->initializer ) { |
|
352
|
|
|
|
|
|
|
# mark it as a conflict ... |
|
353
|
0
|
|
|
|
|
0
|
$conflicts{ $name } = undef; |
|
354
|
|
|
|
|
|
|
# and remove it from our slot set ... |
|
355
|
0
|
|
|
|
|
0
|
delete $slots{ $name }; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
# if we don't have it already ... |
|
358
|
|
|
|
|
|
|
else { |
|
359
|
|
|
|
|
|
|
# make a note of it |
|
360
|
0
|
|
|
|
|
0
|
$slots{ $name } = $slot->initializer; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
16
|
|
|
|
|
91
|
return \%slots, \%conflicts; |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# TODO: |
|
370
|
|
|
|
|
|
|
# We should track the name of the role |
|
371
|
|
|
|
|
|
|
# where the required method was composed |
|
372
|
|
|
|
|
|
|
# from, as well as the two classes in |
|
373
|
|
|
|
|
|
|
# which a method conflicted. |
|
374
|
|
|
|
|
|
|
# - SL |
|
375
|
|
|
|
|
|
|
sub COMPOSE_ALL_ROLE_METHODS { |
|
376
|
16
|
|
|
16
|
0
|
45
|
my @roles = @_; |
|
377
|
|
|
|
|
|
|
|
|
378
|
16
|
50
|
|
|
|
61
|
Carp::confess('[ARGS] You must specify a least one role to compose methods in') |
|
379
|
|
|
|
|
|
|
if scalar( @roles ) == 0; |
|
380
|
|
|
|
|
|
|
|
|
381
|
16
|
|
|
|
|
37
|
my (%methods, %conflicts, %required); |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# flatten the set of required methods ... |
|
384
|
16
|
|
|
|
|
38
|
foreach my $r ( @roles ) { |
|
385
|
18
|
|
|
|
|
106
|
foreach my $m ( $r->required_methods ) { |
|
386
|
3
|
|
|
|
|
9
|
$required{ $m->name } = undef; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# for every role ... |
|
391
|
16
|
|
|
|
|
1949
|
foreach my $r ( @roles ) { |
|
392
|
|
|
|
|
|
|
# and every method in that role ... |
|
393
|
18
|
|
|
|
|
92
|
foreach my $m ( $r->methods ) { |
|
394
|
419
|
|
|
|
|
979
|
my $name = $m->name; |
|
395
|
|
|
|
|
|
|
# if we have already seen the method, |
|
396
|
|
|
|
|
|
|
# but it is not the same refaddr |
|
397
|
|
|
|
|
|
|
# it is a conflict, which means: |
|
398
|
419
|
50
|
33
|
|
|
1198
|
if ( exists $methods{ $name } && $methods{ $name } != $m->body ) { |
|
399
|
|
|
|
|
|
|
# we need to add it to our required-method map |
|
400
|
0
|
|
|
|
|
0
|
$required{ $name } = undef; |
|
401
|
|
|
|
|
|
|
# and note that it is also a conflict ... |
|
402
|
0
|
|
|
|
|
0
|
$conflicts{ $name } = undef; |
|
403
|
|
|
|
|
|
|
# and remove it from our method map |
|
404
|
0
|
|
|
|
|
0
|
delete $methods{ $name }; |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
# if we haven't seen the method ... |
|
407
|
|
|
|
|
|
|
else { |
|
408
|
|
|
|
|
|
|
# add it to the method map |
|
409
|
419
|
|
|
|
|
950
|
$methods{ $name } = $m->body; |
|
410
|
|
|
|
|
|
|
# and remove it from the required-method map |
|
411
|
|
|
|
|
|
|
delete $required{ $name } |
|
412
|
|
|
|
|
|
|
# if it actually exists in it, and ... |
|
413
|
|
|
|
|
|
|
if exists $required{ $name } |
|
414
|
|
|
|
|
|
|
# is not also a conflict ... |
|
415
|
419
|
50
|
33
|
|
|
1156
|
&& !exists $conflicts{ $name }; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
#use Data::Dumper; |
|
421
|
|
|
|
|
|
|
#warn Dumper [ [ map { $_->name } @roles ], \%methods, \%conflicts, \%required ]; |
|
422
|
|
|
|
|
|
|
|
|
423
|
16
|
|
|
|
|
2019
|
return \%methods, \%conflicts, \%required; |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
1; |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
__END__ |