File Coverage

blib/lib/MOP/Internal/Util.pm
Criterion Covered Total %
statement 180 193 93.2
branch 57 106 53.7
condition 22 50 44.0
subroutine 35 35 100.0
pod 0 19 0.0
total 294 403 72.9


line stmt bran cond sub pod time code
1             package MOP::Internal::Util;
2             # ABSTRACT: For MOP Internal Use Only
3              
4 35     35   199 use strict;
  35         65  
  35         941  
5 35     35   161 use warnings;
  35         55  
  35         796  
6              
7 35     35   170 use B (); # nasty stuff, all nasty stuff
  35         130  
  35         428  
8 35     35   140 use Carp (); # errors and stuff
  35         51  
  35         401  
9 35     35   8563 use Sub::Name (); # handling some sub stuff
  35         13873  
  35         886  
10 35     35   8806 use Sub::Metadata (); # handling other sub stuff
  35         30389  
  35         755  
11 35     35   8554 use Symbol (); # creating the occasional symbol
  35         21915  
  35         787  
12 35     35   201 use Scalar::Util (); # I think I use blessed somewhere in here ...
  35         63  
  35         467  
13 35     35   9114 use Devel::OverloadInfo (); # Sometimes I need to know about overloading
  35         329853  
  35         679  
14 35     35   8179 use Devel::Hook (); # for scheduling UNITCHECK blocks ...
  35         23868  
  35         14585  
15              
16             our $VERSION = '0.11';
17             our $AUTHORITY = 'cpan:STEVAN';
18              
19             ## ------------------------------------------------------------------
20             ## Basic Glob access
21             ## ------------------------------------------------------------------
22              
23             sub IS_VALID_MODULE_NAME {
24 112     112 0 206 my ($name) = @_;
25 112         762 $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 10 my ($stash) = @_;
30 4 50       11 Carp::croak('[ARGS] You must specify a stash')
31             unless defined $stash;
32 4 100       44 if ( my $name = B::svref_2object( $stash )->NAME ) {
33 2         7 return IS_VALID_MODULE_NAME( $name );
34             }
35 2         7 return;
36             }
37              
38             sub GET_NAME {
39 1117     1117 0 1588 my ($stash) = @_;
40 1117 50       1977 Carp::croak('[ARGS] You must specify a stash')
41             unless defined $stash;
42 1117         4279 B::svref_2object( $stash )->NAME
43             }
44              
45             sub GET_STASH_NAME {
46 81     81 0 130 my ($stash) = @_;
47 81 50       153 Carp::croak('[ARGS] You must specify a stash')
48             unless defined $stash;
49 81         1090 B::svref_2object( $stash )->STASH->NAME
50             }
51              
52             sub GET_GLOB_NAME {
53 544     544 0 749 my ($stash) = @_;
54 544 50       900 Carp::croak('[ARGS] You must specify a stash')
55             unless defined $stash;
56 544         2217 B::svref_2object( $stash )->GV->NAME
57             }
58              
59             sub GET_GLOB_STASH_NAME {
60 605     605 0 809 my ($stash) = @_;
61 605 50       1153 Carp::croak('[ARGS] You must specify a stash')
62             unless defined $stash;
63 605         3478 B::svref_2object( $stash )->GV->STASH->NAME
64             }
65              
66             sub GET_GLOB_SLOT {
67 1587     1587 0 2669 my ($stash, $name, $slot) = @_;
68              
69 1587 50       2747 Carp::croak('[ARGS] You must specify a stash')
70             unless defined $stash;
71 1587 50       2277 Carp::croak('[ARGS] You must specify a name')
72             unless defined $name;
73 1587 50       2265 Carp::croak('[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 1587 100       2800 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 1437 50 100     7189 if (( $slot eq 'CODE' && $stash->{ $name } eq "-1" ) || ref $stash->{ $name } ne 'GLOB') {
      66        
85 1437         6311 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 1437         2764 return *{ $stash->{ $name } }{ $slot };
  1437         5847  
93             }
94              
95             sub SET_GLOB_SLOT {
96 4     4 0 11 my ($stash, $name, $value_ref) = @_;
97              
98 4 50       14 Carp::croak('[ARGS] You must specify a stash')
99             unless defined $stash;
100 4 50       16 Carp::croak('[ARGS] You must specify a name')
101             unless defined $name;
102 4 50       10 Carp::croak('[ARGS] You must specify a value REF')
103             unless defined $value_ref;
104              
105             {
106 35     35   249 no strict 'refs';
  35         71  
  35         1027  
  4         6  
107 35     35   170 no warnings 'once';
  35         61  
  35         16376  
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         24 my $pkg = B::svref_2object( $stash )->NAME;
113 4         10 *{ $pkg . '::' . $name } = $value_ref;
  4         30  
114             }
115 4         11 return;
116             }
117              
118             ## ------------------------------------------------------------------
119             ## UNITCHECK hook
120             ## ------------------------------------------------------------------
121              
122             sub ADD_UNITCHECK_HOOK {
123 5     5 0 8 my ($cv) = @_;
124 5 50       15 Carp::croak('[ARGS] You must specify a CODE reference')
125             unless $cv;
126 5 50 33     30 Carp::croak('[ARGS] You must specify a CODE reference')
127             unless $cv && ref $cv eq 'CODE';
128 5         23 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 154 my ($object) = @_;
137 109 100 66     813 return 0 unless $object && Scalar::Util::blessed( $object );
138             # might be just a blessed CODE ref ...
139 26 50       120 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 1235 my ($cv) = @_;
147 926 50       1506 Carp::croak('[ARGS] You must specify a CODE reference')
148             unless $cv;
149 926 50 33     2642 Carp::croak('[ARGS] You must specify a CODE reference')
      33        
150             unless $cv && ref $cv eq 'CODE'
151             || CAN_COERCE_TO_CODE_REF( $cv );
152 926         2626 return Sub::Metadata::sub_body_type( $cv ) eq 'UNDEF';
153             }
154              
155             sub DOES_GLOB_HAVE_NULL_CV {
156 160     160 0 518 my ($glob) = @_;
157 160 50       352 Carp::croak('[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       530 return 1 if $glob eq '-1';
167             # next lets see if we have a CODE slot ...
168 159 100       261 if ( my $code = *{ $glob }{CODE} ) {
  159         598  
169 136         949 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         71 return 0;
173             }
174              
175             sub CREATE_NULL_CV {
176 3     3 0 6 my ($in_pkg, $name) = @_;
177 3 50       7 Carp::croak('[ARGS] You must specify a package name')
178             unless defined $in_pkg;
179 3 50       6 Carp::croak('[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       156 eval "sub ${in_pkg}::${name}; 1;" or do { Carp::croak($@) };
  1         115  
184 2         5 return;
185             }
186              
187             sub SET_COMP_STASH_FOR_CV {
188 3     3 0 9 my ($cv, $in_pkg) = @_;
189 3 50       11 Carp::croak('[ARGS] You must specify a CODE reference')
190             unless $cv;
191 3 50       8 Carp::croak('[ARGS] You must specify a package name')
192             unless defined $in_pkg;
193 3 50 66     21 Carp::croak('[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 821 my ($in_pkg, $name, $cv, %opts) = @_;
201              
202 421 50       634 Carp::croak('[ARGS] You must specify a package name')
203             unless defined $in_pkg;
204 421 50       586 Carp::croak('[ARGS] You must specify a name')
205             unless defined $name;
206 421 50 33     1184 Carp::croak('[ARGS] You must specify a CODE reference')
      33        
207             unless $cv && ref $cv eq 'CODE'
208             || CAN_COERCE_TO_CODE_REF( $cv );
209             Carp::croak("[ARGS] You must specify a boolean value for `set_subname` option")
210 421 50       647 if not exists $opts{set_subname};
211              
212             {
213 35     35   224 no strict 'refs';
  35         62  
  35         1030  
  421         456  
214 35     35   170 no warnings 'once', 'redefine';
  35         82  
  35         5469  
215              
216 421         719 my $fullname = $in_pkg.'::'.$name;
217 421 100       640 *{$fullname} = $opts{set_subname} ? Sub::Name::subname($fullname, $cv) : $cv;
  421         1619  
218             }
219 421         900 return;
220             }
221              
222             sub REMOVE_CV_FROM_GLOB {
223 3     3 0 8 my ($stash, $name) = @_;
224              
225 3 50 33     16 Carp::croak('[ARGS] You must specify a stash')
226             unless $stash && ref $stash eq 'HASH';
227 3 50       8 Carp::croak('[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         7 foreach my $slot (qw[ SCALAR ARRAY HASH FORMAT IO ]) {
240 15 100       23 if ( my $val = *{ $glob }{ $slot } ) {
  15         43  
241 3         12 $to_save{ $slot } = $val;
242             }
243             }
244             # replace the old glob with a new one ...
245 3         13 $stash->{ $name } = Symbol::gensym();
246             # now go about constructing our new
247             # glob by restoring the other slots
248             {
249 35     35   215 no strict 'refs';
  35         70  
  35         931  
  3         50  
250 35     35   162 no warnings 'once';
  35         59  
  35         24671  
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         9 foreach my $type ( keys %to_save ) {
257 3         7 *{ $pkg . '::' . $name } = $to_save{ $type };
  3         14  
258             }
259             }
260             }
261             # ... the end
262 3         9 return;
263             }
264              
265             ## ------------------------------------------------------------------
266             ## Role application and composition
267             ## ------------------------------------------------------------------
268              
269             sub APPLY_ROLES {
270 16     16 0 318 my ($meta, $roles) = @_;
271              
272 16 50       83 Carp::croak('[ARGS] You must specify a metaclass to apply roles to')
273             unless Scalar::Util::blessed( $meta );
274 16 50 33     135 Carp::croak('[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         122 foreach my $r ( $meta->roles ) {
278             Carp::croak("[ERROR] Could not find role ($_) in the set of roles in $meta (" . $meta->name . ")")
279 18 50       32 unless scalar grep { $r eq $_ } @$roles;
  22         91  
280             }
281              
282 16         38 my @meta_roles = map { MOP::Role->new( name => $_ ) } @$roles;
  18         105  
283              
284             my (
285 16         257 $slots,
286             $slot_conflicts
287             ) = COMPOSE_ALL_ROLE_SLOTS( @meta_roles );
288              
289 16 50       60 Carp::croak("[CONFLICT] There should be no conflicting slots when composing (" . (join ', ' => @$roles) . ") into (" . $meta->name . ")")
290             if scalar keys %$slot_conflicts;
291              
292 16         46 foreach my $name ( keys %$slots ) {
293             # if we have a slot already by that name ...
294 0 0       0 Carp::croak("[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         40 $methods,
302             $method_conflicts,
303             $required_methods
304             ) = COMPOSE_ALL_ROLE_METHODS( @meta_roles );
305              
306             Carp::croak("[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     69 && (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         44 foreach my $name ( keys %$required_methods ) {
315 3 50       9 delete $required_methods->{ $name }
316             if $meta->name->can( $name );
317             }
318              
319 16 50       54 Carp::croak("[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         80 foreach my $name ( keys %$methods ) {
323             # if we have a method already by that name ...
324 419 50       745 next if $meta->has_method( $name );
325             # otherwise, alias it ...
326 419         767 $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         63 $meta->add_required_method( $_ ) for keys %$required_methods;
334              
335 16         134 return;
336             }
337              
338             sub COMPOSE_ALL_ROLE_SLOTS {
339 16     16 0 39 my @roles = @_;
340              
341 16 50       58 Carp::croak('[ARGS] You must specify a least one role to compose slots in')
342             if scalar( @roles ) == 0;
343              
344 16         27 my (%slots, %conflicts);
345              
346 16         34 foreach my $role ( @roles ) {
347 18         57 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         50 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 34 my @roles = @_;
377              
378 16 50       48 Carp::croak('[ARGS] You must specify a least one role to compose methods in')
379             if scalar( @roles ) == 0;
380              
381 16         23 my (%methods, %conflicts, %required);
382              
383             # flatten the set of required methods ...
384 16         31 foreach my $r ( @roles ) {
385 18         87 foreach my $m ( $r->required_methods ) {
386 3         9 $required{ $m->name } = undef;
387             }
388             }
389              
390             # for every role ...
391 16         1453 foreach my $r ( @roles ) {
392             # and every method in that role ...
393 18         75 foreach my $m ( $r->methods ) {
394 419         664 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     863 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         685 $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     915 && !exists $conflicts{ $name };
416             }
417             }
418             }
419              
420             #use Data::Dumper;
421             #warn Dumper [ [ map { $_->name } @roles ], \%methods, \%conflicts, \%required ];
422              
423 16         1458 return \%methods, \%conflicts, \%required;
424             }
425              
426             1;
427              
428             __END__